Parse at the boundary, trust everywhere else

TypeScriptarchitecturebackend

The TypeScript type on an API response is a lie until something checks it. The compiler believes whatever you assert, so await res.json() as User is a guess wearing a type annotation. It compiles, and it is wrong the first time the API changes a field.

I parse everything that crosses a boundary with a schema instead of casting it:

const User = z.object({
  id: z.string(),
  email: z.string().email(),
  createdAt: z.coerce.date(),
});

const user = User.parse(await res.json());

Now user is genuinely a User, verified at runtime, and the failure happens at the edge where the bad data entered, with a readable message about which field was wrong. Not three layers deep where a undefined.toLowerCase() finally throws.

The pattern generalizes: validate at the boundary, then trust the type internally. Network responses, request bodies, environment variables, config files, anything from outside the program. Parse once at the door, and the type system tells the truth for the rest of the call stack. Cast, and you have only postponed the crash to a worse place.