Route Parameters and Query Strings
URLs carry data to your API in two ways: route parameters (parts of the path) and query strings (key=value pairs after ?). Understanding both is essential for building practical APIs.
Route parameters
Section titled “Route parameters”A route parameter is a named dynamic segment of a URL path, prefixed with ::
app.get('/posts/:id', (req, res) => { console.log(req.params.id) // '42' for GET /posts/42 const id = parseInt(req.params.id, 10) // ...})req.params is always an object of strings — parse numbers explicitly.
Multiple parameters
Section titled “Multiple parameters”app.get('/users/:userId/posts/:postId', (req, res) => { const { userId, postId } = req.params // GET /users/5/posts/12 → { userId: '5', postId: '12' }})Optional parameters
Section titled “Optional parameters”app.get('/posts/:id?', (req, res) => { if (req.params.id) { // get single post } else { // get all posts }})Query strings
Section titled “Query strings”Query strings appear after ? in a URL and carry optional filter/sort/pagination data:
GET /posts?page=1&limit=10&sort=newestAccess them via req.query:
app.get('/posts', (req, res) => { const page = parseInt(req.query.page as string || '1', 10) const limit = parseInt(req.query.limit as string || '20', 10) const sort = (req.query.sort as string) || 'newest'
console.log({ page, limit, sort }) // GET /posts?page=2&limit=5 → { page: 2, limit: 5, sort: 'newest' }})req.query values are strings (or arrays if the same key appears multiple times). Always parse and validate them.
When to use each
Section titled “When to use each”| Use case | Approach |
|---|---|
| Identifying a specific resource | Route param: /posts/:id |
| Filtering a list | Query string: /posts?category=tech |
| Pagination | Query string: /posts?page=1&limit=20 |
| Sorting | Query string: /posts?sort=newest |
| Search | Query string: /posts?q=javascript |
Route params identify what resource you want. Query strings describe how you want it filtered or formatted.
In the Bulletin API
Section titled “In the Bulletin API”// Get a single post by IDapp.get('/posts/:id', (req, res) => { const post = db.prepare('SELECT * FROM posts WHERE id = ?') .get(parseInt(req.params.id)) if (!post) return res.status(404).json({ error: 'Post not found' }) res.json(post)})
// Get posts with optional sortapp.get('/posts', (req, res) => { const sort = req.query.sort === 'top' ? 'upvotes DESC' : 'created_at DESC' const posts = db.prepare(`SELECT * FROM posts ORDER BY ${sort}`).all() res.json(posts)})
// Get comments for a specific postapp.get('/posts/:id/comments', (req, res) => { const comments = db.prepare('SELECT * FROM comments WHERE post_id = ?') .all(parseInt(req.params.id)) res.json(comments)})Exercise
Section titled “Exercise”- Update your
GET /postsroute to support?sort=newest(default) and?sort=oldest— filter the in-memory array accordingly. - Add a
?limit=Nquery parameter that limits how many posts are returned. - Test:
GET /posts?sort=oldest&limit=2. - What does
req.queryreturn when there are no query parameters in the URL?
req.params.namereads dynamic URL segments — always strings, parse numbers explicitly.req.query.namereads query string values — used for filters, sorting, and pagination.- Route params identify a specific resource; query strings describe how to shape the response.
- Always validate and parse query parameters — they come from user input and can’t be trusted.