Local API

The Payload Local API gives you the ability to execute the same operations that are available through REST and GraphQL within Node, directly on your server. Here, you don't need to deal with server latency or network speed whatsoever and can interact directly with your database.

Here are some common examples of how you can use the Local API:

  • Fetching Payload data within React Server Components
  • Seeding data via Node seed scripts that you write and maintain
  • Opening custom Next.js route handlers which feature additional functionality but still rely on Payload
  • Within Access Control and Hooks

Accessing Payload

You can gain access to the currently running payload object via two ways:

Accessing from args or req

In most places within Payload itself, you can access payload directly from the arguments of Hooks, Access Control, Validation functions, and similar. This is the simplest way to access Payload in most cases. Most config functions take the req (Request) object, which has Payload bound to it (req.payload).

Example:

1
const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } }) => {
2
const posts = await payload.find({
3
collection: 'posts',
4
})
5
}

Importing it

If you want to import Payload in places where you don't have the option to access it from function arguments or req, you can import it and initialize it.

There are two places to import Payload:

Option 1 - using HMR, within Next.js

1
import { getPayloadHMR } from '@payloadcms/next/utilities'
2
import config from '@payload-config'
3
4
const payload = await getPayloadHMR({ config })

You should import Payload using the first option (getPayloadHMR) if you are using Payload inside of Next.js (like route handlers, server components, and similar.)

This way, in Next.js development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, getPayloadHMR simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.

If you are accessing Payload via function arguments or req.payload, HMR is automatically supported if you are using it within Next.js.

Option 2 - outside of Next.js

If you are using Payload outside of Next.js, for example in standalone scripts or in other frameworks, you can import Payload with no HMR functionality.

1
import { getPayload } from 'payload'
2
import { importConfig } from 'payload/node'
3
4
const config = await importConfig('./payload.config.ts')
5
const payload = await getPayload({ config })

Both options function in exactly the same way outside of one having HMR support and the other not. However, when you import your Payload Config, you need to make sure that you can import it safely.

For more information about using Payload outside of Next.js, click here.

Local options available

You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.

Local OptionDescription
collectionRequired for Collection operations. Specifies the Collection slug to operate against.
dataThe data to use within the operation. Required for create, update.
depthControl auto-population of nested relationship and upload fields.
localeSpecify locale for any returned documents.
fallbackLocaleSpecify a fallback locale to use for any returned documents.
overrideAccessSkip access control. By default, this property is set to true within all Local API operations.
userIf you set overrideAccess to false, you can pass a user to use against the access control checks.
showHiddenFieldsOpt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config.
paginationSet to false to return all documents and avoid querying for document counts.
contextContext, which will then be passed to context and req.context, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a triggerBeforeChange option which can be read by the BeforeChange hook to determine if it should run or not.

There are more options available on an operation by operation basis outlined below.

Collections

The following Collection operations are available through the Local API:

Create

1
// The created Post document is returned
2
const post = await payload.create({
3
collection: 'posts', // required
4
data: {
5
// required
6
title: 'sure',
7
description: 'maybe',
8
},
9
locale: 'en',
10
fallbackLocale: false,
11
user: dummyUserDoc,
12
overrideAccess: true,
13
showHiddenFields: false,
14
15
// If creating verification-enabled auth doc,
16
// you can optionally disable the email that is auto-sent
17
disableVerificationEmail: true,
18
19
// If your collection supports uploads, you can upload
20
// a file directly through the Local API by providing
21
// its full, absolute file path.
22
filePath: path.resolve(__dirname, './path-to-image.jpg'),
23
24
// Alternatively, you can directly pass a File,
25
// if file is provided, filePath will be omitted
26
file: uploadedFile,
27
})

Find

1
// Result will be a paginated set of Posts.
2
// See /docs/queries/pagination for more.
3
const result = await payload.find({
4
collection: 'posts', // required
5
depth: 2,
6
page: 1,
7
limit: 10,
8
pagination: false, // If you want to disable pagination count, etc.
9
where: {}, // pass a `where` query here
10
sort: '-title',
11
locale: 'en',
12
fallbackLocale: false,
13
user: dummyUser,
14
overrideAccess: false,
15
showHiddenFields: true,
16
})

Find by ID

1
// Result will be a Post document.
2
const result = await payload.findByID({
3
collection: 'posts', // required
4
id: '507f1f77bcf86cd799439011', // required
5
depth: 2,
6
locale: 'en',
7
fallbackLocale: false,
8
user: dummyUser,
9
overrideAccess: false,
10
showHiddenFields: true,
11
})

Count

1
// Result will be an object with:
2
// {
3
// totalDocs: 10, // count of the documents satisfies query
4
// }
5
const result = await payload.count({
6
collection: 'posts', // required
7
locale: 'en',
8
where: {}, // pass a `where` query here
9
user: dummyUser,
10
overrideAccess: false,
11
})

Update by ID

1
// Result will be the updated Post document.
2
const result = await payload.update({
3
collection: 'posts', // required
4
id: '507f1f77bcf86cd799439011', // required
5
data: {
6
// required
7
title: 'sure',
8
description: 'maybe',
9
},
10
depth: 2,
11
locale: 'en',
12
fallbackLocale: false,
13
user: dummyUser,
14
overrideAccess: false,
15
showHiddenFields: true,
16
17
// If your collection supports uploads, you can upload
18
// a file directly through the Local API by providing
19
// its full, absolute file path.
20
filePath: path.resolve(__dirname, './path-to-image.jpg'),
21
22
// If you are uploading a file and would like to replace
23
// the existing file instead of generating a new filename,
24
// you can set the following property to `true`
25
overwriteExistingFiles: true,
26
})

