Request and Response in Depth
The req (request) and res (response) objects are at the heart of every Express handler. You’ve used req.params, req.query, and res.json() — now let’s see the full picture.
The request object (req)
Section titled “The request object (req)”req represents the incoming HTTP request.
req.body
Section titled “req.body”The parsed request body — available after express.json() middleware:
app.use(express.json())
app.post('/posts', (req, res) => { const { title, body, userId } = req.body // req.body is the parsed JSON object})TypeScript note: req.body is typed as any. Define an interface and cast:
interface CreatePostBody { title: string body: string}
app.post('/posts', (req, res) => { const { title, body } = req.body as CreatePostBody})req.headers
Section titled “req.headers”HTTP request headers as a lowercase key-value object:
const contentType = req.headers['content-type']const authHeader = req.headers['authorization'] // 'Bearer eyJ...'const userAgent = req.headers['user-agent']Headers carry metadata: content type, authorization tokens, and cache directives.
req.method
Section titled “req.method”The HTTP method as an uppercase string: 'GET', 'POST', 'DELETE', etc.
req.path and req.url
Section titled “req.path and req.url”// For request to GET /posts/42?sort=newestreq.path // '/posts/42'req.url // '/posts/42?sort=newest'req.ip
Section titled “req.ip”The client’s IP address — useful for rate limiting and logging.
The response object (res)
Section titled “The response object (res)”res is how you send data back to the client.
res.json(data)
Section titled “res.json(data)”Sends a JSON response with Content-Type: application/json:
res.json({ id: 1, title: 'Hello' })// → 200 OK, Content-Type: application/json, body: {"id":1,"title":"Hello"}res.status(code)
Section titled “res.status(code)”Sets the HTTP status code. Chain with .json():
res.status(201).json({ id: newPost.id }) // 201 Createdres.status(400).json({ error: 'Invalid' }) // 400 Bad Requestres.status(404).json({ error: 'Not found' }) // 404 Not Foundres.status(401).json({ error: 'Unauthorized' }) // 401 Unauthorizedres.sendStatus(code)
Section titled “res.sendStatus(code)”Sends a status code with the default status message as the body:
res.sendStatus(204) // 204 No Content (for successful DELETE)res.sendStatus(401) // 401 Unauthorizedres.set(header, value)
Section titled “res.set(header, value)”Sets a response header:
res.set('X-Custom-Header', 'value')res.set('Cache-Control', 'no-store')res.locals
Section titled “res.locals”An object for passing data between middleware functions in a request’s lifecycle:
// In auth middlewareres.locals.user = decodedJwtPayload
// In route handlerconst user = res.locals.userStandard API response patterns
Section titled “Standard API response patterns”Consistent response shapes make your API easier to use:
// Success with datares.json({ data: post })
// Createdres.status(201).json({ data: newPost })
// Errorres.status(400).json({ error: 'Title is required' })
// No content (successful delete)res.sendStatus(204)The Bulletin API uses a simple pattern: top-level data for success, top-level error (string) for failures.
Exercise
Section titled “Exercise”- Log
req.headersin a route handler and make a request — inspect the headers your browser/curl sends. - Build a route that reads
Authorizationfromreq.headers, validates it isn’t empty, and returns401if missing. - Create a helper function
sendError(res, status, message)that reduces repetition in error responses.
req.body— parsed JSON body (requiresexpress.json()middleware)req.headers— lowercase key-value object of request headersreq.params— dynamic URL segments;req.query— query string valuesres.json(data)— sends JSON with correct Content-Typeres.status(code).json(data)— sets status code before sendingres.locals— passes data between middleware in the same request cycle