intermediate

Validation

Validate untrusted input at service boundaries with schemas, normalization, typed outputs, and clear error responses.

Validate all untrusted input at the service boundary — body, query, params, headers — and produce typed, normalized values handlers can trust.

Schema libraries (Zod, Valibot, Joi, Ajv for JSON Schema) parse and coerce deliberately:

					const CreateOrder = z.object({
  sku: z.string().min(1),
  quantity: z.coerce.number().int().positive().max(100),
});

export async function createOrderHandler(req, res) {
  const parsed = CreateOrder.safeParse(req.body);
  if (!parsed.success) {
    return res.status(400).json({ errors: parsed.error.flatten() });
  }
  const order = await service.create(parsed.data);
  res.status(201).json(order);
}
				

Validation is not authorization — knowing the shape of input does not mean the caller may perform the action. Return 400 for malformed input, 422 when semantics fail, 403/401 for authz.

On interviews: where validation lives (controller vs domain), how to avoid duplicating DB constraints, and error response consistency.

Common pitfalls: validating only body; trusting `Content-Type`; using validation library types as domain entities without mapping.

The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.

Checklist:

  • Schemas colocated with route or OpenAPI source.
  • safeParse at boundary; throw inside domain only for invariants.
  • Normalize (trim, lowercase email) once.
  • Separate validation errors from business rule failures.