# @vercel/blob

## Getting started

To start using [Vercel Blob](/storage/blob) SDK, follow the steps below:

> **💡 Note:** You can also interact with Vercel Blob using the [Vercel CLI](/docs/cli/blob)
> for command-line operations. For example, you might want to quickly upload
> assets during local development without writing additional code.

Vercel Blob works with any frontend framework. begin by installing the package:

- ### Create a Blob store
  Navigate to the [Project](/docs/projects/overview) you'd like to add the blob store to. Select the **Storage** tab, then select the **Connect Database** button.

  Under the **Create New** tab, select **Blob** and then the **Continue** button.

  Choose a name for your store and select **Create a new Blob store**. Select the environments where you would like the read-write token to be included. You can also update the prefix of the Environment Variable in Advanced Options

  Once created, you are taken to the Vercel Blob store page.

- ### Prepare your local project
  Since you created the Blob store in a project, environment variables are automatically created and added to the project for you.
  - `BLOB_READ_WRITE_TOKEN`
  To use this environment variable locally, use the Vercel CLI to [pull the values into your local project](/docs/cli/env#exporting-development-environment-variables):
  ```bash
  vercel env pull
  ```

## Read-write token

A read-write token is required to interact with the Blob SDK. When you create a Blob store in your Vercel Dashboard, an environment variable with the value of the token is created for you. You have the following options when deploying your application:

- If you deploy your application in the same Vercel project where your Blob store is located, you *do not* need to specify the `token` parameter, as it's default value is equal to the store's token environment variable
- If you deploy your application in a different Vercel project or scope, you can create an environment variable there and assign the token value from your Blob store settings to this variable. You will then set the `token` parameter to this environment variable
- If you deploy your application outside of Vercel, you can copy the `token` value from the store settings and pass it as the `token` parameter when you call a Blob SDK method

## Using the SDK methods

In the examples below, we use [Fluid compute](/docs/fluid-compute) for optimal performance and scalability.

## Upload a blob

This example creates a Function that accepts a file from a `multipart/form-data` form and uploads it to the Blob store. The function returns a unique URL for the blob.

### `put()`

The `put` method uploads a blob object to the Blob store.

It accepts the following parameters:

- `pathname`: (Required) A string specifying the base value of the return URL
- `body`: (Required) A blob object as `ReadableStream`, `String`, `ArrayBuffer` or `Blob` based on these [supported body types](https://developer.mozilla.org/docs/Web/API/fetch#body)
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter            | Required | Values                                                                                                                                                                                                                                                                                        |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`             | Yes      | `public`                                                                                                                                                                                                                                                                                      |
| `addRandomSuffix`    | No       | A boolean specifying whether to add a random suffix to the `pathname`. It defaults to `false`. **We recommend using this option** to ensure there are no conflicts in your blob filenames.                                                                                                    |
| `allowOverwrite`     | No       | A boolean to allow overwriting blobs. By default an error will be thrown if you try to overwrite a blob by using the same `pathname` for multiple blobs.                                                                                                                                      |
| `cacheControlMaxAge` | No       | A number in seconds to configure how long Blobs are cached. Defaults to one month. Cannot be set to a value lower than 1 minute. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.                                                                        |
| `contentType`        | No       | A string indicating the [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type). By default, it's extracted from the pathname's extension.                                                                                                                             |
| `token`              | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token). You can also pass a client token created with the `generateClientTokenFromReadWriteToken` method |
| `multipart`          | No       | Pass `multipart: true` when uploading large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.                                                                                                                                                |
| `abortSignal`        | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                                                                                                        |
| `onUploadProgress`   | No       | Callback to track upload progress: `onUploadProgress({loaded: number, total: number, percentage: number})`                                                                                                                                                                                    |

#### Example code with folder output

To upload your file to an existing [folder](#folders) inside your blob storage, pass the folder name in the `pathname` as shown below:

#### Example responses

`put()` returns a `JSON` object with the following data for the created blob object:

```json
{
  "pathname": "string",
  "contentType": "string",
  "contentDisposition": "string",
  "url": "string",
  "downloadUrl": "string"
}
```

An example blob (uploaded with `addRandomSuffix: true`) is:

```json
{
  "pathname": "profilesv1/user-12345-NoOVGDVcqSPc7VYCUAGnTzLTG2qEM2.txt",
  "contentType": "text/plain",
  "contentDisposition": "attachment; filename=\"user-12345-NoOVGDVcqSPc7VYCUAGnTzLTG2qEM2.txt\"",
  "url": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345-NoOVGDVcqSPc7VYCUAGnTzLTG2qEM2.txt",
  "downloadUrl": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345-NoOVGDVcqSPc7VYCUAGnTzLTG2qEM2.txt?download=1"
}
```

An example blob uploaded without `addRandomSuffix: true` (default) is:

```json
{
  "pathname": "profilesv1/user-12345.txt",
  "contentType": "text/plain",
  "contentDisposition": "attachment; filename=\"user-12345.txt\"",
  // no automatic random suffix added 👇
  "url": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345.txt",
  "downloadUrl": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345.txt?download=1"
}
```

## Multipart Uploads

When uploading large files you should use multipart uploads to have a more reliable upload process. A multipart upload splits the file into multiple parts, uploads them in parallel and retries failed parts.
This process consists of three phases: creating a multipart upload, uploading the parts and completing the upload. `@vercel/blob` offers three different ways to create multipart uploads:

### Automatic

This method has everything baked in and is easiest to use. It's part of the `put` and `upload` API's. Under the hood it will start the upload, split your file into multiple parts with the same size, upload them in parallel and complete the upload.

### Manual

This method gives you full control over the multipart upload process. It consists of three phases:

**Phase 1: Create a multipart upload**

`createMultipartUpload` accepts the following parameters:

- `pathname`: (Required) A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter            | Required | Values                                                                                                                                                                                                                                                                                        |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`             | Yes      | `public`                                                                                                                                                                                                                                                                                      |
| `contentType`        | No       | The [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) for the file. If not specified, it's derived from the file extension. Falls back to `application/octet-stream` when no extension exists or can't be matched.                                               |
| `token`              | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token). You can also pass a client token created with the `generateClientTokenFromReadWriteToken` method |
| `addRandomSuffix`    | No       | A boolean specifying whether to add a random suffix to the pathname. It defaults to `true`.                                                                                                                                                                                                   |
| `cacheControlMaxAge` | No       | A number in seconds to configure the edge and browser cache. Defaults to one year. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.                                                                                                                      |
| `abortSignal`        | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                                                                                                        |

`createMultipartUpload()` returns a `JSON` object with the following data for the created upload:

```json
{
  "key": "string",
  "uploadId": "string"
}
```

**Phase 2: Upload all the parts**

> **⚠️ Warning:** In the multipart uploader process, it's necessary for you to manage both
> memory usage and concurrent upload requests. Additionally, each part must be a
> minimum of 5MB, except the last one which can be smaller, and all parts should
> be of equal size.

`uploadPart` accepts the following parameters:

- `pathname`: (Required) Same value as the `pathname` parameter passed to `createMultipartUpload`
- `chunkBody`: (Required) A blob object as `ReadableStream`, `String`, `ArrayBuffer` or `Blob` based on these [supported body types](https://developer.mozilla.org/docs/Web/API/fetch#body)
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter     | Required | Values                                                                                                                                                                                                                                                                                        |
| ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`      | Yes      | `public`                                                                                                                                                                                                                                                                                      |
| `partNumber`  | Yes      | A number identifying which part is uploaded                                                                                                                                                                                                                                                   |
| `key`         | Yes      | A string returned from `createMultipartUpload` which identifies the blob object                                                                                                                                                                                                               |
| `uploadId`    | Yes      | A string returned from `createMultipartUpload` which identifies the multipart upload                                                                                                                                                                                                          |
| `token`       | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token). You can also pass a client token created with the `generateClientTokenFromReadWriteToken` method |
| `abortSignal` | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                                                                                                        |

`uploadPart()` returns a `JSON` object with the following data for the uploaded part:

```json
{
  "etag": "string",
  "partNumber": "string"
}
```

**Phase 3: Complete the multipart upload**

`completeMultipartUpload` accepts the following parameters:

- `pathname`: (Required) Same value as the `pathname` parameter passed to `createMultipartUpload`
- `parts`: (Required) An array containing all the uploaded parts
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter            | Required | Values                                                                                                                                                                                                                                                                                        |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`             | Yes      | `public`                                                                                                                                                                                                                                                                                      |
| `key`                | Yes      | A string returned from `createMultipartUpload` which identifies the blob object                                                                                                                                                                                                               |
| `uploadId`           | Yes      | A string returned from `createMultipartUpload` which identifies the multipart upload                                                                                                                                                                                                          |
| `contentType`        | No       | The [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) for the file. If not specified, it's derived from the file extension. Falls back to `application/octet-stream` when no extension exists or can't be matched.                                               |
| `token`              | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token). You can also pass a client token created with the `generateClientTokenFromReadWriteToken` method |
| `addRandomSuffix`    | No       | A boolean specifying whether to add a random suffix to the pathname. It defaults to `true`.                                                                                                                                                                                                   |
| `cacheControlMaxAge` | No       | A number in seconds to configure the edge and browser cache. Defaults to one year. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.                                                                                                                      |
| `abortSignal`        | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                                                                                                        |

`completeMultipartUpload()` returns a `JSON` object with the following data for the created blob object:

```json
{
  "pathname": "string",
  "contentType": "string",
  "contentDisposition": "string",
  "url": "string",
  "downloadUrl": "string"
}
```

### Uploader

A less verbose way than the manual process is the multipart uploader method. It's a wrapper around the manual multipart upload process and takes care of the data that is the same for all the three multipart phases.
This results in a simpler API, but still requires you to handle memory usage and concurrent upload requests.

**Phase 1: Create the multipart uploader**

`createMultipartUploader` accepts the following parameters:

- `pathname`: (Required) A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter            | Required | Values                                                                                                                                                                                                                                                                                        |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`             | Yes      | `public`                                                                                                                                                                                                                                                                                      |
| `contentType`        | No       | The [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) for the file. If not specified, it's derived from the file extension. Falls back to `application/octet-stream` when no extension exists or can't be matched.                                               |
| `token`              | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token). You can also pass a client token created with the `generateClientTokenFromReadWriteToken` method |
| `addRandomSuffix`    | No       | A boolean specifying whether to add a random suffix to the pathname. It defaults to `true`.                                                                                                                                                                                                   |
| `cacheControlMaxAge` | No       | A number in seconds to configure the edge and browser cache. Defaults to one year. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.                                                                                                                      |
| `abortSignal`        | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                                                                                                        |

`createMultipartUploader()` returns an `Uploader` object with the following attributes and methods:

**Phase 2: Upload all the parts**

> **⚠️ Warning:** In the multipart uploader process, it's necessary for you to manage both
> memory usage and concurrent upload requests. Additionally, each part must be a
> minimum of 5MB, except the last one which can be smaller, and all parts should
> be of equal size.

`uploader.uploadPart` accepts the following parameters:

- `partNumber`: (Required) A number identifying which part is uploaded
- `chunkBody`: (Required) A blob object as `ReadableStream`, `String`, `ArrayBuffer` or `Blob` based on these [supported body types](https://developer.mozilla.org/docs/Web/API/fetch#body)

`uploader.uploadPart()` returns an object with the following data for the uploaded part:

**Phase 3: Complete the multipart upload**

`uploader.complete` accepts the following parameters:

- `parts`: (Required) An array containing all the uploaded parts

`uploader.complete()` returns an object with the following data for the created blob object:

## Deleting blobs

This example creates a function that deletes a blob object from the Blob store. You can delete multiple blob objects in a single request by passing an array of blob URLs.

```ts filename="app/delete/route.ts" framework=nextjs-app
import { del } from '@vercel/blob';

export async function DELETE(request: Request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url') as string;
  await del(urlToDelete);

  return new Response();
}
```

```js filename="app/delete/route.js" framework=nextjs-app
import { del } from '@vercel/blob';

export async function DELETE(request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url');
  await del(urlToDelete);

  return new Response();
}
```

```ts filename="app/delete/route.ts" framework=nextjs
import { del } from '@vercel/blob';

export async function DELETE(request: Request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url') as string;
  await del(urlToDelete);

  return new Response();
}
```

```js filename="app/delete/route.js" framework=nextjs
import { del } from '@vercel/blob';

export async function DELETE(request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url');
  await del(urlToDelete);

  return new Response();
}
```

```ts filename="api/blob.ts" framework=other
import { del } from '@vercel/blob';

export async function DELETE(request: Request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url') as string;
  await del(urlToDelete);

  return new Response();
}
```

```js filename="api/blob.js" framework=other
import { del } from '@vercel/blob';

export async function DELETE(request) {
  const { searchParams } = new URL(request.url);
  const urlToDelete = searchParams.get('url');
  await del(urlToDelete);

  return new Response();
}
```

### `del()`

The `del` method deletes one or multiple blob objects from the Blob store.

Since blobs are cached, it may take up to one minute for them to be fully removed from the Vercel CDN cache.

It accepts the following parameters:

- `urlOrPathname`: (Required) A string or array of strings specifying the URL(s) or pathname(s) of the blob object(s) to delete.
- `options`: (Optional) A `JSON` object with the following optional parameter:

| Parameter     | Required | Values                                                                                                                                                                                                 |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`       | No       | A string specifying the read-write token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token) |
| `abortSignal` | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                 |

