Steve Kinney

Adding Types to Headers and Cookies with Express

Request and response headers play a crucial role in HTTP communication. TypeScript can help ensure they’re properly typed.

Typing Request Headers

Headers arrive as strings or undefined:

// Define expected authorization header

interface AuthHeaders {
  authorization?: string;
}

app.get('/protected', (req: Request<{}, {}, {}, {}, AuthHeaders>, res: Response) => {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    return res.status(401).send('Unauthorized');
  }

  // Process authorization header

  const token = authHeader.replace('Bearer ', '');

  // …
});

But the fifth generic parameter of Request is less commonly used. A more practical approach is to use type assertions or header validation:

// Header validation middleware

function requireHeader(headerName: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.headers[headerName]) {
      return res.status(400).json({
        error: `Missing required header: ${headerName}`,
      });
    }

    next();
  };
}

// Using the middleware

app.get('/protected', requireHeader('authorization'), (req: Request, res: Response) => {
  // We can now safely assert this exists

  const authHeader = req.headers.authorization as string;

  // …
});

Setting Response Headers

For response headers, TypeScript’s help is more limited because headers are set imperatively:

app.get('/api/data', (req: Request, res: Response) => {
  // Set headers

  res.setHeader('Content-Type', 'application/json');

  res.setHeader('Cache-Control', 'no-cache');

  // Send response

  res.json({ data: 'example' });
});

We can improve this with constants and helper functions:

// Define header constants

const HEADERS = {
  CONTENT_TYPE: 'Content-Type',

  CACHE_CONTROL: 'Cache-Control',

  AUTHORIZATION: 'Authorization',
} as const;

// Define header value constants

const CONTENT_TYPES = {
  JSON: 'application/json',

  HTML: 'text/html',

  XML: 'application/xml',
} as const;

// Helper function for setting common headers

function setStandardHeaders(res: Response): void {
  res.setHeader(HEADERS.CONTENT_TYPE, CONTENT_TYPES.JSON);

  res.setHeader(HEADERS.CACHE_CONTROL, 'no-cache');
}

// Usage

app.get('/api/data', (req: Request, res: Response) => {
  setStandardHeaders(res);

  // Additional custom headers

  res.setHeader('X-Custom-Header', 'value');

  res.json({ data: 'example' });
});

Working with Cookies

Cookies add another layer of complexity. The popular cookie-parser middleware extends the request object:

import cookieParser from 'cookie-parser';

// Setup cookie parser

app.use(cookieParser());

// Define expected cookies

interface AuthCookies {
  sessionId?: string;
}

// Create a middleware to validate required cookies

function requireCookie(cookieName: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.cookies?.[cookieName]) {
      return res.status(400).json({
        error: `Missing required cookie: ${cookieName}`,
      });
    }

    next();
  };
}

// Use it in routes

app.get('/dashboard', requireCookie('sessionId'), (req: Request, res: Response) => {
  // Safe to assert this cookie exists

  const sessionId = req.cookies.sessionId as string;

  // Use the session ID

  // …

  res.send('Dashboard');
});

// Setting cookies

app.post('/login', (req: Request, res: Response) => {
  // Authentication logic

  // …

  // Set cookie

  res.cookie('sessionId', 'abc123', {
    httpOnly: true,

    secure: process.env.NODE_ENV === 'production',

    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  });

  res.json({ success: true });
});

The cookie-parser types are included with @types/cookie-parser, which extends the Express Request interface.

Last modified on .