Enabling Request‑Level Schema Validation in Fastify with TypeBox
Enable Fastify to automatically validate incoming JSON against a TypeBox schema, returning clear 400 errors on failure. Follow this step‑by‑step guide to register the plugin, define schemas, attach them to routes, and verify the outcome with tests.
26 Feb 2026, 00:47 UTC

Desired Outcome
Automatically validate every incoming JSON request against a TypeBox schema, returning a clear 400 Bad Request with detailed error messages when the payload does not conform. The handler receives a strongly‑typed request.body that is guaranteed to match the schema.
Prerequisites
- Node.js ≥ 18 (LTS) installed and in your
PATH. - Fastify ≥ 4.0.0 added to your project:
npm i fastify. - TypeBox and the Fastify TypeBox plugin:
npm i @sinclair/typebox @fastify/type-provider-typebox. - A basic Fastify server skeleton already running (see Fastify docs for reference).
Focused Procedure
- Register the TypeBox plugin
In your server bootstrap file, import the plugin and register it with the Fastify instance. This augments the
fastify.routemethod to accept TypeBox schemas and provides type inference for handler parameters.import Fastify from 'fastify'; import typeProvider from '@fastify/type-provider-typebox'; const fastify = Fastify(); fastify.register(typeProvider); - Define a TypeBox schema
Use
@sinclair/typeboxto describe the expected shape of the request body. The schema must be exported so it can be reused in tests or documentation.import { Type } from '@sinclair/typebox'; export const CreateUserSchema = Type.Object({ username: Type.String({ minLength: 3 }), email: Type.String({ format: 'email' }), age: Type.Optional(Type.Integer({ minimum: 0 })), }, { additionalProperties: false, }); - Attach the schema to a route
When defining a route, pass the schema via the
schemaoption. Thebodyproperty must reference the TypeBox schema defined earlier. Fastify will automatically validate the incoming JSON against it.import { CreateUserSchema } from './schemas.js'; fastify.post('/users', { schema: { body: CreateUserSchema, response: { 201: Type.Object({ id: Type.String(), ...CreateUserSchema }) } } }, async (request, reply) => { // request.body is now typed as CreateUserSchema. const user = await db.createUser(request.body); reply.code(201).send(user); }); - Run the server
Start Fastify normally:
node server.js(or usenodemonfor dev). The plugin registration occurs before any routes are added, ensuring validation is active.
Expected Checks
- Valid payload test
Send a POST to
/userswith JSON matching the schema:{ "username": "alice", "email": "[contact removed]" }Assert the response status is
201and the body contains the created user with anidfield. - Invalid payload test
Send a malformed request, e.g., missing
emailor providing a string forage:{ "username": "bob", "age": "twenty" }Assert the response status is
400and the payload includes avalidationarray detailing the offending fields. - Server stability
Confirm that no unhandled exceptions occur by inspecting
stderrlogs. The default Fastify error handler should log the validation details without exposing stack traces to the client.
Recovery Options
Fastify’s default error handling already returns a structured JSON object for validation failures. If you prefer a more user‑friendly message, register a custom error handler that formats the validation array.
fastify.setErrorHandler((error, request, reply) => {
if (error.validation) {
const messages = error.validation.map(v => `${v.instancePath} ${v.message}`).join('; ');
reply.code(400).send({ error: 'Invalid request', details: messages });
} else {
reply.send(error);
}
});
Alternatively, fall back to the generic 400 response by simply not customizing the error handler; Fastify will still provide the validation array in the body.
Limitations & Practical Checks
- Validation runs only if the route’s
schema.bodyis defined. Omitting it will skip validation and allow malformed data to reach the handler. - Ensure the plugin version matches the Fastify major version; mismatches can cause runtime errors or loss of type inference.
- When using TypeScript, the inferred
request.bodytype is accurate only if the schema is exported and referenced correctly. Verify type safety by compiling your project withtsc --noEmit.
Conclusion
By registering @fastify/type-provider-typebox, defining clear TypeBox schemas, and attaching them to routes, you gain automatic, reliable request validation with minimal boilerplate. The handler receives strongly‑typed data, and clients receive precise error messages when requests fail validation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.