CodePark/ questions / TypeScript on the server
START PRACTICINGPRACTICE
Backend question · TypeScript › TypeScript on the server

Your Express handler declares req.body as a CreateUser type. Why is that not enough, and how do you make the type trustworthy?

import { z } from "zod";

const CreateUser = z.object({
  name: z.string().min(1),
  age: z.number().int().positive(),
});
type CreateUser = z.infer<typeof CreateUser>;

app.post("/users", (req, res) => {
  const result = CreateUser.safeParse(req.body);
  if (!result.success) return res.status(400).json(result.error.issues);
  const input: CreateUser = result.data;
});

The type is only a promise to the compiler. Nothing checks it when the code runs. TypeScript types are erased at compile time: the compiler removes them, so the running JavaScript has no types left. In Express, req.body is typed any. Adding a type annotation or a cast to it only silences the compiler. A client can still send a missing field, or a string where you expect a number.

To make the type trustworthy, validate external input at the boundary, the place where data enters your app. Use a schema library:

  • Zod parses unknown data and returns either typed data or a list of issues.
  • z.infer builds the static type from the schema.
  • Because the type comes from the schema, the validator and the type cannot drift apart.

In NestJS, the usual equivalent is DTO (Data Transfer Object) classes with class-validator decorators, plus the global ValidationPipe.

  • Types are erased at compile time, so nothing checks request data at runtime.
  • Annotating or casting req.body only silences the compiler.
  • Validate input at the boundary with a schema, such as zod or class-validator.
  • z.infer builds the type from the schema, so there is one source of truth.