Validation at the boundary
Parse untrusted input once, at the edge, into a type the rest of your code can rely on, and never validate the same thing twice on the inside.
A TypeScript interface describing a request body is a comment with better tooling. At runtime the body is whatever arrived, and if you cast it you have told the compiler to stop helping while changing nothing about the data. Parsing it with a schema library, Zod, Pydantic, whatever your runtime prefers, is the step that turns a claim into a fact, and it should happen once, at the boundary, before any handler logic runs.
Draw the line between validation and business rules deliberately. Shape, type, format, range and length are validation, and they belong in the schema where they can produce a consistent 400 with a field-level error list. Whether this user may create this booking is business logic and belongs in the handler, because it needs the database and it produces a different status code.
Two defaults are worth setting on day one. Reject unknown keys rather than ignoring them, so a client typo fails loudly instead of silently doing nothing. And bound every string, because an unbounded text field is a memory and storage problem that arrives as a surprise long after the endpoint was written.
You should now be able to
- Validate a request body into a typed value
- Explain why type annotations alone prove nothing at runtime
- Decide what belongs in schema validation and what is business logic
Loading…