--- 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]]