patterntypescriptexpressTip
Content Negotiation with Accept Headers
Viewed 0 times
content negotiationAccept header406 Not AcceptableCSVXMLformat
Problem
APIs return JSON unconditionally. Clients that need XML, CSV, or specific JSON variants have no way to request their preferred format, requiring separate endpoints per format.
Solution
Implement content negotiation using the Accept request header to serve multiple formats from one endpoint.
app.get('/users', async (req, res) => {
const users = await db.getUsers();
// req.accepts() negotiates based on quality factors (q=0.9)
const accept = req.accepts(['json', 'csv', 'xml']);
switch (accept) {
case 'json':
res.json(users);
break;
case 'csv':
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename="users.csv"');
res.send(toCsv(users));
break;
case 'xml':
res.setHeader('Content-Type', 'application/xml');
res.send(toXml(users));
break;
default:
// 406 Not Acceptable
res.status(406).json({
error: 'Not Acceptable',
supported: ['application/json', 'text/csv', 'application/xml'],
});
}
});Why
Content negotiation allows one endpoint to serve multiple formats. The client declares preference via Accept header with quality factors; the server responds with the best match or 406.
Gotchas
- Return 406 Not Acceptable (not 400) when the requested format is not supported.
- Default to JSON when no Accept header is sent.
- Set the correct Content-Type on the response — clients use it to decode the body.
Revisions (0)
No revisions yet.