Files
payload/docs/admin/server-functions.mdx
2024-10-07 09:44:15 -04:00

181 lines
6.2 KiB
Plaintext

---
title: Server Functions
label: Server Functions
order: 100
desc: Execute custom server-side logic from client-side code using Server Functions in Payload.
keywords: server functions, server-side functions, server-side logic, server-side code, server-side, functions, Payload, headless, Content Management System, cms, javascript, react, node, nextjs
---
The Payload [Admin Panel] supports [React Server Functions](https://react.dev/reference/rsc/server-actions) directly through the Payload Config. Server Functions are functions that are defined on the server, which may use server-only modules, but are called by the client. This is a way to execute server-side logic through a client-side action.
Server Functions are a good alternative to traditional [REST API Endpoints](../rest-api#custom-endpoints), but with a few key differences. While they behave similarly, Server Functions:
1. are simpler to define, not requiring a specified route or method
2. are easier to consume, not requiring the Fetch API
3. are able to return React and/or JSX
Server Functions do not necessarily need to be defined in the Payload Config. It is possible to write your own Server Functions and thread them to your client accordingly. You will, however, be responsible for authenticating those requests yourself. All Server Functions defined through the Payload Config will automatically receive a `req` argument, containing the `user`, `payload`, and more.
<Banner type="info">
<strong>Note:</strong>
Server Functions defined through the Payload Config are only available within the Admin Panel, not your public-facing application. For public-facing server-side logic, you can write your own Server Functions directly into your application.
</Banner>
## Admin Options
To add a new Server Function, use the `admin.serverFunctions` property in your Payload config:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
admin: {
// highlight-start
serverFunctions: [
{
name: 'my-server-action',
fn: ({ req, value }) => `The value is: "${value}"`
}
]
// highlight-end
}
})
```
The following options are available:
| Option | Type | Description |
| --- | --- | --- |
| **name** | `string` | The name of the Server Function. |
| **fn** | `Function` | The function to execute. [More details](#function-arguments) |
### Function Arguments
The function receives an object with the following properties:
| Property | Type | Description |
| --- | --- | --- |
| **req** | `PayloadRequest` | The request object, containing `payload`, `user`, and `config` properties. |
| **importMap** | `Record<string, any>` | The import map object. |
## Client-side Usage
To execute a Server Function from the client, use the `useServerFunctions` hook, passing the `name` of your Server Function:
```tsx
'use client'
import React, { useCallback } from 'react'
import { useServerFunctions } from '@payloadcms/ui'
const MyComponent = () => {
const { serverFunction } = useServerFunctions()
const [result, setResult] = React.useState<string | null>(null)
const callServerAction = useCallback(async () => {
const result = await serverFunction({
name: 'my-server-action',
args: {
value: 'Hello, world!'
}
}) as string
setResult(result)
}, [serverFunction])
return (
<button onClick={callServerAction} type="button">
{result || 'Call Server Action'}
</button>
)
}
```
## How it works
In order for Payload to support Sever Functions through the Payload Config, a single handler is placed at the root of the application:
```ts
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { ServerFunctionClient } from 'payload'
import config from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts'
import { handleServerFunctions } from '@payloadcms/next/utilities' // highlight-line
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
// highlight-start
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
// highlight-end
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
serverFunction={serverFunction} // highlight-line
>
{children}
</RootLayout>
)
export default Layout
```
The Server Function Handler is a necessary pattern for Server Functions to have access to the Payload Config, as well as any other server-only modules that may be required. This is because all server-only modules _must_ be imported in the closure as the Server Function, wherever the `use server` directive is used. [More details](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#closures-and-encryption).
## Troubleshooting
#### "Unknown Server Function: 'my-server-function'"
Ensure the `name` property of your Server Function matches the name you are passing through the `serverFunction` args.
#### "Error: Client Functions cannot be passed directly to Server Functions. Only Functions passed from the Server can be passed back again"
Non-serializable values cannot cross the server / client boundary. Ensure that the args your sending through your Server Function are serializable, i.e. not containing any functions, classes, etc.
#### "Body exceeded _n_ limit"
By default, Next.js places a 1mb limit on the body size of incoming requests. However, this can be increased by setting the `bodySizeLimit` option in your `next.config.ts` file. [More details](https://nextjs.org/docs/app/api-reference/next-config-js/serverActions#bodysizelimit).
```ts
{
// ...
experimental: {
serverActions: {
bodySizeLimit: '2mb',
}
}
}
```
## TypeScript
You can import the Payload `ServerFunction` type as well as other common types from the `payload` package. [More details](../typescript/overview).
```ts
import type {
ServerFunction,
ServerFunctionArgs,
ServerFunctionClient,
ServerFunctionClientArgs,
ServerFunctionConfig,
DefaultServerFunctionArgs,
} from 'payload'
```