183 lines
5.8 KiB
Markdown
183 lines
5.8 KiB
Markdown
---
|
|
title: "Local API — Server Functions"
|
|
aliases: [server-functions, server-actions-payload, payload-server-actions]
|
|
tags: [payloadcms, local-api, server-functions, next-js, authentication]
|
|
sources: [raw/local-api__server-functions.md]
|
|
created: 2026-05-15
|
|
updated: 2026-05-15
|
|
---
|
|
|
|
## Overview
|
|
|
|
**Server functions** (formerly "server actions") are Next.js-native async functions marked with `'use server'` that run exclusively on the server. They bridge the gap between client components and Payload's [[wiki/payloadcms/local-api|Local API]], which cannot be called directly from client-side code.
|
|
|
|
## Why Use Server Functions
|
|
|
|
- **Local API is server-only** — client components can't call `payload.*` directly; server functions act as the secure bridge
|
|
- **No extra endpoints needed** — avoids creating REST/GraphQL routes just for simple CRUD operations
|
|
- **Security** — restricts exposed operations to only what the function explicitly allows
|
|
- **Performance** — Next.js optimizes caching, DB queries, and network overhead automatically
|
|
|
|
## Basic Pattern
|
|
|
|
```ts
|
|
// server/actions.ts
|
|
'use server'
|
|
|
|
import { getPayload } from 'payload'
|
|
import config from '@payload-config'
|
|
|
|
export async function createPost(data) {
|
|
const payload = await getPayload({ config })
|
|
try {
|
|
return await payload.create({ collection: 'posts', data })
|
|
} catch (error) {
|
|
throw new Error(`Error creating post: ${error.message}`)
|
|
}
|
|
}
|
|
```
|
|
|
|
```ts
|
|
// components/PostForm.tsx
|
|
'use client'
|
|
import { createPost } from '../server/actions'
|
|
|
|
// call like any async function from onClick/onSubmit
|
|
const newPost = await createPost({ title: 'Hello' })
|
|
```
|
|
|
|
## Common Operations
|
|
|
|
### Create Document
|
|
|
|
```ts
|
|
'use server'
|
|
export async function createPost(data) {
|
|
const payload = await getPayload({ config })
|
|
return await payload.create({ collection: 'posts', data })
|
|
}
|
|
```
|
|
|
|
### Update Document
|
|
|
|
```ts
|
|
'use server'
|
|
export async function updatePost(id, data) {
|
|
const payload = await getPayload({ config })
|
|
return await payload.update({ collection: 'posts', id, data })
|
|
}
|
|
```
|
|
|
|
### Authenticate User (check session)
|
|
|
|
```ts
|
|
'use server'
|
|
import { headers as getHeaders } from 'next/headers'
|
|
|
|
export const authenticateUser = async () => {
|
|
const payload = await getPayload({ config })
|
|
const headers = await getHeaders()
|
|
const { user } = await payload.auth({ headers })
|
|
return user ? { hello: user.email } : { hello: 'Not authenticated' }
|
|
}
|
|
```
|
|
|
|
### File Upload
|
|
|
|
Pass the file as a second argument and merge it into the data:
|
|
|
|
```ts
|
|
'use server'
|
|
export async function createPostWithUpload(data, upload) {
|
|
const payload = await getPayload({ config })
|
|
return await payload.create({
|
|
collection: 'posts',
|
|
data: { ...data, media: upload },
|
|
})
|
|
}
|
|
```
|
|
|
|
## Built-in Auth Server Functions
|
|
|
|
`@payloadcms/next/auth` exports ready-made server functions for auth operations that manage cookies automatically — no need to handle tokens manually.
|
|
|
|
| Function | Import | Purpose |
|
|
|----------|--------|---------|
|
|
| `login` | `@payloadcms/next/auth` | Verify credentials, set auth cookie |
|
|
| `logout` | `@payloadcms/next/auth` | Clear auth cookie + sessions |
|
|
| `refresh` | `@payloadcms/next/auth` | Refresh token + session |
|
|
|
|
**Pattern** — wrap each in a thin `'use server'` helper (config can't be imported in client components):
|
|
|
|
```ts
|
|
'use server'
|
|
import { login } from '@payloadcms/next/auth'
|
|
import config from '@payload-config'
|
|
|
|
export async function loginAction({ email, password }: { email: string; password: string }) {
|
|
return await login({ collection: 'users', config, email, password })
|
|
}
|
|
```
|
|
|
|
```ts
|
|
'use server'
|
|
import { logout } from '@payloadcms/next/auth'
|
|
import config from '@payload-config'
|
|
|
|
export async function logoutAction() {
|
|
return await logout({ allSessions: true, config })
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
- Always wrap Local API calls in `try/catch`
|
|
- Return structured error objects — don't expose raw errors to the frontend
|
|
- Log server-side for debugging
|
|
|
|
```ts
|
|
export async function createPost(data) {
|
|
try {
|
|
const payload = await getPayload({ config })
|
|
return await payload.create({ collection: 'posts', data })
|
|
} catch (error) {
|
|
console.error('Error creating post:', error)
|
|
return { error: 'Failed to create post' }
|
|
}
|
|
}
|
|
```
|
|
|
|
## Security
|
|
|
|
- **Access control**: Check `user.role` before sensitive operations
|
|
- **Avoid leaking data**: Never return passwords, tokens, or sensitive fields
|
|
- Use Payload's `UnauthorizedError` for clean rejection:
|
|
|
|
```ts
|
|
import { UnauthorizedError } from 'payload'
|
|
|
|
export async function deletePost(postId, user) {
|
|
if (!user || user.role !== 'admin') throw new UnauthorizedError()
|
|
const payload = await getPayload({ config })
|
|
return await payload.delete({ collection: 'posts', id: postId })
|
|
}
|
|
```
|
|
|
|
## Key Takeaways
|
|
|
|
- `'use server'` at the top of the file (or function) marks it as a server function
|
|
- `getPayload({ config })` is the entry point — import `config` from `@payload-config`
|
|
- All [[wiki/payloadcms/local-api|Local API]] operations (`create`, `update`, `delete`, `find`, `auth`) work inside server functions
|
|
- Built-in auth helpers (`login`/`logout`/`refresh`) from `@payloadcms/next/auth` handle cookie complexity
|
|
- Server functions replace the need for custom [[wiki/payloadcms/rest-api|REST API]] endpoints for internal operations
|
|
- Always return the result — don't just run the operation and discard
|
|
- Use `overrideAccess: false` + pass `user` if you want [[wiki/payloadcms/local-api-access-control|access control]] enforced
|
|
- For file uploads, pass the `File` object as a separate argument and merge into data
|
|
|
|
## Related
|
|
|
|
- [[wiki/payloadcms/local-api|Local API — Overview]]
|
|
- [[wiki/payloadcms/local-api-access-control|Local API — Access Control]]
|
|
- [[wiki/payloadcms/local-api-outside-nextjs|Local API — Outside Next.js]]
|
|
- [[wiki/payloadcms/authentication-operations|Authentication — Operations]]
|
|
- [[wiki/payloadcms/rest-api|REST API]]
|