Response Methods

Difficulty: Intermediate

The Express response object (res) is an enhanced version of Node.js's http.ServerResponse. It provides a rich set of methods for sending data back to the client. While the raw Node.js response requires you to manually set headers, serialize data, and call end(), Express response methods handle these details automatically.

res.json(data) is the workhorse method for API development. It serializes a JavaScript object or array to JSON, sets the Content-Type header to 'application/json', and sends the response. It handles edge cases like circular references (throws an error) and special values (Infinity, NaN become null). Under the hood, it calls JSON.stringify() with the app's 'json replacer' and 'json spaces' settings.

res.send(data) is a more general method that automatically determines the Content-Type based on the argument type. If you pass a string, it sets Content-Type to 'text/html'. If you pass an object or array, it behaves like res.json(). If you pass a Buffer, it sets Content-Type to 'application/octet-stream'. For most API work, prefer res.json() for clarity.

res.status(code) sets the HTTP status code and returns the response object, allowing method chaining. This is commonly used with res.json(): res.status(201).json(newUser) or res.status(404).json({ error: 'Not found' }). Express also provides res.sendStatus(code) which sets the status AND sends the status text as the response body - res.sendStatus(204) sends a 204 response with 'No Content' as the body.

res.redirect(url) sends a redirect response (302 by default) that tells the browser to navigate to a different URL. You can specify a different status code: res.redirect(301, '/new-url') for permanent redirects. res.redirect('back') redirects to the URL in the Referer header, useful for redirecting back after form submissions.

res.cookie() sets a cookie on the client, and res.clearCookie() removes one. Cookies are sent via the Set-Cookie response header. Important cookie options include httpOnly (prevents JavaScript access, protecting against XSS), secure (only sent over HTTPS), sameSite (CSRF protection), maxAge (expiration in milliseconds), and path (which URLs the cookie applies to).

Code examples

Core Response Methods

const express = require('express');
const app = express();
app.use(express.json());

// res.json() - JSON response
app.get('/api/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});

// res.status().json() - Status code + JSON
app.post('/api/users', (req, res) => {
  const user = { id: 3, ...req.body };
  res.status(201).json(user); // 201 Created
});

// res.send() - Auto-detects content type
app.get('/html', (req, res) => {
  res.send('<h1>Hello World</h1>'); // Content-Type: text/html
});

app.get('/text', (req, res) => {
  res.type('text').send('Plain text response'); // Force text/plain
});

// res.sendStatus() - Status code + status text as body
app.delete('/api/users/:id', (req, res) => {
  res.sendStatus(204); // Sends "No Content"
});

// res.redirect() - Redirect to another URL
app.get('/old-page', (req, res) => {
  res.redirect(301, '/new-page'); // Permanent redirect
});

app.get('/new-page', (req, res) => {
  res.send('You have been redirected!');
});

app.listen(3000, () => console.log('Server on port 3000'));

res.json() is for JSON APIs, res.send() auto-detects content type from the argument, res.status() chains with other methods, res.sendStatus() sends just a status code, and res.redirect() navigates the client to another URL.

Sending Files and Downloads

const express = require('express');
const path = require('path');
const app = express();

// res.sendFile() - Send a file as the response
app.get('/page', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// res.download() - Prompt file download
app.get('/download/report', (req, res) => {
  const filePath = path.join(__dirname, 'files', 'report.pdf');
  res.download(filePath, 'monthly-report.pdf', (err) => {
    if (err) {
      res.status(404).json({ error: 'File not found' });
    }
  });
});

// Conditional response format (content negotiation)
app.get('/api/data', (req, res) => {
  const data = { id: 1, name: 'Sample', value: 42 };

  res.format({
    'application/json': () => {
      res.json(data);
    },
    'text/html': () => {
      res.send(`<h1>${data.name}</h1><p>Value: ${data.value}</p>`);
    },
    'text/plain': () => {
      res.type('text').send(`${data.name}: ${data.value}`);
    },
    default: () => {
      res.status(406).json({ error: 'Not Acceptable' });
    }
  });
});

app.listen(3000, () => console.log('Server on port 3000'));

res.sendFile() sends a file with the correct Content-Type based on extension. res.download() triggers a browser download dialog with a custom filename. res.format() performs content negotiation, sending different formats based on the client's Accept header.

Cookies and Response Headers

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();

app.use(express.json());
app.use(cookieParser());

// Set cookies on login
app.post('/api/login', (req, res) => {
  const { username } = req.body;

  // Set a regular cookie
  res.cookie('username', username, {
    maxAge: 24 * 60 * 60 * 1000, // 1 day
    httpOnly: false               // Accessible by JavaScript
  });

  // Set an HTTP-only session cookie
  res.cookie('sessionToken', 'abc123xyz', {
    httpOnly: true,   // NOT accessible by JavaScript (XSS protection)
    secure: process.env.NODE_ENV === 'production', // HTTPS only in prod
    sameSite: 'strict', // CSRF protection
    maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
  });

  res.json({ message: `Welcome, ${username}!` });
});

// Read cookies
app.get('/api/profile', (req, res) => {
  res.json({
    username: req.cookies.username || 'Guest',
    hasSession: !!req.cookies.sessionToken
  });
});

// Clear cookies on logout
app.post('/api/logout', (req, res) => {
  res.clearCookie('username');
  res.clearCookie('sessionToken');
  res.json({ message: 'Logged out' });
});

// Custom response headers
app.get('/api/data', (req, res) => {
  res.set('X-Total-Count', '42');
  res.set('X-Request-Id', Math.random().toString(36).slice(2));
  res.set('Cache-Control', 'public, max-age=300');
  res.json({ data: 'Some data' });
});

app.listen(3000, () => console.log('Server on port 3000'));

res.cookie() sets cookies with options for security (httpOnly, secure, sameSite) and expiration (maxAge). res.clearCookie() removes cookies. res.set() adds custom headers - commonly used for pagination metadata (X-Total-Count) and cache control.

Key points

Concepts covered

res.json, res.send, res.status, res.redirect, res.render, res.sendFile, res.cookie, Method Chaining