Chapter 6 of 13
~11 minThe Request & Response Objects
Every route handler and middleware function works with the same two objects: req, representing the incoming HTTP request, and res, representing the outgoing response. Express extends Node’s bare http request/response objects with a large set of convenience properties and methods.
Key req Properties
req.params holds route parameters, req.query holds the parsed query string, and req.body holds the parsed request body (once a body-parsing middleware like express.json() has run). req.headers gives access to every HTTP header as a lower-cased-key object, and req.method and req.originalUrl report the HTTP verb and the full requested path, which is especially useful for logging inside a mounted router where req.url has already had the mount prefix stripped off.
app.post('/orders/:id/items', (req, res) => { console.log('Method:', req.method); console.log('Original URL:', req.originalUrl); console.log('Route param id:', req.params.id); console.log('Query:', req.query); console.log('Body:', req.body); console.log('Content-Type header:', req.headers['content-type']); res.status(201).json({ received: true }); });
Sending Responses: send, json, status
res.send() sends a response body of nearly any type (a string, a Buffer, or an object, which it automatically serializes as JSON), while res.json() is more explicit about intent and always serializes its argument as JSON with the correct Content-Type header. res.status(code) sets the HTTP status code and returns res, so it is almost always chained directly onto a following .json() or .send() call.
app.get('/health', (req, res) => { res.status(200).json({ status: 'ok' }); }); app.get('/legacy-text', (req, res) => { res.send('Plain text response'); // Content-Type: text/html by default for strings }); app.post('/tasks', (req, res) => { res.status(201).json({ id: 1, title: req.body.title }); }); app.get('/missing', (req, res) => { res.status(404).json({ error: 'Not found' }); });
Redirects and Other res Methods
res.redirect([status,] path) sends an HTTP redirect response, defaulting to a 302 (temporary) status unless a different code like 301 (permanent) is passed explicitly. res.set()/res.header() sets a response header manually, res.cookie() sets a cookie, and res.end() ends the response with no body at all, commonly paired with a 204 No Content status.
app.post('/login', (req, res) => { // ... verify credentials ... res.redirect('/dashboard'); // 302 by default }); app.delete('/tasks/:id', (req, res) => { // ... delete the task ... res.status(204).end(); // no body, request succeeded });
A Response Can Only Be Sent Once
Calling more than one of res.send/json/end/redirect for the same request throws an "ERR_HTTP_HEADERS_SENT" error, because the HTTP response headers are already committed to the socket after the first call. This commonly happens by accident when a middleware sends an error response but forgets to return immediately afterward, letting execution fall through to a second response later in the same function.
Coding challenge
Write a route handler for POST /login that checks req.body for a "username" field; if missing, respond with a 400 status and a JSON error message and return immediately; otherwise respond with a 200 status and a JSON object confirming the login.
// Write your solution here
Answer every question below to mark this chapter as complete.
Quiz Yourself
Score: 0/41. Where does Express place the parsed request body, once a body-parsing middleware has run?
2. What is the practical difference between res.send() and res.json()?
3. Why does calling res.json() twice for the same request throw an error?
4. What does res.redirect('/dashboard') do by default, without specifying a status code?