next-safe-action Client & Action Definition
Quick Start
// src/lib/safe-action.ts
import
{
createSafeActionClient
}
from
"next-safe-action"
;
export
const
actionClient
=
createSafeActionClient
(
)
;
// src/app/actions.ts
"use server"
;
import
{
z
}
from
"zod"
;
import
{
actionClient
}
from
"@/lib/safe-action"
;
export
const
greetUser
=
actionClient
.
inputSchema
(
z
.
object
(
{
name
:
z
.
string
(
)
.
min
(
1
)
}
)
)
.
action
(
async
(
{
parsedInput
:
{
name
}
}
)
=>
{
return
{
greeting
:
Hello,
${
name
}
!
}
;
}
)
;
Chainable API Order
createSafeActionClient(opts?)
.use(middleware) // repeatable, adds middleware to chain
.metadata(data) // required if defineMetadataSchema is set
.inputSchema(schema, utils?) // Standard Schema or async factory function
.bindArgsSchemas([...]) // schemas for .bind() arguments (order with inputSchema is flexible)
.outputSchema(schema) // validates action return value
.action(serverCodeFn, utils?) // creates SafeActionFn
.stateAction(serverCodeFn, utils?) // creates SafeStateActionFn (for useActionState)
Each method returns a new client instance — the chain is immutable.
Entry Points
Entry point
Environment
Exports
next-safe-action
Server
createSafeActionClient
,
createMiddleware
,
returnValidationErrors
,
flattenValidationErrors
,
formatValidationErrors
,
DEFAULT_SERVER_ERROR_MESSAGE
, error classes, all core types
next-safe-action/hooks
Client
useAction
,
useOptimisticAction
, hook types
next-safe-action/stateful-hooks
Client
useStateAction
(deprecated — use React's
useActionState
directly)
Supporting Docs
Client setup & configuration
Input & output validation with Standard Schema
Server error handling
Anti-Patterns
// BAD: Missing "use server" directive — action won't work
import
{
actionClient
}
from
"@/lib/safe-action"
;
export
const
myAction
=
actionClient
.
action
(
async
(
)
=>
{
}
)
;
// GOOD: Always include "use server" in action files
"use server"
;
import
{
actionClient
}
from
"@/lib/safe-action"
;
export
const
myAction
=
actionClient
.
action
(
async
(
)
=>
{
}
)
;
// BAD: Calling .action() without .metadata() when metadataSchema is defined
const
client
=
createSafeActionClient
(
{
defineMetadataSchema
:
(
)
=>
z
.
object
(
{
actionName
:
z
.
string
(
)
}
)
,
}
)
;
client
.
action
(
async
(
)
=>
{
}
)
;
// TypeScript error!
// GOOD: Always provide metadata before .action() when schema is defined
client
.
metadata
(
{
actionName
:
"myAction"
}
)
.
action
(
async
(
)
=>
{
}
)
;
// BAD: Returning an error instead of throwing
export
const
myAction
=
actionClient
.
inputSchema
(
z
.
object
(
{
email
:
z
.
string
(
)
.
email
(
)
}
)
)
.
action
(
async
(
{
parsedInput
}
)
=>
{
const
exists
=
await
db
.
user
.
findByEmail
(
parsedInput
.
email
)
;
if
(
exists
)
{
return
{
error
:
"Email taken"
}
;
// Not type-safe, not standardized
}
}
)
;
// GOOD: Use returnValidationErrors for field-level errors
import
{
returnValidationErrors
}
from
"next-safe-action"
;
export
const
myAction
=
actionClient
.
inputSchema
(
z
.
object
(
{
email
:
z
.
string
(
)
.
email
(
)
}
)
)
.
action
(
async
(
{
parsedInput
}
)
=>
{
const
exists
=
await
db
.
user
.
findByEmail
(
parsedInput
.
email
)
;
if
(
exists
)
{
returnValidationErrors
(
z
.
object
(
{
email
:
z
.
string
(
)
.
email
(
)
}
)
,
{
email
:
{
_errors
:
[
"Email is already in use"
]
}
,
}
)
;
}
return
{
success
:
true
}
;
}
)
;
Server Code Function Parameters
The function passed to
.action()
receives a single object:
.
action
(
async
(
{
parsedInput
,
// validated input (typed from inputSchema)
clientInput
,
// raw client input (unknown)
bindArgsParsedInputs
,
// validated bind args tuple
bindArgsClientInputs
,
// raw bind args
ctx
,
// context from middleware chain
metadata
,
// metadata set via .metadata()
}
)
=>
{
// return data
}
)
;
For
.stateAction()
, a second argument is added:
.
stateAction
(
async
(
{
parsedInput
,
ctx
}
,
{
prevResult
}
)
=>
{
// prevResult is the previous SafeActionResult (structuredClone'd)
return
{
count
:
(
prevResult
.
data
?.
count
??
0
)
+
1
}
;
}
)
;
safe-action-client
安装
npx skills add https://github.com/next-safe-action/skills --skill safe-action-client