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.