# How to Explicitly Specify Types for Express Request and Response Objects in TypeScript

> Master Express TypeScript by explicitly typing request and response objects. Leverage generics for compile-time validation of route data, bodies, and JSON responses.

- Repository: [expressjs/express](https://github.com/expressjs/express)
- Tags: best-practices
- Published: 2026-02-21

---

**Use the generic parameters provided by `@types/express` to type `Request<Params, ResBody, ReqBody, ReqQuery>` and `Response<ResBody>`, enabling compile-time validation of route data, request bodies, and JSON responses.**

The **expressjs/express** repository provides the runtime implementation, while the companion **@types/express** package supplies the TypeScript declarations that describe the framework's public API. Explicitly specifying types for Express Request and Response objects transforms loosely typed JavaScript handlers into self-documenting, refactor-safe code that catches mismatches between your route contracts and implementation at compile time.

## Understanding the Generic Type Signatures

The Express type definitions expose four generic parameters on the `Request` interface and one on `Response`. These map directly to the data extraction logic found in the source code.

- **`Request<Params, ResBody, ReqBody, ReqQuery>`** corresponds to `req.params`, `req.res`, `req.body`, and `req.query` as implemented in [`lib/request.js`](https://github.com/expressjs/express/blob/main/lib/request.js).
- **`Response<ResBody>`** describes the payload sent by `res.json()`, which the runtime serializes using `JSON.stringify` (see [`lib/response.js`](https://github.com/expressjs/express/blob/main/lib/response.js) lines 31-46).

When you import `{ Request, Response, NextFunction }` from `express`, you gain access to these generics to lock down the shape of data flowing through your application.

## Typing Route Parameters and Query Strings

Route parameters are parsed from the URL pattern by the router logic in [`lib/router/index.js`](https://github.com/expressjs/express/blob/main/lib/router/index.js) and attached to `req.params`. Use the first generic parameter to enforce their types.

```typescript
import { Request, Response, NextFunction } from 'express';

interface UserParams {
  id: string;  // /users/:id
}

export const getUser = (
  req: Request<UserParams>,
  res: Response,
  next: NextFunction
) => {
  const userId = req.params.id;  // ✅ TypeScript knows this is a string
  res.json({ id: userId, name: 'Alice' });
};

```

For query strings, which Express parses using the `qs` library (or Node's built-in `url.parse` depending on configuration), use the fourth generic parameter.

```typescript
interface SearchQuery {
  q: string;
  page?: number;
}

export const search = (
  req: Request<{}, any, any, SearchQuery>,
  res: Response,
  next: NextFunction
) => {
  const term = req.query.q;         // ✅ string
  const page = req.query.page ?? 1; // ✅ number | undefined
  res.json({ term, page });
};

```

## Typing Request Bodies and JSON Responses

Middleware like `express.json()` processes the raw body and assigns it to `req.body`. Type this using the third generic parameter, and enforce response contracts with `Response<ResBody>`.

```typescript
interface CreateUserBody {
  name: string;
  email: string;
  age?: number;
}

interface UserResponse {
  id: string;
  name: string;
  email: string;
  age?: number;
}

export const createUser = (
  req: Request<{}, UserResponse, CreateUserBody>,
  res: Response<UserResponse>,
  next: NextFunction
) => {
  const { name, email, age } = req.body;  // ✅ name and email are required strings
  const newUser: UserResponse = { id: '123', name, email, age };
  res.status(201).json(newUser);          // ✅ Must match UserResponse shape
};

```

The second generic parameter on `Request` (shown as `UserResponse` above) types `req.res`, which is useful when accessing the response object from within middleware chains.

## Typing Middleware and res.locals

Middleware functions often augment the request or response objects. Type `res.locals` by passing an interface to the `Response` generic, and always type `next` as `NextFunction` to prevent misuse in error-handling flows.

```typescript
interface MyLocals {
  user?: { id: string; role: string };
}

export const authMiddleware = (
  req: Request,
  res: Response<MyLocals>,
  next: NextFunction
) => {
  res.locals.user = { id: '123', role: 'admin' };
  next();
};

```

## Centralizing Types and Enabling Strict Checks

Maintain a dedicated `types/` directory for interfaces shared across handlers. This centralizes your API contract and simplifies refactoring when the underlying data model changes.

```typescript
// types/user.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

// handlers/user.ts
import { Request, Response, NextFunction } from 'express';
import { User } from '../types/user';

export const getUser = (
  req: Request<{ id: string }>,
  res: Response<User>,
  next: NextFunction
) => {
  const user: User = { id: req.params.id, name: 'Bob', email: 'bob@example.com' };
  res.json(user);
};

```

Enable strict compiler options in [`tsconfig.json`](https://github.com/expressjs/express/blob/main/tsconfig.json) to ensure these explicit typings enforce compile-time validation:

- `strict: true`
- `noImplicitAny: true`
- `strictNullChecks: true`

## Summary

- Import `Request`, `Response`, and `NextFunction` from `express` to access the official type definitions that mirror the runtime implementation in [`lib/request.js`](https://github.com/expressjs/express/blob/main/lib/request.js) and [`lib/response.js`](https://github.com/expressjs/express/blob/main/lib/response.js).
- Use `Request<Params, ResBody, ReqBody, ReqQuery>` to explicitly type route parameters, response bodies, request bodies, and query strings.
- Apply `Response<ResBody>` to enforce the shape of JSON payloads sent via `res.json()`.
- Type `res.locals` through the `Response` generic when building middleware that attaches data to the response cycle.
- Store shared interfaces in a central location and enable `strict` TypeScript options to maximize compile-time safety.

## Frequently Asked Questions

### How do I type a request body when using express.json() middleware?

Define an interface describing the expected JSON structure and pass it as the third generic argument to `Request`. For example, `req: Request<{}, any, CreateUserBody>` ensures `req.body` matches your `CreateUserBody` interface after the middleware in [`lib/request.js`](https://github.com/expressjs/express/blob/main/lib/request.js) processes the payload.

### Can I override the default types for req.params without affecting other handlers?

Yes. Express's `Request` type is generic, allowing you to specify route-local parameter shapes like `Request<{ id: string }>` for one handler and `Request<{ orgId: string; userId: string }>` for another. These declarations are scoped to the individual function signature and do not pollute the global Express namespace.

### What is the purpose of the second generic parameter on Request?

The second parameter types the response body that `req.res` will eventually send (`ResBody`). While optional, it is useful when your handler logic inspects `req.res` or when you want to ensure consistency between the request handler and the eventual JSON payload shape defined in [`lib/response.js`](https://github.com/expressjs/express/blob/main/lib/response.js).

### Should I install @types/express separately or is it included?

You must install `@types/express` as a dev dependency when working with TypeScript, as the **expressjs/express** repository itself contains only JavaScript source code (including [`package.json`](https://github.com/expressjs/express/blob/main/package.json) which lists Node `>=18` as the engine). The type definitions reside in the DefinitelyTyped repository and provide the generic signatures necessary for explicitly specifying types in your application.