Background
Our request DTOs currently express validation constraints through tsoa JSDoc annotations, e.g.:
export interface RegisterRequest {
/** @minLength 1 */
firstName: string
/** @pattern ^(.+)@(.+)$ Please provide valid email */
email: string
/** @minLength 14 Password must be at least 14 characters */
password: string
role: Role
}
Problem
tsoa annotations only accept hardcoded literals — there's no way to reference a variable or shared constant inside them (confirmed after digging through the tsoa docs). This has a few consequences:
- Non-DRY: the same constraints (min lengths, patterns, password rules) are duplicated as string literals across DTOs with no single source of truth.
- Brittle / drift-prone: the annotations can silently fall out of sync with the actual runtime validation logic (e.g. the
PasswordStrength function). Right now our only guard against this is a set of backend tests that flag when the annotations and PasswordStrength diverge — a safety net, not a fix.
Proposed solution
Port input validation to Zod, using zod-openapi to generate the OpenAPI/Swagger schema from the Zod schemas. This gives us:
- A single source of truth for each field's constraints, reusable across validation and doc generation.
- Shared constants (e.g.
MIN_PASSWORD_LENGTH) that can feed both the schema and functions like PasswordStrength, eliminating the drift problem.
- Consistency with Elsa Data, which already uses Zod.
Scope / next steps
Notes
Surfaced from this slack discussion.
Background
Our request DTOs currently express validation constraints through tsoa JSDoc annotations, e.g.:
Problem
tsoa annotations only accept hardcoded literals — there's no way to reference a variable or shared constant inside them (confirmed after digging through the tsoa docs). This has a few consequences:
PasswordStrengthfunction). Right now our only guard against this is a set of backend tests that flag when the annotations andPasswordStrengthdiverge — a safety net, not a fix.Proposed solution
Port input validation to Zod, using
zod-openapito generate the OpenAPI/Swagger schema from the Zod schemas. This gives us:MIN_PASSWORD_LENGTH) that can feed both the schema and functions likePasswordStrength, eliminating the drift problem.Scope / next steps
zod-openapiintegration with our current tsoa/OpenAPI setupRegisterRequestas a reference DTOPasswordStrengthNotes
Surfaced from this slack discussion.