@truxie/zod
v0.1.4
Published
Zod validation pipes, param decorators, and serialization interceptor for truxie
Maintainers
Readme
@truxie/zod
Zod validation for truxie: typed parameter decorators, a response allow-list, and the JSON-Schema converter the documentation packages use.
npm install @truxie/zod zodValidate what comes in
const createUser = z.object({email: z.email(), name: z.string().min(1)})
const CreateUserDTO = ZodValidatedBody(createUser)
const listQuery = z.object({page: z.coerce.number().int().positive().default(1)})
const ListQuery = ZodValidatedQuery(listQuery)
@Controller('users')
class UserController {
@Post('/')
create(@CreateUserDTO() body: z.infer<typeof createUser>) {}
@Get('/')
list(@ListQuery() query: z.infer<typeof listQuery>) {}
}A failure throws ValidationError — a 400 whose errors are keyed by field path. ZodValidatedParams does the same for :params, which arrive as strings, so reach for z.coerce there.
For one value rather than a whole object, use the pipe form: @Query('page', new ZodParamPipe(z.coerce.number())).
Validate what goes out
const userResponse = z.object({id: z.string(), email: z.email()}) // no password
@Serialize(userResponse)
@Get('/:id')
getUser(@Param('id') id: string) {
return this.users.findById(id) // undeclared fields are stripped
}@Serialize needs its interceptor to run: register SerializeInterceptor globally (globalInterceptors: [SerializeInterceptor]) or per controller with @UseInterceptors(SerializeInterceptor). The schema is an allow-list, which is the point — a field nobody declared cannot leak because someone added it to a model.
Documentation, from the same schemas
zodToJsonSchema converts a schema for @truxie/openapi and @truxie/mcp, so the contract a renderer publishes is the one the handler enforces:
generateOpenApiDocument(app, {toJsonSchema: zodToJsonSchema})Because the decorators record their schema on the route, this needs nothing else written down.
Zod 3 and 4. MIT.
