Skip to content

Express Request and Response Objects

Every route handler in Express receives a request object and a response object. This lesson covers the properties and methods on req and res you will use in almost every handler you write.

What Are req and res?

req represents the incoming HTTP request, it holds the URL, headers, method, route parameters, query string, and (once parsed by middleware) the body. res represents the outgoing HTTP response, offering methods to set status codes, headers, and the response body.

Both objects are Express-enhanced versions of Node's raw http.IncomingMessage and http.ServerResponse, meaning all the low-level Node properties still exist, plus the extra convenience methods Express adds.

app.get('/users/:id', (req, res) => {
  console.log(req.method);   // "GET"
  console.log(req.params.id); // route parameter
  console.log(req.query);     // query string object

  res.status(200).json({ id: req.params.id });
});

req.params.id comes from the :id segment in the route path; req.query comes from ?key=value pairs after the ?.

Common req and res Members

req.params    // route parameters
req.query     // query string parameters
req.body      // parsed request body (needs middleware)
req.headers   // request headers
req.method    // HTTP method
req.path      // URL path without query string

res.status(code)
res.send(body)
res.json(obj)
res.redirect(url)
res.set(header, value)
  • req.params only exists for named route segments like :id.
  • req.body is undefined unless a body-parsing middleware (like express.json()) ran first.
  • res methods are chainable, e.g. res.status(201).json(user).
  • Only one final response method (send, json, redirect, end, ...) should run per request.

req / res Cheatsheet

The properties and methods you will reach for constantly while writing Express handlers.

Member Example Description
req.params req.params.id Named route segment values
req.query req.query.page Query string values
req.body req.body.email Parsed request body
req.headers req.headers['authorization'] Request headers (lowercased)
req.get() req.get('Content-Type') Reads a single header
res.status() res.status(404) Sets the HTTP status code
res.json() res.json({ ok: true }) Sends JSON with correct headers
res.redirect() res.redirect('/login') Sends a 302 redirect

Reading Data From the Request

A single route often needs to combine several sources of input: route parameters for identifying a resource, query parameters for filtering or pagination, and the body for data being created or updated.

// GET /users/42/orders?status=paid&page=2
app.get('/users/:userId/orders', (req, res) => {
  const { userId } = req.params;
  const { status, page = 1 } = req.query;

  res.json({ userId, status, page: Number(page) });
});

Sending a Response

You must send exactly one response per request. Express does not automatically end the response, if a handler never calls a terminating method like res.send(), res.json(), or res.end(), the client's connection hangs until it times out.

Method When to Use
res.send(str) Plain text or HTML
res.json(obj) Structured JSON data
res.status(code).json(obj) JSON with a specific status code
res.sendStatus(code) Status-only response, e.g. 204
res.redirect(url) Redirect the client to another URL

Common Mistakes

  • Calling two terminating response methods in the same handler (e.g. res.send() then later res.json()), causing a "headers already sent" error.
  • Reading req.body without first registering express.json() or express.urlencoded().
  • Confusing req.params (route segments) with req.query (the query string).
  • Forgetting return after sending an early response inside a conditional, letting code fall through and try to respond twice.

Key Takeaways

  • req describes the incoming request; res builds the outgoing response.
  • req.params, req.query, and req.body are the three main sources of client input.
  • Exactly one terminating response method should run per request.
  • Chain res.status() before res.json()/res.send() to set a non-default status code.

Pro Tip

Add return before every response call inside conditional branches (return res.status(400).json(...)), it is a small habit that prevents an entire class of double-response bugs.