Building Schemas Programmatically with TypeBox
Instead of using JSON Schemas, you have the option to define your schemas using TypeBox.
Example
import { createRouter, Response, Type } from 'fets'
const router = createRouter().route({
path: '/user/:id',
method: 'POST',
schemas: {
request: {
headers: Type.Object({ authorization: Type.String() }),
params: Type.Object({ id: Type.String({ format: 'uuid' }) })
}
// Add schemas.responses if you need them in the generated OpenAPI document
},
// TypeScript infers the handler return type for the router client,
// but that does not populate OpenAPI `responses` at runtime.
handler: request => {
if (request.params.id !== EXPECTED_UUID) {
return Response.json({ message: 'User not found' }, { status: 404 })
}
return Response.json({ id: request.params.id, name: 'John Doe' })
}
})TypeScript can infer response types from the handler return value for the typed router client, but
feTS cannot reflect those types into the generated OpenAPI document. Define schemas.responses
explicitly if you need OpenAPI response schemas.
Why not Zod?
feTS is built around JSON Schema. Any library that produces JSON Schema can be used for typing and OpenAPI documentation.
Runtime request validation, however, currently requires
TypeBox schemas (Type.*). Plain JSON Schema objects are
used for types and OpenAPI generation only — they are not validated at runtime. We intentionally
avoid embedding a heavier general-purpose validator (such as AJV) to keep the bundle small.
Libraries like Zod can still be used if you convert their schemas to JSON Schema for typing/OpenAPI, but feTS does not ship a multi-validator adapter layer. Prefer TypeBox when you need both types and runtime validation.
In addition, you can see how fast TypeBox is; Type Benchmarks