Working with Content Negotiation
| API | Returns |
req.accepts(types) | Best match or false |
req.acceptsCharsets() | Charset negotiation |
req.acceptsEncodings() | Compression preferences |
req.acceptsLanguages() | i18n preferences |
Example: Manual negotiation
app.get("/data", (req, res) => {
switch (req.accepts(["json", "html", "xml"])) {
case "json": return res.json(data);
case "html": return res.render("data", { data });
case "xml": return res.type("xml").send(toXml(data));
default: return res.status(406).send("Not Acceptable");
}
});
2. Responding with JSON
Example: Standard envelope
res.json({
data: items,
meta: { page, limit, total },
links: { next: "/items?page=" + (page + 1) }
});
3. Responding with HTML
| Method | Use |
res.send(html) | Inline HTML string |
res.render(view) | Template engine |
res.sendFile(path) | Pre-built HTML file |
4. Responding with XML
Example: XML response
import { create } from "xmlbuilder2";
res.type("application/xml").send(
create({ user: { id: user.id, name: user.name } }).end({ prettyPrint: true })
);
app.get("/users/:id", (req, res) => {
const user = getUser(req.params.id);
res.format({
"application/json": () => res.json(user),
"text/html": () => res.render("user", { user }),
"text/plain": () => res.send(`${user.id}: ${user.name}`),
default: () => res.status(406).send("Not Acceptable")
});
});
Note: res.format() automatically sets Vary: Accept for proper caching.
6. Setting Default Content Type
| res.type() | Result Content-Type |
"json" | application/json |
"html" | text/html; charset=utf-8 |
"text" | text/plain; charset=utf-8 |
".pdf" | application/pdf |
"image/png" | image/png |
7. Handling Unsupported Types
Example: 406 Not Acceptable
const accepted = req.accepts(["json", "html"]);
if (!accepted) return res.status(406).json({
error: "Not Acceptable",
supported: ["application/json", "text/html"]
});
function toCsv(rows) {
if (!rows.length) return "";
const headers = Object.keys(rows[0]);
const escape = (v) => `"${String(v).replace(/"/g, '""')}"`;
return [headers.join(","), ...rows.map(r => headers.map(h => escape(r[h])).join(","))].join("\n");
}
res.format({
"application/json": () => res.json(rows),
"text/csv": () => res.attachment("export.csv").type("csv").send(toCsv(rows))
});
9. Parsing Multiple Content Types
Example: Multi-type body parsing
app.use(express.json({ type: ["application/json", "application/*+json"] }));
app.use(express.urlencoded({ extended: true }));
app.use(express.text({ type: "text/csv" }));
10. Setting charset in Content-Type
| Pattern | Result |
res.type("html") | text/html; charset=utf-8 (auto) |
res.set("Content-Type", "text/html; charset=iso-8859-1") | Explicit override |
res.charset = "utf-8" | Manual property |
Warning: Always use utf-8 for new APIs. Mixing charsets between layers (DB, app, transport) causes mojibake.