Skip to content

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

  1. Every route has a schema. No unvalidated endpoints.
  2. Validate before touching the database.
  3. Fail fast with a clear, non-internal error (see error-handling.md).
  4. Reuse src/shared/zod-schemas instead of re-declaring field rules.
  5. WebSocket messages are validated per-event with Zod in src/interfaces/websocket/schemas (see api/websocket/protocol.md).