Build a Typed REST API with Express, TypeScript & Zod

Lesson 4 of 8

Typed Request Handlers & Middleware

Express's own types describe the shape of a request/response, but leave req.body, req.params, and req.query as loosely typed by default. This lesson tightens that up and builds a reusable pattern for async route handlers.

Typing params, body, and query explicitly

TypeScript

Request's first generic parameter types req.params. Left as plain Request, req.params.id would be typed loosely, spelling it out here catches a typo like req.params.taskId at compile time instead of at 2am in production logs.

A reusable async handler wrapper

Express doesn't automatically catch a rejected Promise from an async route handler, an unhandled rejection just hangs or crashes the process.

TypeScript

asyncHandler wraps any async function, catches whatever it throws or rejects with, and forwards it to next(err), Express's normal error-handling path, instead of letting it disappear silently.

Typed error-handling middleware

TypeScript

Express recognizes error-handling middleware by its four parameters, err first, note it's typed unknown, not Error, anything can technically be thrown in JavaScript, so instanceof Error narrowing is what makes reading .message safe.

TIP

Register app.use(errorHandler) after every route. Express matches error middleware in registration order too, one placed too early simply never gets reached.

📝 Typed Handlers & Middleware Quiz

Passing score: 70%
  1. 1.Express automatically catches a rejected Promise thrown inside an async route handler.

  2. 2.How does Express recognize error-handling middleware?

  3. 3.In Request<{ id: string }>, the generic parameter types req.____.

  4. 4.What does asyncHandler do with a rejected Promise from the wrapped function?

  5. 5.app.use(errorHandler) should be registered before the routes it is meant to catch errors from.