Zod Schemas
Schema validations are an optional configuration in createEndpoint and createEndpointConfig. They enable Zod-based validations for params, searchParams, and body, ensuring full type safety for the endpoint's request context.
import { z } from "zod"
import { createEndpoint } from "@aura-stack/router"
export const endpoint = createEndpoint(
"POST",
"/auth/signIn",
async (ctx) => {
const { username, password } = ctx.body
return Response.json({ message: "Successful Login" })
},
{
schemas: {
body: z.object({
username: z.string(),
password: z.string(),
}),
},
}
)Overview
Key Concepts
- Detailed information about schema validations within
createEndpointcan be found in the API Reference. - Zod is not included in
@aura-stack/router. To use validations, it must be installed separately. - Zod schemas are optional but highly recommended for adding runtime validation to:
paramsviaschemas.paramssearchParamsviaschemas.searchParamsbodyviaschemas.body
Type Inference
When Zod schemas are added, the endpoint automatically extracts and infers types from them. These inferred types apply to:
- The
Route Handler Request Context. - The
Middleware Context.
This ensures fully typed access to ctx.params, ctx.searchParams, and ctx.body.
Installation
To enable schema validation, install zod.
npm install @aura-stack/router zodSchemas Validation
Schema validations can be passed directly to an endpoint or defined using the createEndpointConfig function.
Directly
The schema can be passed as the fourth argument of createEndpoint.
import { z } from "zod"
import { createEndpoint } from "@aura-stack/router"
export const deleteUser = createEndpoint(
"DELETE",
"/users/:userId",
async (ctx) => {
const { userId } = ctx.params
return Response.json({ message: "User DELETED successfully" })
},
{
schemas: {
params: z.object({
userId: z.coerce.number(),
}),
},
}
)Using createEndpointConfig
The createEndpointConfig function can also be used, passing the configuration object as the fourth argument to createEndpoint.
When the endpoint requires a dynamic parameter, the route path should be provided as the first argument.
import { z } from "zod"
import { createEndpoint, createEndpointConfig } from "@aura-stack/router"
const config = createEndpointConfig("/users/:userId", {
schemas: {
params: z.object({
userId: z.coerce.number(),
}),
},
})
export const deleteUser = createEndpoint(
"DELETE",
"/users/:userId",
async (ctx) => {
const { userId } = ctx.params
return Response.json({ message: "User DELETED successfully" })
},
config
)Params
Basic Validation
import { z } from "zod"
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const dynamicsConfig = createEndpointConfig("/users/:userId/books/:bookId", {
schemas: {
params: z.object({
userId: z.regex(/^[0-9]+$/),
bookId: z.uuid(),
}),
},
})
const getBookById = createEndpoint(
"GET",
"/users/:userId/books/:bookId",
async (ctx) => {
const { userId, bookId } = ctx.params
return Response.json({ bookId })
},
dynamicsConfig
)Search Params Validation
Basic Validation
import { z } from "zod"
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const searchConfig = createEndpointConfig({
schemas: {
searchParams: z.object({
q: z.string().min(1),
page: z.coerce.number().int().positive().default(1),
}),
},
})
const searchUsers = createEndpoint(
"GET",
"/users/search",
async (ctx) => {
const { q, page } = ctx.searchParams
return Response.json({ query: q, page, results: [] })
},
searchConfig
)Advanced Validation
import { z } from "zod"
import { createEndpointConfig } from "@aura-stack/router"
const advancedSearchConfig = createEndpointConfig({
schemas: {
searchParams: z.object({
q: z.string().min(3).max(100),
category: z.enum(["tech", "science", "arts"]).optional(),
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
sortBy: z.enum(["date", "relevance", "popularity"]).default("relevance"),
sortOrder: z.enum(["asc", "desc"]).default("desc"),
}),
},
})Request Body Validation
Basic Validation
import { z } from "zod"
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const createUserConfig = createEndpointConfig({
schemas: {
body: z.object({
name: z.string().min(1),
email: z.string().email(),
}),
},
})
const createUser = createEndpoint(
"POST",
"/users",
async (ctx) => {
const { name, email } = ctx.body
return Response.json({ id: "new-id", name, email }, { status: 201 })
},
createUserConfig
)Advanced Validation
import { z } from "zod"
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const updateProfileConfig = createEndpointConfig({
schemas: {
body: z.object({
profile: z.object({
firstName: z.string().min(1),
lastName: z.string().min(1),
bio: z.string().max(500).optional(),
}),
settings: z.object({
theme: z.enum(["light", "dark"]).default("light"),
notifications: z.boolean().default(true),
language: z.string().length(2).default("en"),
}),
tags: z.array(z.string()).min(1).max(10),
}),
},
})
const updateProfile = createEndpoint(
"PATCH",
"/profile",
async (ctx) => {
const { profile, settings, tags } = ctx.body
return Response.json({ updated: true })
},
updateProfileConfig
)Combined Validation
Both searchParams and body can be validated in the same endpoint.
import { z } from "zod"
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const createPostConfig = createEndpointConfig({
schemas: {
body: z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
tags: z.array(z.string()).optional(),
}),
searchParams: z.object({
publish: z.enum(["true", "false"]).default("false"),
notify: z.enum(["true", "false"]).default("true"),
}),
},
})
const createPost = createEndpoint(
"POST",
"/posts",
async (ctx) => {
const { title, content, tags } = ctx.body
const { publish, notify } = ctx.searchParams
return Response.json(
{
id: "new-post",
title,
content,
tags,
published: publish === "true",
notified: notify === "true",
},
{ status: 201 }
)
},
createPostConfig
)