Typed endpoints
Define API endpoints with Zod schemas for TypeScript inference and OpenAPI generation.
The define() function lets you attach Zod schemas to your endpoints. You get full TypeScript inference for request bodies, query parameters, path parameters, and headers — plus automatic OpenAPI 3.1 spec generation at build time.
Basic usage
import { define } from '@robojs/server'
import { z } from 'zod'
export const POST = define(
{
summary: 'Create a user',
body: z.object({
name: z.string(),
email: z.string().email()
}),
response: {
201: z.object({ id: z.string() })
}
},
async (request) => {
const body = await request.json() // Typed as { name: string, email: string }
return { id: crypto.randomUUID() }
}
)import { define } from '@robojs/server'
import { z } from 'zod'
export const POST = define(
{
summary: 'Create a user',
body: z.object({
name: z.string(),
email: z.string().email()
}),
response: {
201: z.object({ id: z.string() })
}
},
async (request) => {
const body = await request.json() // Typed as { name: string, email: string }
return { id: crypto.randomUUID() }
}
)The body is NOT auto-parsed. Call await request.json() to get the typed value.
Schema options
Prop
Type
Supported status codes for response: 200, 201, 204, 400, 401, 403, 404, 409, 422, 500.
Full example
import { define } from '@robojs/server'
import { z } from 'zod'
export const GET = define(
{
summary: 'Get user by ID',
tags: ['users'],
params: z.object({
id: z.string().uuid()
}),
query: z.object({
include: z.string().optional()
}),
headers: z.object({
'x-api-key': z.string()
}),
response: {
200: z.object({
id: z.string(),
name: z.string(),
email: z.string()
}),
404: z.object({
error: z.string()
})
}
},
async (request) => {
const { id } = request.params // typed as { id: string }
const { include } = request.query // typed as { include?: string }
const apiKey = request.header('x-api-key') // typed as string
return { id, name: 'Alice', email: 'alice@example.com' }
}
)import { define } from '@robojs/server'
import { z } from 'zod'
export const GET = define(
{
summary: 'Get user by ID',
tags: ['users'],
params: z.object({
id: z.string().uuid()
}),
query: z.object({
include: z.string().optional()
}),
headers: z.object({
'x-api-key': z.string()
}),
response: {
200: z.object({
id: z.string(),
name: z.string(),
email: z.string()
}),
404: z.object({
error: z.string()
})
}
},
async (request) => {
const { id } = request.params // typed as { id: string }
const { include } = request.query // typed as { include?: string }
const apiKey = request.header('x-api-key') // typed
return { id, name: 'Alice', email: 'alice@example.com' }
}
)Plain exports still work
You don't need define() for every endpoint. Plain exports work as before:
import type { RoboRequest } from '@robojs/server'
export function GET(request: RoboRequest) {
return { status: 'ok' }
}
export function GET(request) {
return { status: 'ok' }
}These endpoints won't appear in the generated OpenAPI spec unless they use define().
OpenAPI generation
Run robo build to generate .robo/openapi.json from your typed endpoints. See OpenAPI for configuration.
