---
title: "PayloadCMS — Local API"
aliases: [local-api, payload-local-api, payload-server-api]
tags: [payloadcms, local-api, server-side, nextjs, typescript]
sources:
  - raw/local-api__overview.md
  - https://payloadcms.com/docs/local-api/overview
  - https://payloadcms.com/docs/local-api/outside-nextjs
  - https://payloadcms.com/docs/local-api/server-functions
  - https://payloadcms.com/docs/local-api/access-control
created: 2026-05-15
updated: 2026-05-15
---

# PayloadCMS — Local API

## Overview

The Local API executes the same operations as REST/GraphQL but **directly against the database** — no HTTP layer, no latency. Ideal for React Server Components, seed scripts, hooks, and custom route handlers.

- Access via `req.payload` (in hooks/access control) or import `getPayload`
- **Access control is skipped by default** (`overrideAccess: true`)
- Full TypeScript inference from generated types
- Works in Next.js (with HMR) and standalone scripts

## Accessing Payload

```ts
// Option 1: from hook/access control args
const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } }) => {
  const posts = await payload.find({ collection: 'posts' })
}

// Option 2: import and initialize
import { getPayload } from 'payload'
import config from '@payload-config'

const payload = await getPayload({ config })
```

## Collection Operations

### Create

```ts
const post = await payload.create({
  collection: 'posts',
  data: { title: 'sure', description: 'maybe' },
  locale: 'en',
  overrideAccess: true,
  filePath: path.resolve(__dirname, './image.jpg'), // for upload collections
  duplicateFromID: 'existing-doc-id',              // optional clone
})
```

### Find

```ts
const result = await payload.find({
  collection: 'posts',
  depth: 2,
  page: 1,
  limit: 10,
  pagination: false, // skip count queries
  where: { status: { equals: 'published' } },
  sort: '-createdAt',
  locale: 'en',
  overrideAccess: false,
  user: dummyUser,
})
```

### Find by ID

```ts
const result = await payload.findByID({
  collection: 'posts',
  id: '507f1f77bcf86cd799439011',
  depth: 2,
  disableErrors: true, // returns null instead of throwing
})
```

### Count / FindDistinct

```ts
const { totalDocs } = await payload.count({ collection: 'posts', where: {} })

const { values } = await payload.findDistinct({
  collection: 'posts',
  field: 'title',
  sort: 'title',
})
```

### Update (by ID or bulk)

```ts
// By ID
await payload.update({ collection: 'posts', id: '...', data: { title: 'new' } })

// Bulk — returns { docs: [], errors: [] }
await payload.update({
  collection: 'posts',
  where: { fieldName: { equals: 'value' } },
  data: { title: 'updated' },
  overrideLock: false, // enforce document locks
})
```

### Delete (by ID or bulk)

```ts
await payload.delete({ collection: 'posts', id: '...' })
await payload.delete({ collection: 'posts', where: { fieldName: { equals: 'v' } } })
```

## Auth Operations

```ts
// Login
const { token, user, exp } = await payload.login({
  collection: 'users',
  data: { email: 'dev@payload.com', password: 'secret' },
})

// Current user from headers
const { user, permissions } = await payload.auth({ headers, canSetHeaders: false })

// Password reset flow
const token = await payload.forgotPassword({ collection: 'users', data: { email: '...' } })
const result = await payload.resetPassword({ collection: 'users', data: { password: 'new', token } })

// Verify email
await payload.verifyEmail({ collection: 'users', token: '...' })
await payload.unlock({ collection: 'users', data: { email: '...' } })
```

## Global Operations

```ts
const header = await payload.findGlobal({ slug: 'header', depth: 2 })

await payload.updateGlobal({
  slug: 'header',
  data: { nav: [{ url: 'https://example.com' }] },
  overrideLock: false,
})
```

## Server Functions (Next.js)

Local API cannot be called from client components directly. Use `'use server'` functions as a bridge:

```ts
'use server'
import { getPayload } from 'payload'
import config from '@payload-config'

export async function createPost(data) {
  const payload = await getPayload({ config })
  return await payload.create({ collection: 'posts', data })
}
```