`del()` returns a `void` response. A delete action is always successful if the blob url exists. A delete action won't throw if the blob url doesn't exists.

## Get blob metadata

This example creates a Function that returns a blob object's metadata.

```ts filename="app/get-blob/route.ts" framework=nextjs-app
import { head } from '@vercel/blob';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

```js filename="app/get-blob/route.js" framework=nextjs-app
import { head } from '@vercel/blob';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

```ts filename="app/get-blob/route.ts" framework=nextjs
import { head } from '@vercel/blob';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

```js filename="app/get-blob/route.js" framework=nextjs
import { head } from '@vercel/blob';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

```ts filename="/api/blob.ts" framework=other
import { head } from '@vercel/blob';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

```js filename="/api/blob.js" framework=other
import { head } from '@vercel/blob';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const blobUrl = searchParams.get('url');
  const blobDetails = await head(blobUrl);

  return Response.json(blobDetails);
}
```

### `head()`

The `head` method returns a blob object's metadata.

It accepts the following parameters:

- `urlOrPathname`: (Required) A string specifying the URL or pathname of the blob object to read.
- `options`: (Optional) A `JSON` object with the following optional parameter:

| Parameter     | Required | Values                                                                                                                                                                                                 |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`       | No       | A string specifying the read-write token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token) |
| `abortSignal` | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                 |

