# ADR-003: Standardized Input Validation using Middleware

**Date**: 2025-12-12

**Status**: Proposed

## Context

Our Express route handlers currently perform manual validation of request parameters, queries, and bodies. This involves repetitive boilerplate code using `parseInt`, `isNaN`, and type checks like `Array.isArray`. This approach has several disadvantages:

1. **Code Duplication**: The same validation logic (e.g., checking for a valid integer ID) is repeated across many different routes.
2. **Cluttered Business Logic**: Route handlers are cluttered with validation code, obscuring their primary business logic.
3. **Inconsistent Error Messages**: Manual validation can lead to inconsistent error messages for similar validation failures across the API.
4. **Error-Prone**: It is easy to forget a validation check, leading to unexpected data types being passed to service and repository layers, which could cause runtime errors.

## Decision

We will adopt a schema-based approach for input validation using the `zod` library and a custom Express middleware.

1. **Adopt `zod` for Schema Definition**: We will use `zod` to define clear, type-safe schemas for the `params`, `query`, and `body` of each API request. `zod` provides powerful and declarative validation rules and automatically infers TypeScript types.

2. **Create a Reusable Validation Middleware**: A generic `validateRequest(schema)` middleware will be created. This middleware will take a `zod` schema, parse the incoming request against it, and handle success and error cases.
   - On successful validation, the parsed and typed data will be attached to the `req` object (e.g., `req.body` will be replaced with the parsed body), and `next()` will be called.
   - On validation failure, the middleware will call `next()` with a custom `ValidationError` containing a structured list of issues, which `ADR-001`'s `errorHandler` can then format into a user-friendly `400 Bad Request` response.

3. **Refactor Routes**: All route handlers will be refactored to use this new middleware, removing all manual validation logic.

4. **(New) Resilient Type Inference**: To achieve full type safety, we will use **inline type assertions** with `z.infer<typeof schema>`. This ensures the code inside the handler is fully typed and benefits from IntelliSense without creating complex utility types that conflict with Express's `RequestHandler` signature.

### Example Usage (Refined Pattern)

```typescript
// In flyer.routes.ts

import { z } from 'zod';
import { validateRequest } from '../middleware/validation';

// 1. Define the schema
const getFlyerSchema = z.object({
  params: z.object({
    id: z.string().pipe(z.coerce.number().int().positive()),
  }),
});

// 2. Infer the type from the schema for local use
type GetFlyerRequest = z.infer<typeof getFlyerSchema>;

// 3. Apply the middleware and use an inline cast for the request
router.get('/:id', validateRequest(getFlyerSchema), async (req, res, next) => {
  // Cast 'req' to the inferred type.
  // This provides full type safety for params, query, and body.
  const { params } = req as unknown as GetFlyerRequest;

  try {
    const flyer = await db.flyerRepo.getFlyerById(params.id); // params.id is 'number'
    res.json(flyer);
  } catch (error) {
    next(error);
  }
});
```

## Consequences

### Positive

**Reduced Complexity**: Avoids maintaining a complex ValidatedRequestHandler utility type that could conflict with Express or TypeScript upgrades.
**Explicit Contracts**: By defining the Request type right next to the route, the contract for that endpoint is immediately visible.
**Improved Readability**: Route handlers become much cleaner and focus exclusively on their core business logic.
**Type Safety**: `zod` schemas provide strong compile-time and runtime type safety, reducing bugs.
**Consistent and Detailed Errors**: The `errorHandler` can be configured to provide consistent, detailed validation error messages for all routes (e.g., "Query parameter 'limit' must be a positive integer").
**Robustness**: Prevents invalid data from ever reaching the service or database layers.

### Negative

**Minor Verbosity**: Requires one extra line (type ... = z.infer<...>) and a controlled cast (as unknown as ...) within the handler function.
**New Dependency**: Introduces `zod` as a new project dependency.
**Learning Curve**: Developers need to learn the `zod` schema definition syntax.
**Refactoring Effort**: Requires a one-time effort to create schemas and refactor all existing routes to use the `validateRequest` middleware.
