Introduction
Every useful API needs to receive data from the client — a user ID in the URL, filter options in the query string, or a JSON body with form data. Before you can build CRUD endpoints, you need to know exactly where Express puts each piece of incoming data and how to read it reliably.
This lesson walks through the three main input sources — URL params, query strings, and request bodies — then puts all three to work in a complete set of CRUD endpoints for a simple resource.
Reading Input: Params, Query Strings, and Body
Express parses incoming request data into three distinct objects on the request object.
URL parameters are named segments of the path. You declare them with a colon in your route definition, and Express makes them available on req.params.
Query strings appear after the question mark in a URL, like /users?role=admin&limit=10. Express parses them automatically into req.query.
Request bodies carry structured data sent by the client, most often JSON. Express does not parse the body by default — you need the built-in express.json() middleware registered before your routes.
Once that middleware is in place, POST and PUT payloads are available on req.body.
Building a Full CRUD API
To keep the focus on routing mechanics, the examples below use an in-memory array as the data store. In a real project you would replace the array operations with database calls.
Start with the data and a helper to generate IDs:
Create — POST /users reads the body and pushes a new record:
Read all — GET /users supports optional filtering by role via query string:
Read one — GET /users/:id uses req.params to find a single record:
Update — PUT /users/:id combines params (which record) with body (new values):
Delete — DELETE /users/:id removes the record and returns 204 No Content:
Common Mistakes
Forgetting express.json() is the most frequent source of confusion for beginners. If req.body is undefined, check that the middleware is registered and that the client is sending a Content-Type: application/json header.
Not converting req.params.id to a number before comparing it is another subtle bug. Route params are always strings. Comparing the string "1" to the number 1 with strict equality will always return false, so your find or findIndex calls will silently miss every record.
Returning data after sending a response causes a "headers already sent" crash in Express. Always use return before res calls inside conditionals, as shown in the examples above.
Summary
Express splits incoming request data across three objects: req.params for URL segments, req.query for query strings, and req.body for the parsed JSON payload. Registering express.json() middleware unlocks body parsing. With those three sources understood, building standard CRUD endpoints is a matter of mapping HTTP methods and status codes to the right data operation — POST and 201 for creation, GET for reads, PUT for updates, DELETE with 204 for removal.
Lesson Checkpoint
Review what you learned and get feedback on your work.