URL Parameters vs Query Strings in Express.js (With Real Examples)
If you've built more than one Express route, you've probably typed req.params and req.query without stopping to ask why one is used here and the other is used there. They both pull data out of a URL, so it's easy to treat them as interchangeable — until your API design falls apart because a "filter" is hard-coded into the route path, or a required resource ID is quietly optional.
This guide breaks down URL parameters and query strings from first principles, shows you exactly how to read them in Express, and gives you a simple rule for deciding which one to reach for.
The URL, Broken Down
Before comparing the two, look at where each one actually lives in a URL:
https://api.example.com/users/42/posts?sort=latest&limit=10
└──┬──┘ └───────┬───────┘
URL Parameter Query String
(:id → "42") (sort=latest&limit=10)
- Everything before the
?is the path — and any dynamic piece of that path is a URL parameter (also called a route parameter). - Everything after the
?is the query string — a set of key-value pairs separated by&.
They sit in different parts of the URL because they serve different purposes.
What Are URL Parameters?
URL parameters (route parameters) are dynamic segments of the path itself. They identify a specific, named resource. In Express, you declare them in your route definition with a colon:
app.get('/users/:id', (req, res) => {
res.send(`Fetching user with ID: ${req.params.id}`);
});
A request to /users/42 matches this route, and Express extracts 42 as req.params.id. Without that segment, the route doesn't match at all — there's no such thing as an "optional" URL parameter unless you explicitly design one.
Think of URL parameters as identifiers. They answer the question: which specific thing am I asking for?
-
/users/42→ the user whose ID is 42 -
/products/laptop-x200→ the product with that slug -
/orders/9981/items/3→ item 3, inside order 9981
What Are Query Parameters?
Query parameters (the query string) come after the ? and are made up of key-value pairs. They don't identify a resource — they modify or filter the request you're already making.
app.get('/products', (req, res) => {
const { category, sort, page } = req.query;
res.send(`Category: ${category}, Sort: ${sort}, Page: ${page}`);
});
A request to /products?category=shoes&sort=price&page=2 gives you:
req.query
// { category: 'shoes', sort: 'price', page: '2' }
Think of query parameters as filters or modifiers. They answer the question: how do I want this data shaped, sorted, or narrowed down?
-
/products?category=shoes→ filter products by category -
/articles?sort=newest&limit=5→ sort and limit results -
/search?q=express+js&page=2→ a search term plus pagination
The Core Difference
| URL Parameter | Query String | |
|---|---|---|
| Location | Part of the path | After the ?
|
| Purpose | Identifies a specific resource | Filters, sorts, or modifies a request |
| Required? | Usually yes — the route won't match without it | Usually optional |
| Express access | req.params |
req.query |
| Example | /users/42 |
/users?role=admin&active=true |
| Analogy | "Which record?" | "How do you want it?" |
The distinction isn't just cosmetic. If something is essential to identifying what you're fetching, it belongs in the path as a parameter. If it's optional, or there could be several combined at once, it belongs in the query string.
Accessing Route Parameters in Express
Express makes this straightforward with req.params. You can define multiple parameters in a single route:
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.send(`User ${userId}, Post ${postId}`);
});
A request to /users/7/posts/103 gives you:
req.params
// { userId: '7', postId: '103' }
Note: all values in req.params are strings. If you're expecting a number, convert it explicitly:
const userId = parseInt(req.params.userId, 10);
Accessing Query Strings in Express
Query parameters are available on req.query, which Express parses automatically from the URL — no extra middleware required:
app.get('/search', (req, res) => {
const { q, page = 1, limit = 10 } = req.query;
res.json({
searchTerm: q,
page: Number(page),
limit: Number(limit)
});
});
A request to /search?q=nodejs&page=2 returns:
{ "searchTerm": "nodejs", "page": 2, "limit": 10 }
Notice the default values for page and limit — this is standard practice, since query parameters are optional by nature. You should always assume a query parameter might not be there and handle that case gracefully.
When to Use Params vs Query
Here's the mental shortcut that removes the guesswork:
Use a URL parameter when the value identifies a specific resource, and the route is meaningless without it.
GET /users/:id → one specific user
GET /orders/:orderId → one specific order
Use a query string when the value filters, sorts, paginates, or optionally modifies a collection of results.
GET /users?role=admin&status=active
GET /orders?sortBy=date&limit=20
A useful test: if you removed this value, would the endpoint still make sense? If removing it breaks the entire meaning of the request (no user ID means no user to fetch), it's a parameter. If removing it just gives you the default, unfiltered view, it's a query string.
You'll often combine both in the same route:
app.get('/users/:id/orders', (req, res) => {
const { id } = req.params; // which user
const { status, sort } = req.query; // how to filter/sort their orders
res.send(`Orders for user ${id}, filtered by ${status}, sorted by ${sort}`);
});
GET /users/42/orders?status=shipped&sort=newest — the user is identified by a parameter, while the filtering is handled entirely through the query string. This is exactly how well-designed REST APIs are structured.
Wrapping Up
URL parameters and query strings both extract data from a URL, but they're not interchangeable:
-
URL parameters (
req.params) identify what resource you're working with. They live in the path and are usually required. -
Query strings (
req.query) describe how you want that resource — filtered, sorted, paginated. They live after the?and are usually optional.
Once you internalize "params identify, query modifies," designing clean, predictable Express routes becomes second nature — and your API consumers will thank you for it.
Found this useful? Follow for more practical Node.js and Express.js breakdowns.
Top comments (0)