From Fastify
Fastify's JSON Schema validation vs NextRush's bring-your-own-schema approach, and what else differs.
Fastify and NextRush both prioritize a fast, explicit request pipeline, but they solve request validation very differently — Fastify bakes JSON Schema into route registration; NextRush ships a thin middleware that works with whatever schema library you already use.
Validation: the real difference
Validation model
| Property | Type | Description |
|---|---|---|
Schema format | JSON Schema [Fastify] vs Standard Schema [NextRush] | Fastify validates against a route-level JSON Schema option; NextRush accepts any library implementing the Standard Schema interface (Zod, Valibot, ArkType). |
Where it lives | route registration option [Fastify] vs a middleware [NextRush] | Fastify: { schema: { body, querystring } } passed to app.get/post. NextRush: validate(schema) composed like any other middleware. |
Serialization | fast-json-stringify (AOT) [Fastify] vs native JSON.stringify [NextRush] | Fastify compiles a response schema into a fast serializer ahead of time; NextRush uses ctx.json(), which calls JSON.stringify directly. Fastify's AOT approach can outperform on JSON-heavy routes. |
const UserSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
},
},
};
app.post('/users', { schema: UserSchema }, async (request, reply) => {
return { received: request.body };
});import { validate } from '@nextrush/validation';
import { z } from 'zod';
const User = z.object({
name: z.string().min(1),
email: z.string().email(),
});
router.post('/users', validate(User), (ctx) => {
ctx.json({ received: ctx.body }); // ctx.body is the validated, coerced value
});validate() (from @nextrush/validation) validates the request body by default, or a
{ body, query, params } map for multiple targets. On success it overwrites ctx.body with the
coerced value; ctx.query/ctx.params are validated but left as strings (see the
validation package's own docs
for why). Failures throw the framework's existing ValidationError from @nextrush/errors,
rendered as a 400 with aggregated issues — there's no separate error-shape convention to learn
per validator.
Bring your own schema library
NextRush doesn't ship a validator. Any library implementing
Standard Schema — Zod 3.24+, Valibot 1.0+, ArkType 2.0+ — works
identically with validate(). Switching libraries later doesn't change your routes.
Other differences
Request/response model
| Property | Type | Description |
|---|---|---|
request + reply | Single ctx | NextRush merges both into one context object, same as it does coming from Express. |
Lifecycle hooks (onRequest, onSend, ...) | Middleware onion model | Fastify's named hook points map onto NextRush's before/after middleware — see Middleware. |
fastify-plugin encapsulation | Middleware + registrars, no encapsulation scope | NextRush middleware and registrars are global to the app; there is no per-plugin scoping boundary. |
Runtime targets | Node.js only [Fastify] vs Node, Bun, Deno, Edge [NextRush] | NextRush ships separate adapters per runtime — see Runtime Compatibility. |
// Fastify hook
app.addHook('onRequest', async (request, reply) => {
/* runs before the handler */
});
// NextRush equivalent
app.use(async (ctx, next) => {
/* before */
await next();
/* after — no separate hook needed */
});Next steps
- Validation — the full
validate()API and error shape. - Middleware — the onion model behind NextRush's hooks equivalent.
- Runtime Compatibility — Node, Bun, Deno, and Edge adapters.