Zod Schemas
Schema validations are an optional configuration in createEndpoint and createEndpointConfig. They allow you to apply Zod-based validations to params, searchParams, and body, providing full type-safety for your endpoint's request context.
import { zod } 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()
})
}
})What you'll learn
Good to know
- For detailed information about schema validations inside createEndpoint, see the API Reference.
- Zod is not include in
@aura-stack/router, so, if you need to add validatons, you need to install zod. - Zod schemas are optional but highly recommended to add runtime validation to:
paramsviaschemas.paramssearchParamsviaschemas.searchParamsbodyviaschemas.body
Type Inference
When adding Zod schemas, the endpoint automatically extracts and infers the types from them. These inferred types are applied to:
- The
Route Handler Request Context. - The
Middleware Context.
This ensures fully typed access to:
ctx.paramsctx.searchParamsctx.body
The types can be accessed from the main entry points
/or/types. Available types:
RequestContext.ContextParams.ContextSearchParams.ContextBody.
Installation
To enable schema validation, install Zod:
npm install zodSchema Validations
Schema validations can be passed directly to an endpoint or using createEndpointConfig
Directly
Pass the schema as the fourth argument of createEndpoint.
import { zod } 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 DELETE successfully" })
}, {
schemas: {
params: z.object({
userId: z.coerce.number()
})
}
})Using createEndpointConfig
Use createEndpointConfig and pass the config as the fourth argument.
import { zod } 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 DELETE 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
Validate both searchParams and body 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
)