Validation¶
Input validation in dichit-backend happens in two complementary ways: Fastify JSON schema (server-config-level) and Zod (shared schemas).
Approach¶
| Layer | Where | Role |
|---|---|---|
| Fastify schema | route schema option | body, querystring, params, headers validation + serialization |
| Zod | src/shared/zod-schemas/ | reusable/typed validation used inside services and WS schemas |
| Swagger | src/shared/swagger-schemas/ | OpenAPI response/request contracts |
Routes use @fastify/type-provider-json-schema-to-ts so the JSON schema types flow directly into request handlers (static typing).
Server-level config¶
server.ts configures AJV:
ajv: {
plugins: [ajvErrors, ajvFilePlugin],
customOptions: {
removeAdditional: 'failing',
coerceTypes: true,
useDefaults: true,
allErrors: true,
keywords: ['collectionFormat']
}
}
Meaning:
removeAdditional: 'failing'— reject unknown properties (strict contracts).coerceTypes: true— coerce compatible types (e.g. numeric strings).useDefaults/allErrors— defaults applied, all errors reported.
Route example¶
const opts = {
schema: {
body: makeBodySchema(zCreateUser),
response: { 200: makeResponseSchema(userDtoSchema) }
}
};
app.post('/v2/users', opts, async (request, reply) => {
/* thin handler */
});
Rules¶
- Every route has a schema. No unvalidated endpoints.
- Validate before touching the database.
- Fail fast with a clear, non-internal error (see error-handling.md).
- Reuse
src/shared/zod-schemasinstead of re-declaring field rules. - WebSocket messages are validated per-event with Zod in
src/interfaces/websocket/schemas(see api/websocket/protocol.md).