Update Many

1
// Result will be an object with:
2
// {
3
// docs: [], // each document that was updated
4
// errors: [], // each error also includes the id of the document
5
// }
6
const result = await payload.update({
7
collection: 'posts', // required
8
where: {
9
// required
10
fieldName: { equals: 'value' },
11
},
12
data: {
13
// required
14
title: 'sure',
15
description: 'maybe',
16
},
17
depth: 0,
18
locale: 'en',
19
fallbackLocale: false,
20
user: dummyUser,
21
overrideAccess: false,
22
showHiddenFields: true,
23
24
// If your collection supports uploads, you can upload
25
// a file directly through the Local API by providing
26
// its full, absolute file path.
27
filePath: path.resolve(__dirname, './path-to-image.jpg'),
28
29
// If you are uploading a file and would like to replace
30
// the existing file instead of generating a new filename,
31
// you can set the following property to `true`
32
overwriteExistingFiles: true,
33
})

Delete

1
// Result will be the now-deleted Post document.
2
const result = await payload.delete({
3
collection: 'posts', // required
4
id: '507f1f77bcf86cd799439011', // required
5
depth: 2,
6
locale: 'en',
7
fallbackLocale: false,
8
user: dummyUser,
9
overrideAccess: false,
10
showHiddenFields: true,
11
})

Delete Many

1
// Result will be an object with:
2
// {
3
// docs: [], // each document that is now deleted
4
// errors: [], // any errors that occurred, including the id of the errored on document
5
// }
6
const result = await payload.delete({
7
collection: 'posts', // required
8
where: {
9
// required
10
fieldName: { equals: 'value' },
11
},
12
depth: 0,
13
locale: 'en',
14
fallbackLocale: false,
15
user: dummyUser,
16
overrideAccess: false,
17
showHiddenFields: true,
18
})

Auth Operations

If a collection has Authentication enabled, additional Local API operations will be available:

Login

1
// result will be formatted as follows:
2
// {
3
// token: 'o38jf0q34jfij43f3f...', // JWT used for auth
4
// user: { ... } // the user document that just logged in
5
// exp: 1609619861 // the UNIX timestamp when the JWT will expire
6
// }
7
8
const result = await payload.login({
9
collection: 'users', // required
10
data: {
11
// required
12
email: 'dev@payloadcms.com',
13
password: 'rip',
14
},
15
req: req, // pass a Request object to be provided to all hooks
16
res: res, // used to automatically set an HTTP-only auth cookie
17
depth: 2,
18
locale: 'en',
19
fallbackLocale: false,
20
overrideAccess: false,
21
showHiddenFields: true,
22
})

Forgot Password

1
// Returned token will allow for a password reset
2
const token = await payload.forgotPassword({
3
collection: 'users', // required
4
data: {
5
// required
6
email: 'dev@payloadcms.com',
7
},
8
req: req, // pass a Request object to be provided to all hooks
9
})

Reset Password

1
// Result will be formatted as follows:
2
// {
3
// token: 'o38jf0q34jfij43f3f...', // JWT used for auth
4
// user: { ... } // the user document that just logged in
5
// }
6
const result = await payload.resetPassword({
7
collection: 'users', // required
8
data: {
9
// required
10
password: req.body.password, // the new password to set
11
token: 'afh3o2jf2p3f...', // the token generated from the forgotPassword operation
12
},
13
req: req, // pass a Request object to be provided to all hooks
14
res: res, // used to automatically set an HTTP-only auth cookie
15
})

Unlock

1
// Returned result will be a boolean representing success or failure
2
const result = await payload.unlock({
3
collection: 'users', // required
4
data: {
5
// required
6
email: 'dev@payloadcms.com',
7
},
8
req: req, // pass a Request object to be provided to all hooks
9
overrideAccess: true,
10
})

Verify

1
// Returned result will be a boolean representing success or failure
2
const result = await payload.verifyEmail({
3
collection: 'users', // required
4
token: 'afh3o2jf2p3f...', // the token saved on the user as `_verificationToken`
5
})

Globals

The following Global operations are available through the Local API:

Find

1
// Result will be the Header Global.
2
const result = await payload.findGlobal({
3
slug: 'header', // required
4
depth: 2,
5
locale: 'en',
6
fallbackLocale: false,
7
user: dummyUser,
8
overrideAccess: false,
9
showHiddenFields: true,
10
})

Update

1
// Result will be the updated Header Global.
2
const result = await payload.updateGlobal({
3
slug: 'header', // required
4
data: {
5
// required
6
nav: [
7
{
8
url: 'https://google.com',
9
},
10
{
11
url: 'https://payloadcms.com',
12
},
13
],
14
},
15
depth: 2,
16
locale: 'en',
17
fallbackLocale: false,
18
user: dummyUser,
19
overrideAccess: false,
20
showHiddenFields: true,
21
})

TypeScript

Local API calls will automatically infer your generated types.

Here is an example of usage:

1
// Properly inferred as `Post` type
2
const post = await payload.create({
3
collection: 'posts',
4
5
// Data will now be typed as Post and give you type hints
6
data: {
7
title: 'my title',
8
description: 'my description',
9
},
10
})
Next

Using Payload outside Next.js