`head()` returns one of the following:

- a `JSON` object with the requested blob object's metadata
- throws a `BlobNotFoundError` if the blob object was not found

## List blobs

This example creates a Function that returns a list of blob objects in a Blob store.

```ts filename="app/get-blobs/route.ts" framework=nextjs-app
import { list } from '@vercel/blob';

export async function GET(request: Request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

```js filename="app/get-blobs/route.js" framework=nextjs-app
import { list } from '@vercel/blob';

export async function GET(request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

```ts filename="app/get-blobs/route.ts" framework=nextjs
import { list } from '@vercel/blob';

export async function GET(request: Request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

```js filename="app/get-blobs/route.js" framework=nextjs
import { list } from '@vercel/blob';

export async function GET(request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

```ts filename="api/blob.ts" framework=other
import { list } from '@vercel/blob';

export async function GET(request: Request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

```js filename="api/blob.js" framework=other
import { list } from '@vercel/blob';

export async function GET(request) {
  const { blobs } = await list();
  return Response.json(blobs);
}
```

### `list()`

The `list` method returns a list of blob objects in a Blob store.

It accepts the following parameters:

- `options`: (Optional) A `JSON` object with the following optional parameters:

| Parameter     | Required | Values                                                                                                                                                                                                 |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`       | No       | A string specifying the read-write token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token) |
| `limit`       | No       | A number specifying the maximum number of blob objects to return. It defaults to 1000                                                                                                                  |
| `prefix`      | No       | A string used to filter for blob objects contained in a specific folder assuming that the folder name was used in the `pathname` when the blob object was uploaded                                     |
| `cursor`      | No       | A string obtained from a previous `list` response to be used for reading the next page of results                                                                                                      |
| `mode`        | No       | A string specifying the response format. Can either be `expanded` (default) or `folded`. In `folded` mode all blobs that are located inside a folder will be folded into a single folder string entry  |
| `abortSignal` | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                                 |

`list()` returns a `JSON` object in the following format:

### Pagination

For a long list of blob objects (the default list `limit` is 1000), you can use the `cursor` and `hasMore` parameters to paginate through the results as shown in the example below:

### Folders

To retrieve the folders from your blob store, alter the `mode` parameter to modify the response format of the list operation.
The default value of `mode` is `expanded`, which returns all blobs in a single array of objects.

Alternatively, you can set `mode` to `folded` to roll up all blobs located inside a folder into a single entry.
These entries will be included in the response as `folders`. Blobs that are not located in a folder will still be returned in the blobs property.

By using the `folded` mode, you can efficiently retrieve folders and subsequently list the blobs inside them by using the returned `folders` as a `prefix` for further requests.
Omitting the `prefix` parameter entirely, will return all folders in the root of your store. Be aware that the blobs pathnames and the folder names will always be fully quantified and never relative to the prefix you passed.

## Copy a blob

This example creates a Function that copies an existing blob to a new path in the store.

```ts filename="app/copy-blob/route.ts" framework=nextjs-app
import { copy } from '@vercel/blob';

export async function PUT(request: Request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl') as string;
  const toPathname = form.get('toPathname') as string;

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

```js filename="app/copy-blob/route.js" framework=nextjs-app
import { copy } from '@vercel/blob';

export async function PUT(request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl');
  const toPathname = form.get('toPathname');

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

```ts filename="app/copy-blob/route.ts" framework=nextjs
import { copy } from '@vercel/blob';

export async function PUT(request: Request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl') as string;
  const toPathname = form.get('toPathname') as string;

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

```js filename="app/copy-blob/route.js" framework=nextjs
import { copy } from '@vercel/blob';

export async function PUT(request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl');
  const toPathname = form.get('toPathname');

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

```ts filename="api/copy-blob.ts" framework=other
import { copy } from '@vercel/blob';

export async function PUT(request: Request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl') as string;
  const toPathname = form.get('toPathname') as string;

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

```js filename="api/copy-blob.js" framework=other
import { copy } from '@vercel/blob';

export async function PUT(request) {
  const form = await request.formData();

  const fromUrl = form.get('fromUrl');
  const toPathname = form.get('toPathname');

  const blob = await copy(fromUrl, toPathname, { access: 'public' });

  return Response.json(blob);
}
```

### `copy()`

The `copy` method copies an existing blob object to a new path inside the blob store.

The `contentType` and `cacheControlMaxAge` will not be copied from the source blob. If the values should be carried over to the copy, they need to be defined again in the options object.

Contrary to `put()`, `addRandomSuffix` is false by default. This means no automatic random id suffix is added to your blob url, unless you pass `addRandomSuffix: true`. This also means `copy()` overwrites files per default, if the operation targets a pathname that already exists.

It accepts the following parameters:

- `fromUrlOrPathname`: (Required) A blob URL or pathname identifying an already existing blob
- `toPathname`: (Required) A string specifying the new path inside the blob store. This will be the base value of the return URL
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter            | Required | Values                                                                                                                                                                                      |
| -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`             | Yes      | `public`                                                                                                                                                                                    |
| `contentType`        | No       | A string indicating the [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type). By default, it's extracted from the toPathname's extension.                         |
| `token`              | No       | A string specifying the token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token) |
| `addRandomSuffix`    | No       | A boolean specifying whether to add a random suffix to the pathname. It defaults to `false`.                                                                                                |
| `cacheControlMaxAge` | No       | A number in seconds to configure the edge and browser cache. Defaults to one year. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.                    |
| `abortSignal`        | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                                                      |

`copy()` returns a `JSON` object with the following data for the copied blob object:

An example blob is:

```json
{
  "pathname": "profilesv1/user-12345-copy.txt",
  "contentType": "text/plain",
  "contentDisposition": "attachment; filename=\"user-12345-copy.txt\"",
  "url": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345-copy.txt",
  "downloadUrl": "https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345-copy.txt?download=1"
}
```

## Client uploads

As seen in the [client uploads quickstart docs](/docs/storage/vercel-blob/client-upload), you can upload files directly from clients (like browsers) to the Blob store.

All client uploads related methods are available under `@vercel/blob/client`.

### `upload()`

The `upload` method is dedicated to [client uploads](/docs/storage/vercel-blob/client-upload). It fetches a client token on your server using the `handleUploadUrl` before uploading the blob. Read the [client uploads](/docs/storage/vercel-blob/client-upload) documentation to learn more.

```js
upload(pathname, body, options);
```

It accepts the following parameters:

- `pathname`: (Required) A string specifying the base value of the return URL
- `body`: (Required) A blob object as `ReadableStream`, `String`, `ArrayBuffer` or `Blob` based on these [supported body types](https://developer.mozilla.org/docs/Web/API/fetch#body)
- `options`: (Required) A `JSON` object with the following required and optional parameters:

| Parameter          | Required | Values                                                                                                                                                            |
| ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access`           | Yes      | `public`                                                                                                                                                          |
| `contentType`      | No       | A string indicating the [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type). By default, it's extracted from the pathname's extension. |
| `handleUploadUrl`  | Yes\*    | A string specifying the route to call for generating client tokens for [client uploads](/docs/storage/vercel-blob/client-upload).                                 |
| `clientPayload`    | No       | A string to be sent to your `handleUpload` server code. Example use-case: attaching the post id an image relates to. So you can use it to update your database.   |
| `multipart`        | No       | Pass `multipart: true` when uploading large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.                    |
| `abortSignal`      | No       | An [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the operation                                                            |
| `onUploadProgress` | No       | Callback to track upload progress: `onUploadProgress({loaded: number, total: number, percentage: number})`                                                        |

`upload()` returns a `JSON` object with the following data for the created blob object:

```ts
{
  pathname: string;
  contentType: string;
  contentDisposition: string;
  url: string;
  downloadUrl: string;
}
```

An example `url` is:

```
https://ce0rcu23vrrdzqap.public.blob.vercel-storage.com/profilesv1/user-12345-NoOVGDVcqSPc7VYCUAGnTzLTG2qEM2.txt
```

### `handleUpload()`

A server-side route helper to manage client uploads, it has two responsibilities:

1. Generate tokens for client uploads
2. Listen for completed client uploads, so you can update your database with the URL of the uploaded file for example

```js
handleUpload(options);
```

It accepts the following parameters:

- `options`: (Required) A `JSON` object with the following parameters:

| Parameter                                         | Required | Values                                                                                                                                                                                                 |
| ------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `token`                                           | No       | A string specifying the read-write token to use when making requests. It defaults to `process.env.BLOB_READ_WRITE_TOKEN` when deployed on Vercel as explained in [Read-write token](#read-write-token) |
| `request`                                         | Yes      | An `IncomingMessage` or `Request` object to be used to determine the action to take                                                                                                                    |
| [`onBeforeGenerateToken`](#onbeforegeneratetoken) | Yes      | A function to be called right before generating client tokens for client uploads. See below for usage                                                                                                  |
| [`onUploadCompleted`](#onuploadcompleted)         | Yes      | A function to be called by Vercel Blob when the client upload finishes. This is useful to update your database with the blob url that was uploaded                                                     |
| `body`                                            | Yes      | The request body                                                                                                                                                                                       |

`handleUpload()` returns:

```ts
Promise<
  | { type: 'blob.generate-client-token'; clientToken: string }
  | { type: 'blob.upload-completed'; response: 'ok' }
>;
```

#### `onBeforeGenerateToken()`

The `onBeforeGenerateToken` function receives the following arguments:

- `pathname`: The destination path for the blob
- `clientPayload`: A string payload specified on the client when calling `upload()`
- `multipart`: A boolean specifying whether the file is a multipart upload.

The function must return an object with the following properties:

| Parameter             | Required | Values                                                                                                                                                                                                                    |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addRandomSuffix`     | No       | A boolean specifying whether to add a random suffix to the `pathname`. It defaults to `false`. **We recommend using this option** to ensure there are no conflicts in your blob filenames.                                |
| `allowedContentTypes` | No       | An array of strings specifying the [media type](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Type) that are allowed to be uploaded. By default, it's all content types. Wildcards are supported (`text/*`) |
| `maximumSizeInBytes`  | No       | A number specifying the maximum size in bytes that can be uploaded. The maximum is 5TB.                                                                                                                                   |
| `validUntil`          | No       | A number specifying the timestamp in ms when the token will expire. By default, it's now + 1 hour.                                                                                                                        |
| `allowOverwrite`      | No       | A boolean to allow overwriting blobs. By default an error will be thrown if you try to overwrite a blob by using the same `pathname` for multiple blobs.                                                                  |
| `cacheControlMaxAge`  | No       | A number in seconds to configure how long Blobs are cached. Defaults to one month. Cannot be set to a value lower than 1 minute. See the [caching](/docs/storage/vercel-blob/#caching) documentation for more details.    |
| `callbackUrl`         | No       | A string specifying the URL that Vercel Blob will call when the upload completes. See [client uploads](/docs/storage/vercel-blob/client-upload) for examples.                                                             |
| `tokenPayload`        | No       | A string specifying a payload to be sent to your server on upload completion.                                                                                                                                             |

#### `onUploadCompleted()`

The `onUploadCompleted` function receives the following arguments:

- `blob`: The blob that was uploaded. See the return type of [`put()`](#put) for more details.
- `tokenPayload`: The payload that was defined in the [`onBeforeGenerateToken()`](#onbeforegeneratetoken) function.

### Client uploads routes

Here's an example Next.js App Router route handler that uses `handleUpload()`:

```ts filename="app/api/post/upload/route.ts"
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client';
import { NextResponse } from 'next/server';

// Use-case: uploading images for blog posts
export async function POST(request: Request): Promise<NextResponse> {
  const body = (await request.json()) as HandleUploadBody;

  try {
    const jsonResponse = await handleUpload({
      body,
      request,
      onBeforeGenerateToken: async (pathname, clientPayload) => {
        // Generate a client token for the browser to upload the file
        // ⚠️ Authenticate and authorize users before generating the token.
        // Otherwise, you're allowing anonymous uploads.

        // ⚠️ When using the clientPayload feature, make sure to validate it
        // otherwise this could introduce security issues for your app
        // like allowing users to modify other users' posts

        return {
          allowedContentTypes: [
            // optional, default to all content types
            'image/jpeg',
            'image/png',
            'image/webp',
            'text/*',
          ],
          // callbackUrl: 'https://example.com/api/avatar/upload',
          // optional, `callbackUrl` is automatically computed when hosted on Vercel
        };
      },
      onUploadCompleted: async ({ blob, tokenPayload }) => {
        // Get notified of client upload completion
        // ⚠️ This will not work on `localhost` websites,
        // Use ngrok or similar to get the full upload flow

        console.log('blob upload completed', blob, tokenPayload);

        try {
          // Run any logic after the file upload completed,
          // If you've already validated the user and authorization prior, you can
          // safely update your database
        } catch (error) {
          throw new Error('Could not update post');
        }
      },
    });

    return NextResponse.json(jsonResponse);
  } catch (error) {
    return NextResponse.json(
      { error: error instanceof Error ? error.message : String(error) },
      { status: 400 }, // The webhook will retry 5 times waiting for a 200
    );
  }
}
```

## Handling errors

When you make a request to the SDK using any of the above methods, they will return an error if the request fails due to any of the following reasons:

- Missing required parameters
- An invalid token or a token that does have access to the Blob object
- Suspended Blob store
- Blob file or Blob store not found
- Unforeseen or unknown errors

To catch these errors, wrap your requests with a `try/catch` statement as shown below:


--------------------------------------------------------------------------------
title: "Attack Challenge Mode"
description: "Learn how to use Attack Challenge Mode to help control who has access to your site when it"
last_updated: "2026-02-03T02:58:49.539Z"
source: "https://vercel.com/docs/vercel-firewall/attack-challenge-mode"
--------------------------------------------------------------------------------