> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Upgrade @sanity/client from an older major version

Find which @sanity/client version you run, why pre-v3 clients with a token skip the API CDN, and what changed in each major version.

Each major version of `@sanity/client` changes some defaults or requirements. On a project that still runs an old major version, those changes decide whether your queries reach the API CDN, how failed requests are handled, and which documents your queries return.

This guide shows how to find which version you run, explains the pre-v3 behavior that sends token-authenticated queries to the live API, and lists what to update for each major version.

Prerequisites:

- Access to the project's `package.json` and lockfile.
- Node.js 22.12 or later to run v8. If you can't upgrade Node.js, upgrade to v7, which runs on Node.js 20.

## Check which version of @sanity/client you run

List every installed copy of the client. Other packages can depend on their own, older copy, so check each line of the output, not only your direct dependency:

**CLI**

```sh
npm ls @sanity/client
```

To check the behavior instead of the version number, read the resolved configuration from a client instance. `client.config().useCdn` returns the value the client actually uses, which can differ from the value you passed in.

## Tokens turn off the API CDN before v3

In `@sanity/client` v2 and earlier, setting a `token` turns `useCdn` off, even when you pass `useCdn: true`. The client sends every query to the live, uncached API at `api.sanity.io`, so token-authenticated traffic never benefits from the [API CDN](https://www.sanity.io/docs/content-lake/api-cdn) and counts against the live API's rate limits. A frontend that sends a token on every request can hit `429 Too Many Requests` responses while its configuration says `useCdn: true`.

In Node.js, v2 and earlier log a warning when you create the client:

```text
You have set `useCdn` to `true` while also specifying a token. This is usually not what you want. The CDN cannot be used with an authorization token, since private data cannot be cached. See https://docs.sanity.io/help/js-client-usecdn-token for more information.
```

In a browser on a domain other than localhost, the client logs nothing, so the switch to the live API goes unnoticed.

Starting with v3.0.0, a client with a `token` and `useCdn: true` sends queries to the API CDN at `apicdn.sanity.io`. The API CDN caches authenticated responses separately for each token. To fix the behavior, upgrade to v3 or later; the latest major version is the recommended target.

The same configuration on v2 and on v8:

**Before (v2)**

```javascript
const sanityClient = require('@sanity/client')

// On v2 and earlier, the token turns useCdn off.
// Every query goes to the live API at api.sanity.io.
const client = sanityClient({
  projectId: 'YOUR_PROJECT_ID',
  dataset: 'production',
  token: process.env.SANITY_API_READ_TOKEN,
  useCdn: true,
})

console.log(client.config().useCdn) // false
```

**After (v8)**

```typescript
import {createClient} from '@sanity/client'

export const client = createClient({
  projectId: 'YOUR_PROJECT_ID',
  dataset: 'production',
  apiVersion: '2026-03-01',
  token: process.env.SANITY_API_READ_TOKEN,
  useCdn: true,
})

console.log(client.config().useCdn) // true
```

> [!NOTE]
> Choose useCdn for each token client
> After the upgrade, a token client with `useCdn: true` can receive cached responses. If a client needs the freshest data on every request, such as a client that previews drafts, set `useCdn: false` on that client explicitly.
> In every version, `withCredentials: true` turns the API CDN off.

## What changed in each major version

Read every section between your current version and the version you upgrade to. Each section links to the full release notes or migration guide in the `@sanity/client` repository.

### v3

- A `token` combined with `useCdn: true` uses the API CDN instead of the live API.
- Requires Node.js 12 or later.
- Removes the deprecated `merge` patch operation and the deprecated `document` property on the `assets.upload` response.
- `client.observable.fetch()` returns a cold observable.

Details: [v3.0.0 release notes](https://github.com/sanity-io/client/blob/main/CHANGELOG.md#300).

### v4

- Expands ES module support. Some bundler and runtime setups might need configuration changes.

Details: [v4.0.0 release notes](https://github.com/sanity-io/client/blob/main/CHANGELOG.md#400-2023-01-02).

### v5

- Replaces the default export with the named export `createClient`. Change `import sanityClient from '@sanity/client'` to `import {createClient} from '@sanity/client'`. From v5.2.1, the default export works again, but it's deprecated and logs a warning.
- Requires Node.js 14.18 or later and no longer ships ES5 output.
- Removes `client.assets.getImageUrl()` (use `@sanity/image-url`), `client.assets.delete()` (use `client.delete()`), and `client.clientConfig` (use `client.config()`).

Details: [Migrating from v4](https://github.com/sanity-io/client/blob/main/docs/MIGRATE-v4.md).

### v6

- `useCdn` defaults to `true`. Set `useCdn: false` to keep querying the live API.
- Retries `GET` and `HEAD` requests and queries automatically when the API responds with `429`, `502`, or `503`. Also retries `GET` and `HEAD` requests on network errors. If your application has its own retry logic, remove it or set `maxRetries: 0`. See [Automatic retries](https://www.sanity.io/docs/apis-and-sdks/js-client-advanced).

Details: [Migrating from v5](https://github.com/sanity-io/client/blob/main/docs/MIGRATE-v5.md).

### v7

- Requires Node.js 20 or later.

Details: [v7.0.0 release notes](https://github.com/sanity-io/client/blob/main/CHANGELOG.md#700-2025-04-30).

### v8

- Requires Node.js 22.12 or later and ships as ES modules only. `require('@sanity/client')` still works on Node.js 22.12 or later.
- TypeScript projects need `moduleResolution` set to `node16`, `nodenext`, or `bundler`.
- Removes the `requester` option, the per-request `proxy` option, and the UMD bundle.
- Retries DNS `ENOTFOUND` errors for `GET` and `HEAD` requests. Set `maxRetries: 0` to fail immediately.
- Adds the HTTP status text to `ClientError` and `ServerError` messages, for example `HTTP 400 Bad Request`. Update any code that matches on error message text.

Details: [Migrating from v7](https://github.com/sanity-io/client/blob/main/docs/MIGRATE-v7.md).

## Set apiVersion to control the default perspective

Which documents a query returns by default depends on the `apiVersion` you set, not on the client's major version. Requests with an `apiVersion` earlier than `2025-02-19` use the `raw` [perspective](https://www.sanity.io/docs/content-lake/perspectives), which returns drafts alongside published documents when the request includes a token. From `2025-02-19`, the default is `published`.

Clients older than v2.7.0 have no `apiVersion` option and always use API version `v1`. When you upgrade, set `apiVersion` to a fixed date and test your queries against it. See [API versioning](https://www.sanity.io/docs/content-lake/api-versioning).

## Fix the ERR_REQUIRE_ESM error after upgrading to v8

If `require('@sanity/client')` fails with `Error [ERR_REQUIRE_ESM]: require() of ES Module`, the project usually runs v8 on a Node.js version earlier than 22.12. When npm installs v8 on those versions, it also warns `npm warn EBADENGINE Unsupported engine`. Upgrade Node.js to 22.12 or later. If you can't upgrade Node.js, install v7 with `npm install @sanity/client@7`. v7 runs on Node.js 20 or later.

## Next steps

- To set up a new client on the current version, see [Getting started with @sanity/client](https://www.sanity.io/docs/apis-and-sdks/js-client-getting-started).
- To tune automatic retries or cancel requests, see [Advanced client patterns with @sanity/client](https://www.sanity.io/docs/apis-and-sdks/js-client-advanced).
- To choose a perspective for your queries, see [Querying content with @sanity/client](https://www.sanity.io/docs/apis-and-sdks/js-client-querying).

