100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Node.js & Express Backend
25 minintermediate

Content Negotiation and Media Types

Content Negotiation and Media Types

Content negotiation is the mechanism by which an HTTP client and server agree on the best representation of a resource. The client advertises what formats it can accept via the Accept header; the server inspects that header and responds with the most appropriate Content-Type. Express does not implement full content negotiation automatically, but it gives you req.accepts() and res.format() to wire it up cleanly.

Analogy🏏Cricket
Think of it like cricket: A broadcaster wants to cover the same match for three audiences: live TV viewers want HD video, radio listeners want audio commentary, and data journalists want ball-by-ball JSON feeds. The production team checks which channel each requester is tuned to and sends exactly the right format. The match itself (the resource) never changes; only the representation does. Similarly, your Express route serves /products to browsers as HTML, to mobile apps as JSON, and to data pipelines as CSV — all from a single endpoint. Rohit Sharma's innings stats are the same no matter which scoreboard you read them on.

The Accept Header and MIME Types

The Accept request header lists the MIME types the client prefers, with optional quality values (q-factors) indicating priority. For example: Accept: application/json, text/html;q=0.9, */*;q=0.8. The server should honour the highest-priority type it can produce. Common MIME types in APIs include application/json, application/xml, text/csv, text/html, and application/pdf.

javascript
// Reading Accept header manually
app.get('/report', (req, res) => {
  const preferred = req.accepts(['json', 'html', 'csv']);

  if (preferred === 'json') {
    res.json({ sales: 1200 });
  } else if (preferred === 'html') {
    res.send('<h1>Sales: 1200</h1>');
  } else if (preferred === 'csv') {
    res.setHeader('Content-Type', 'text/csv');
    res.send('metric,value\nsales,1200');
  } else {
    res.status(406).json({ error: 'Not Acceptable' });
  }
});
Analogy🏏Cricket
Think of it like cricket: A team's media officer fields requests from reporters after a match. A TV crew wants video highlights, a print journalist wants a written quote, and an international agency wants a stats spreadsheet. The officer checks who is asking and hands out the right material. If someone asks for a format she cannot provide — say, a 3D hologram — she politely says 'not available' (406). Express's req.accepts() plays the same triage role.

res.format() — Declarative Content Negotiation

Express provides res.format() which takes an object keyed by MIME type and automatically picks the best match based on the Accept header. It throws a 406 error automatically if no match is found, which you can catch with a default key. This is cleaner than nested if-else logic and keeps negotiation intent visible at the route level.

javascript
app.get('/players/:id', async (req, res) => {
  const player = await Player.findById(req.params.id);

  res.format({
    'application/json': () => {
      res.json(player);
    },
    'text/html': () => {
      res.render('player', { player });
    },
    'text/csv': () => {
      res.setHeader('Content-Disposition', 'attachment; filename="player.csv"');
      res.send(`id,name,runs\n${player.id},${player.name},${player.runs}`);
    },
    default: () => {
      res.status(406).send('Not Acceptable');
    }
  });
});
Analogy🏏Cricket
Think of it like cricket: Think of the umpire's signal system. One signal means six, another means wide, another means no-ball. Each signal is a clear, distinct response to a distinct situation. res.format() works the same way — you declare each possible output format and Express routes the request to the right handler automatically, without ambiguity. Virat Kohli's team management would never send a Test match answer to a T20 question.
Lesson 16 of 36
0% complete