@sugardarius/anzen

Get started

Get started with Anzen quickly in your app.

Server Action

Learn how to get started with createSafeServerAction to create a safe server action with Anzen. It's a factory that helps you create a server action with input validation, authorization, and error handling.

Install the package

Install anzen and a validation library.

npm i @sugardarius/anzen decoders

Create the server action

Create a server action using the createSafeServerAction factory.

actions/create-thread.ts
'use server'

import { object, string } from 'decoders'
import { createSafeServerAction } from '@sugardarius/anzen'

export const createThread = createSafeServerAction(
  {
    id: 'create-thread-action',
  },
  async ({ id }) => {
    return { id }
  }
)

Validate the input

Validate your input.

actions/create-thread.ts
import { object, string, datelike } from 'decoders'
import { createSafeServerAction } from '@sugardarius/anzen'

export const createThread = createSafeServerAction(
  {
    id: 'create-thread-action',
    input: object({
      title: string,
    }),
  },
  async ({ id, input }) => {
    //         ^^^^^ Input is inferred from the input validation
    return { id, title: input.title }
  }
)

Authorize the action

Authorize the action (if required).

actions/create-thread.ts
import { object, string, datelike } from 'decoders'
import { createSafeServerAction } from '@sugardarius/anzen'

import { auth } from '~/lib/auth'

export const createThread = createSafeServerAction(
  {
    id: 'create-thread-action',
    input: object({
      title: string,
    }),
    authorize: async () => {
      const session = await auth()
      if (!session.user) {
        throw new Error('user is not authenticated')
      }
      return { user: session.user }
    },
  },
  async ({ id, input, auth }) => {
    //                ^^^^ Auth context is inferred from the authorize function
    return { id, title: input.title, username: auth.user.name }
  }
)

Call the action

Call the action from a Client Component.

app/page.tsx
import { createThread } from '~/actions/create-thread'

export default function Form() {
  return (
    <form
      action={async (formData) => {
        const result = await createThread(formData)
        console.log(result)
      }}
    >
      <input id='title' name='title' />
      <button type='submit'>Create Thread</button>
    </form>
  )
}

Route handler

Learn how to get started with createSafeRouteHandler to create a safe route handler with Anzen. It's a factory that helps you create a route handler with input validation, authorization, and error handling.

Install the package

Install anzen and a validation library.

npm i @sugardarius/anzen decoders

Create the route handler

Add a Route Handler using the createSafeRouteHandler factory.

app/api/thread/route.ts
import { createSafeRouteHandler } from '@sugardarius/anzen'

export const POST = createSafeRouteHandler(
  {
    id: 'thread-route',
  },
  async ({ id }) => {
    return Response.json({ id })
  }
)

Validate the request body

Validate your different inputs like the request body. For POST, PUT, or PATCH, use body with a Standard Schema (JSON). You can also validate dynamic segments, searchParams, or formData depending on the request.

app/api/thread/route.ts
import { object, string } from 'decoders'
import { createSafeRouteHandler } from '@sugardarius/anzen'

export const POST = createSafeRouteHandler(
  {
    id: 'thread-route',
    body: object({
      title: string,
    }),
  },
  async ({ id, body }) => {
    //         ^^^^ Body is inferred from the body validation
    return Response.json({ id, title: body.title })
  }
)

Authorize the route

Authorize the request (if required).

app/api/thread/route.ts
import { object, string } from 'decoders'
import { createSafeRouteHandler } from '@sugardarius/anzen'

import { auth } from '~/lib/auth'

export const POST = createSafeRouteHandler(
  {
    id: 'thread-route',
    body: object({
      title: string,
    }),
    authorize: async () => {
      const session = await auth()
      if (!session.user) {
        return new Response(null, { status: 401 })
      }
      return { user: session.user }
    },
  },
  async ({ id, body, auth }) => {
    //               ^^^^ Auth context is inferred from the authorize function
    return Response.json({ id, title: body.title, username: auth.user.name })
  }
)

Page Server Component

Learn how to get started with createSafePageServerComponent to wrap a Next.js App Router page.tsx with Anzen. It validates dynamic segments and searchParams, runs authorization, and handles errors consistently.

