Better Upload

Upload Routes

Upload routes are where you define how files are uploaded. To define a route, use the route function.

You can create multiple routes for different purposes (e.g. images, videos).

Here is a basic example of a single file upload route:

import { route } from 'better-upload/server';

route({
  fileTypes: ['image/*'], // Accepts all image types
  maxFileSize: 1024 * 1024 * 4, // 4MB
});

Single file routes have the following options:

PropTypeDefault
fileTypes?
string[]
All file types allowed
maxFileSize?
number
5242880 (5MB)
signedUrlExpiresIn?
number
120 (2 minutes)
clientMetadataSchema?
object
-

Callbacks

When defining a route, you may want to run code before or after the upload. You can do this by using the callbacks.

Before upload

The onBeforeUpload callback is called before the pre-signed URL is generated. Use this to run custom logic before uploading a file, such as auth and rate-limiting.

The request, file, and metadata sent from the client are available.

route({
  onBeforeUpload: async ({ req, file, clientMetadata }) => {
    const user = await auth();

    if (!user) {
      throw new UploadFileError('Not logged in!');
    }

    return {
      objectKey: user.id,
      bucketName: 'another-bucket',
    };
  },
});

Rejecting uploads

Throw UploadFileError to reject the file upload. This will also send the error message to the client.

You can return an object with the following properties:

PropTypeDefault
objectKey?
string
-
objectMetadata?
Record<string, string>
-
metadata?
object
-
bucketName?
string
-

After generating pre-signed URL

The onAfterSignedUrl callback is called after the pre-signed URL is generated. Use this to run custom logic after the URL is generated, such as logging and saving data.

In addition to all previous data, metadata from the onBeforeUpload callback is also available.

route({
  onAfterSignedUrl: async ({ req, file, metadata, clientMetadata }) => {
    // the file now has the objectKey property

    return {
      metadata: {
        example: '123',
      },
    };
  },
});

You can return an object with the following properties:

PropTypeDefault
metadata?
object
-

Multipart uploads

If you want to upload files larger than 5GB, you need to use multipart uploads. To enable it, set multipart to true. It works both for single and multiple files. No change is needed in the client.

route({
  multipleFiles: true,
  multipart: true, 
  partSize: 1024 * 1024 * 20, // 20MB, default is 50MB
});

You can now also modify the following options:

PropTypeDefault
partSize?
number
52428800 (50MB)
partSignedUrlExpiresIn?
number
1500 (25 minutes)
completeSignedUrlExpiresIn?
number
1800 (30 minutes)

Even for files under 5GB, using multipart uploads can speed up the upload process. Empty files (0 bytes) will fail to upload with multipart uploads.

Router

The router is where all upload routes are defined. To define a router, create an object with the Router type.

import { type Router } from 'better-upload/server';

export const router: Router = {
  client: s3,
  bucketName: 'my-bucket',
  routes: {
    // your routes...
  },
};