### Built-in auth server functions (`@payloadcms/next/auth`)

```ts
import { login, logout, refresh } from '@payloadcms/next/auth'

// login — sets auth cookie
await login({ collection: 'users', config, email, password })

// logout — clears auth cookie
await logout({ allSessions: true, config })

// refresh — rotates JWT
await refresh({ config })
```

## Outside Next.js (Standalone Scripts)

```ts
import { getPayload } from 'payload'
import config from '@payload-config'

const seed = async () => {
  const payload = await getPayload({ config })
  await payload.create({ collection: 'users', data: { email: 'dev@example.com', password: 'pw' } })
}
await seed()
```

Run with: `payload run src/seed.ts`
- Loads `.env` the same way Next.js does (no `dotenv` needed)
- Flags: `--use-swc` (faster, needs `@swc-node/register`), `--disable-transpile` (for Bun)

## Key Options Reference

| Option | Default | Description |
|--------|---------|-------------|
| `overrideAccess` | `true` | Skip access control checks |
| `user` | — | User to check against (when `overrideAccess: false`) |
| `depth` | `2` | Relationship auto-population depth |
| `locale` / `fallbackLocale` | — | Localization |
| `select` | — | Field projection |
| `populate` | — | Override `defaultPopulate` for related docs |
| `pagination` | `true` | Set `false` to skip count queries |
| `showHiddenFields` | `false` | Return hidden fields |
| `overrideLock` | `true` | Ignore document locks |
| `disableErrors` | `false` | Return null/empty instead of throwing |
| `disableTransaction` | `false` | Skip DB transaction |
| `context` | — | Pass extra data to hooks via `req.context` |

## Access Control

```ts
// Skip access control (default)
await payload.create({ collection: 'users', data: { ... } })

// Enforce access control
await payload.create({
  collection: 'users',
  overrideAccess: false,
  user, // authenticated user document
  data: { ... },
})
```

## Transactions

Pass `req` through all local operations when using Postgres or MongoDB replica sets:

```ts
const post = await payload.find({ collection: 'posts', req })
```

## Gotchas

- Access control is **off by default** — easy to accidentally expose privileged data in seed scripts
- `disableErrors: true` silences `findByID` errors; use carefully in production
- `payload run` handles `.env` loading; avoid `dotenv` alongside it
- Server functions must use `'use server'` directive and can't directly import Payload config in client components
- For file uploads in server functions, merge the `File` object into `data` before calling `payload.create()`
- `beforeRead`/`afterRead` hooks may not receive full doc when `select` is active — use entity-level `select` config to force fields

## Key Takeaways

- **No HTTP overhead** — Local API hits the DB directly; ideal for RSC, seeds, hooks, custom route handlers
- **Access control off by default** (`overrideAccess: true`) — always set `overrideAccess: false` + `user` for user-facing logic
- **Two access patterns:** `req.payload` (in hooks/access control) vs `await getPayload({ config })` (everywhere else)
- **Thread `req` through** all operations when using Postgres or MongoDB replica sets (transactions)
- **Full TypeScript inference** — `payload.create({ collection: 'posts', data: { ... } })` returns typed `Post`
- **`pagination: false`** skips count queries — useful for internal data fetching where counts aren't needed
- **`disableErrors: true`** makes `findByID` return `null` instead of throwing — use cautiously in production
- **`context`** passes extra metadata to hooks without polluting document data (e.g. `triggerBeforeChange` flag)
- **Server functions** (`'use server'`) are the bridge for using Local API from client-triggered actions in Next.js
- **`payload run`** CLI handles `.env` automatically for standalone scripts — no need for `dotenv`

## Related

- [[wiki/payloadcms/queries|Queries]]
- [[wiki/payloadcms/rest-api|REST API]]
- [[wiki/payloadcms/hooks|Hooks]]
- [[wiki/payloadcms/authentication-operations|Authentication — Operations]]
- [[wiki/payloadcms/local-api-outside-nextjs|Local API — Outside Next.js]]
- [[wiki/payloadcms/local-api-access-control|Local API — Access Control]]
- [[wiki/payloadcms/database-transactions|Database — Transactions]]