Install the package

Install anzen and a validation library.

npm i @sugardarius/anzen decoders

Create a page using the createSafePageServerComponent factory.

app/thread/[threadId]/page.tsx
import { createSafePageServerComponent } from '@sugardarius/anzen/server-components'

export default createSafePageServerComponent(
  {
    id: 'thread-page',
  },
  async ({ id }) => {
    return <div>Page {id}</div>
  }
)

Validate the URL data

Validate URL data with segments and/or searchParams (Standard Schema).

app/thread/[threadId]/page.tsx
import { string, optional } from 'decoders'
import { createSafePageServerComponent } from '@sugardarius/anzen/server-components'

export default createSafePageServerComponent(
  {
    id: 'thread-page',
    segments: {
      threadId: string,
    },
    searchParams: {
      q: optional(string),
    },
  },
  async ({ id, segments, searchParams }) => {
    //         ^^^^^^^^ ^^^^^^^^^^^^^ Inferred from validation
    return (
      <div>
        {segments.threadId} — query: {searchParams.q ?? '—'}
      </div>
    )
  }
)

Authorize the page

Authorize the page (if required). Use next/navigation helpers such as unauthorized or notFound when access should stop; they work with Anzen’s error handling.

app/thread/[threadId]/page.tsx
import { unauthorized } from 'next/navigation'
import { string, optional } from 'decoders'
import { createSafePageServerComponent } from '@sugardarius/anzen/server-components'

import { auth } from '~/lib/auth'

export default createSafePageServerComponent(
  {
    id: 'thread-page',
    segments: {
      threadId: string,
    },
    searchParams: {
      q: optional(string),
    },
    authorize: async () => {
      const session = await auth()
      if (!session.user) {
        unauthorized()
      }
      return { user: session.user }
    },
  },
  async ({ id, segments, searchParams, auth }) => {
    //                                 ^^^^ Inferred from authorize
    return (
      <div>
        {auth.user.name}: {segments.threadId} ({searchParams.q ?? '—'})
      </div>
    )
  }
)

Layout Server Component

Learn how to get started with createSafeLayoutServerComponent to wrap a Next.js App Router layout.tsx with Anzen. It supports the same segments validation and authorization as pages, and receives children (and optional parallel-route slots via experimental_slots when you need them).

Install the package

Install anzen and a validation library.

npm i @sugardarius/anzen decoders

Create the layout

Create a layout using the createSafeLayoutServerComponent factory.

app/thread/[threadId]/layout.tsx
import { createSafeLayoutServerComponent } from '@sugardarius/anzen/server-components'

export default createSafeLayoutServerComponent(
  {
    id: 'thread-layout',
  },
  async ({ children }) => {
    return <div className='mx-auto max-w-lg py-8'>{children}</div>
  }
)

Validate dynamic segments for that route.

app/thread/[threadId]/layout.tsx
import { string } from 'decoders'
import { createSafeLayoutServerComponent } from '@sugardarius/anzen/server-components'

export default createSafeLayoutServerComponent(
  {
    id: 'thread-layout',
    segments: {
      threadId: string,
    },
  },
  async ({ id, segments, children }) => {
    //         ^^^^^^^^ Inferred from segments validation
    return (
      <div>
        <header>Thread {segments.threadId}</header>
        {children}
      </div>
    )
  }
)

Authorize the layout

Authorize the layout (if required).

app/thread/[threadId]/layout.tsx
import { unauthorized } from 'next/navigation'
import { string } from 'decoders'
import { createSafeLayoutServerComponent } from '@sugardarius/anzen/server-components'

import { auth } from '~/lib/auth'

export default createSafeLayoutServerComponent(
  {
    id: 'thread-layout',
    segments: {
      threadId: string,
    },
    authorize: async () => {
      const session = await auth()
      if (!session.user) {
        unauthorized()
      }
      return { user: session.user }
    },
  },
  async ({ id, segments, auth, children }) => {
    //                   ^^^^ Inferred from authorize
    return (
      <div>
        <header>
          {auth.user.name}{segments.threadId}
        </header>
        {children}
      </div>
    )
  }
)

Last updated on

On this page