In Express, req.params holds the values of named segments in the route path (/users/:id gives req.params.id), req.query holds the query string after the ? (/search?q=node gives req.query.q), and req.body holds the data sent in the request body. req.params and req.query are filled on every request, but req.body stays undefined until a body parser such as app.use(express.json()) reads it. Values in params and query are always strings.
The first request answers {"params":{"userId":"7"},"query":{"sort":"new","limit":"2"}}. Note the quotes: 7 and 2 are strings, so convert them with Number() before you do math or compare with numbers. Now switch the method to POST, keep the path /users/7/posts and send the body {"title": "Hello", "draft": true}: the answer is 201 with "body":{"title":"Hello","draft":true}, where true is a real boolean because it came from JSON.
req.params vs req.query vs req.body
req.params | req.query | req.body | |
|---|---|---|---|
| Comes from | The path, /users/:id | The query string, ?sort=new | The request body |
| Example URL or body | /users/7 | /users?sort=new | {"name": "Ada"} |
| Value | { id: '7' } | { sort: 'new' } | { name: 'Ada' } |
| Types | Strings | Strings and arrays of strings | Whatever the JSON holds |
| Needs setup | A :name in the route | Nothing | app.use(express.json()) |
| Typical use | Which resource | Filter, sort, page | Data to create or update |
A route can have several parameters, and each name becomes a key of req.params: /teams/:teamId/members/:memberId gives both ids. A hyphen or a dot also separates them, so /flights/:from-:to turns /flights/LAX-SFO into { from: 'LAX', to: 'SFO' }.
req.query with repeated and missing keys
A key that appears twice becomes an array, and a key that is missing is undefined, so give options a default:
The answer is {"page":1,"tags":["red","sale"],"raw":{"tag":["red","sale"]}}. Send GET /products?tag=red&page=3 and tags is still an array, ["red"], because [].concat() turns one string or an array into an array. Express 5 parses query strings with Node's simple parser, so ?filter[size]=m gives the key "filter[size]", not a nested object; Express 4 nested it.
req.body needs express.json()
Node delivers the body as a stream of bytes. express.json() reads it, parses it, and sets req.body, but only when the request has Content-Type: application/json. HTML forms send application/x-www-form-urlencoded, which needs express.urlencoded() instead.
Send POST /signup with the body {"email": "ada@example.com"} and you get 201 with {"email":"ada@example.com","name":"anonymous"}. Send an empty body and you get 400. The ?? {} matters: without it, destructuring undefined throws a TypeError on any request that has no JSON body. For real validation of the shape and types, use a schema library such as zod.
req.headers and req.get()
req.headers is an object of every request header, with lowercase names. req.get(name) reads one header and ignores case, so req.get('Content-Type') and req.get('content-type') return the same value. A few other properties you will use:
path is /whoami and originalUrl is /whoami?debug=1. Add the header Authorization: Bearer abc in the request bar and send it again: token becomes "Bearer abc". Inside a router mounted with app.use('/api', router), req.path loses the /api prefix while req.originalUrl keeps it (Express routing explains mounting).
Common mistakes
Reading req.body on a GET request. Browsers and most clients send no body with GET. Put GET options in the query string.
Testing a query flag for truthiness. ?admin=false gives the string 'false', which is truthy, so if (req.query.admin) passes. Compare strings: req.query.admin === 'true'.
Sending JSON without the header. A client that posts JSON as text/plain gets req.body === undefined. Set Content-Type: application/json on the client.
Frequently Asked Questions
How do I get the client's IP address in Express?
Read req.ip. Behind a proxy or load balancer it is the proxy's address until you set app.set('trust proxy', 1), which makes Express read the X-Forwarded-For header instead.
How do I get the full URL of a request in Express?
req.originalUrl is the path plus the query string as the client sent it. Build the absolute URL with `${req.protocol}://${req.get('host')}${req.originalUrl}`.
How do I handle file uploads in Express?
File uploads arrive as multipart/form-data, which express.json() and express.urlencoded() do not parse. Use a package such as multer, which puts the files in req.file or req.files and the other fields in req.body.
How do I read a cookie from the request?
The raw header is in req.get('cookie'). To get an object in req.cookies, install the cookie-parser package and add app.use(cookieParser()).