Safe page server component
API reference for the safe App Router page factory — validated segments and search params, authorization, and error handling.
createSafePageServerComponent wraps a default-exported page Server Component. It validates dynamic segments and search params with Standard Schema dictionaries, runs optional authorization, and exposes a typed ctx to your render function. Import from the @sugardarius/anzen/server-components entry.
Import
importfrom '@sugardarius/anzen/server-components'Signature
function createSafePageServerComponent<
AC = undefined,
TSegments = undefined,
TSearchParams = undefined,
>(
options: CreateSafePageServerComponentOptions<AC, TSegments, TSearchParams>,
page:
ctx: SafePageServerComponentContext<AC, TSegments, TSearchParams>
=> Promise<React.ReactElement>
):props:
params: Awaitable<any>
searchParams: Awaitable<any>
}) => Promise<React.ReactElement>The returned function is what you export default from page.tsx.
Options
When creating a safe page server component you can use a bunch of options for helping you achieve different tasks 👇🏻
id
id?: string
Used for logging in development or when the debug option is enabled. You can also use it to add extra logging or monitoring.
By default the id is set to [unknown:page:server:component].
export default createSafePageServerComponent
'races/[id]/page',
asyncid=>
returndiv>Page {id}</div>
onError
onError?: (err: unknown) => Awaitable<never>
Callback triggered when the pageserver component throws an unhandled error.
By default it rethrows the error as a service hatch to Next.js do its job and use error boundaries. The error is logged into the console.
Use it if you want to manage unexpected errors properly to log, trace or define behaviors like using notFound or redirect.
importfrom 'next/navigation'
importfrom '@sugardarius/anzen/server-components'
export default createSafePageServerComponent
onError: asyncerr: unknown): Promise<never> =>
ifinstanceof NotFoundError
notFound
throw
async=>
returndiv>Hello</div>
debug
debug?: boolean
Use this options to enable debug mode. It will add logs in the page server component to help you debug the request.
By default it's set to false for production builds.
In development builds, it will be true if NODE_ENV is not set to production.
importfrom '@sugardarius/anzen/server-components'
export default createSafePageServerComponenttrueasync=>
returndiv>Hello</div>
})You can configure page server component options' validation using a validation library dynamic route segments and URL query parameters 👇🏻
authorize
authorize?: PageAuthFunction<AC, TSegments, TSearchParams>
Function to use to authorize the page server component. By default it always authorize the server component.
Return never (throws an error, notFound, forbidden, unauthorized, or redirect) when the request to the server component is not authorized.
The authorize function receives validated attributes (segments, searchParams) when they are defined, allowing you to use validated data for authorization logic.
Parameters:
id: string- Server component IDsegments?:- Validated route dynamic segments (ifsegmentsoption is defined)searchParams?:- Validated search params (ifsearchParamsoption is defined)
Basic authorization
importfrom '@sugardarius/anzen/server-components'
importfrom '~/lib/auth'
importfrom 'next/navigation'
export default createSafePageServerComponent
authorize: async=>
const session = awaitgetSession
if!session) {
unauthorized
return
asyncauth=>
returndiv>Hello {auth.user.name}!</div>
Authorization with validated segments
importfrom 'zod'
importfrom '@sugardarius/anzen/server-components'
importfrom '~/lib/auth'
importfrom 'next/navigation'
export default createSafePageServerComponent
string
string
authorize: asyncsegments=>
// segments are already validated at this point
const session = awaitgetSession
if!session) {
throw new Error'Unauthorized'
// Check if user has access to this account
const hasAccess = await checkAccountAccess
if!hasAccess) {
notFound
return
asyncauth, segments=>
return
div>
div>
Authorization with validated search params
importfrom 'zod'
importfrom 'next/navigation'
importfrom '@sugardarius/anzen/server-components'
export default createSafePageServerComponent
string
authorize: asyncsearchParams=>
// searchParams are already validated at this point
const isValidCode = await validateCode
if!isValidCode) {
unauthorized
return
asyncauth, searchParams=>
returndiv>Code: {auth.code}</div>
Authorization with all validated props
importfrom 'zod'
importfrom '@sugardarius/anzen/server-components'
importfrom '~/lib/auth'
importfrom 'next/navigation'
export default createSafePageServerComponent
string
string
authorize: asyncsegments, searchParams=>
// All props are validated and available
const session = awaitgetSession
if!session) {
throw new Error'Unauthorized'
const hasPermission = await checkPermission
if!hasPermission) {
notFound
return
asyncauth, segments, searchParams=>
return
div>
div>
segments
segments?: TSegments
Dynamic route segments used for the page server component path. By design it will handle automatically if the segments are a Promise or not.
Please note the expected input is a StandardSchemaDictionary.
importfrom 'zod'
importfrom '@sugardarius/anzen/server-components'
export default createSafePageServerComponent
string
stringoptional
asyncsegments=>
return
div>
div>
onSegmentsValidationError
onSegmentsValidationError?: OnValidationError
Callback triggered when dynamic segments validations returned issues. By default it throws a ValidationError and issues are logged into the console.
importfrom 'zod'
importfrom '@sugardarius/anzen/server-components'
importfrom 'next/navigation'
export default createSafePageServerComponent
string
stringoptional
onSegmentsValidationError: asyncissues) =>
error'Invalid segments', issues)
notFound
asyncsegments=>
return
div>
div>
searchParams
searchParams?: TSearchParams
Search params used in the page.
Please note the expected input is a StandardSchemaDictionary.
importfrom 'decoders'
importfrom '@sugardarius/anzen/server-components'
export default createSafePageServerComponent
optional
asyncsearchParams=>
return
div>
div>
onSearchParamsValidationError
onSearchParamsValidationError?: OnValidationError
Callback triggered when search params validations returned issues. By default it throws a ValidationError and issues are logged into the console.
importfrom 'decoders'
importfrom '@sugardarius/anzen/server-components'
importfrom 'next/navigation'
export default createSafePageServerComponent
optional
onSearchParamsValidationError: asyncissues) =>
error'Invalid search params', issues)
redirect'/'
asyncsearchParams=>
return
div>
div>
Handler context
SafePageServerComponentContext is the context object that is passed to the handler
| Field | When |
|---|---|
id | Always. |
auth | When authorize returned a value. |
segments | When segments was configured. |
searchParams | When searchParams was configured. |
There is no children on the page context (use the layout factory for that).
Error handling
By design the factory will catch any error thrown in the page server component and rethrow it by default to let Next.js handle it with error boundaries.
You can customize the error handling if you want to fine tune error management.
importfrom '@sugardarius/anzen/server-components'
importfrom '~/lib/errors'
importfrom '~/lib/db'
importfrom 'next/navigation'
export default createSafePageServerComponent
onError: asyncerr: unknown): Promise<never> =>
// Take it as an example not as a real use case 😅
ifinstanceof NotFoundError
notFound
else ifinstanceof DbUnknownError
error'Database error', err)
throw
throw
async=>
constdata, err] = awaitfindUnique'liveblocks'
if
throw new DbUnknownError500
if=== null
throw new NotFoundError
returndiv>{data.name}</div>
Synchronous Validation
Validation must be synchronous. The Standard Schema contract discourages async validation. If a schema resolves validation asynchronously, behavior is undefined and may throw at runtime.
Fair use note
Please note that if you're not using any of the proposed options in createSafePageServerComponent it means you're surely don't need it.
// Calling 👇🏻
export default createSafePageServerComponentasync=>
returndiv>Hello</div>
})
// is equal to declare the page server component this way 👇🏻
export default async function Page() {
returndiv>Hello</div>
}See also
Last updated on