5.8 KiB
5.8 KiB
| title | aliases | tags | sources | created | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Local API — Server Functions |
|
|
|
2026-05-15 | 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, 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
// 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}`)
}
}
// 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
'use server'
export async function createPost(data) {
const payload = await getPayload({ config })
return await payload.create({ collection: 'posts', data })
}
Update Document
'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)
'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:
'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):
'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 })
}
'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
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.rolebefore sensitive operations - Avoid leaking data: Never return passwords, tokens, or sensitive fields
- Use Payload's
UnauthorizedErrorfor clean rejection:
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 functiongetPayload({ config })is the entry point — importconfigfrom@payload-config- All wiki/payloadcms/local-api operations (
create,update,delete,find,auth) work inside server functions - Built-in auth helpers (
login/logout/refresh) from@payloadcms/next/authhandle cookie complexity - Server functions replace the need for custom wiki/payloadcms/rest-api endpoints for internal operations
- Always return the result — don't just run the operation and discard
- Use
overrideAccess: false+ passuserif you want wiki/payloadcms/local-api-access-control enforced - For file uploads, pass the
Fileobject as a separate argument and merge into data