Skip to main content
Madhukar
All Articles

URL Parameters vs Query Strings in Express.js

August 3, 20265 min read
Express.jsNode.jsBackendApi Design
URL Parameters vs Query Strings in Express.js

The same URL, two very different jobs

Look at this URL: /users/42?sort=name&order=asc. Two very different things are happening inside it. 42 identifies which user we're talking about. sort=name&order=asc describes how the response should be shaped — sorted a certain way. Confusing these two — treating an identifier like a filter, or a filter like an identifier — is one of the most common early mistakes when building routes in Express. This guide draws a clear, practical line between them.

What URL Parameters Are

A URL parameter (often called a route parameter) is a named segment built directly into a route’s path, used to identify a specific resource.

/users/:id

Here, :id is a placeholder — when a request comes in for /users/42, Express captures 42 as the value of id. Think of URL parameters as identifiers: they answer the question "which specific thing are we talking about?"

/users/42        → which user
/products/17 → which product
/orders/1042 → which order

What Query Parameters Are

A query parameter (or query string) is the part of a URL that comes after a ?, made up of key-value pairs, used to filter, sort, or modify a request — not to identify a specific resource.

/users?sort=name&order=asc

Here, sort=name and order=asc are query parameters, separated by &. Think of query parameters as filters or modifiers: they answer the question "how should this response be shaped?" — not "which specific thing."

/products?category=electronics&maxPrice=500
/search?q=laptop&page=2

Differences Between Them

A simple mental test: if removing the value would make the route meaningless (you can’t fetch “a user” without knowing which user), it’s a URL parameter. If removing it just means “return the default, unfiltered version” (all users, in default order), it’s a query parameter.

Accessing Params in Express

Express captures URL parameters using a colon (:) in the route definition, and makes them available on req.params:

app.get("/users/:id", (req, res) => {
const userId = req.params.id;
res.send(`Fetching user with ID: ${userId}`);
});

Requesting /users/42 results in req.params.id being "42".

Multiple URL parameters

A route can define more than one parameter at once:

app.get("/users/:userId/orders/:orderId", (req, res) => {
const { userId, orderId } = req.params;
res.send(`User ${userId}, Order ${orderId}`);
});

Requesting /users/42/orders/1042 gives you req.params.userId === "42" and req.params.orderId === "1042".

Accessing Query Strings in Express

Query parameters don’t need to be declared in the route path at all — Express automatically parses anything after ? into req.query:

app.get("/users", (req, res) => {
const { sort, order } = req.query;
res.send(`Sorting by ${sort}, order: ${order}`);
});

Requesting /users?sort=name&order=asc results in req.query.sort === "name" and req.query.order === "asc".

Handling optional query parameters

Since query parameters are typically optional, it’s common to provide sensible defaults when they’re missing:

app.get("/products", (req, res) => {
const sort = req.query.sort || "default";
const page = req.query.page || 1;
res.send(`Sorting: ${sort}, Page: ${page}`);
});

Requesting just /products (with no query string at all) still works fine, falling back to the defaults.

When to Use Params vs Query

Use a URL parameter when identifying a specific resource

app.get("/users/:id", ...)          // one specific user
app.get("/products/:id", ...) // one specific product
app.get("/orders/:orderId", ...) // one specific order

If the route doesn’t make sense without a specific value, it belongs in the path as a parameter.

Use a query string when filtering, sorting, or paginating

app.get("/users", ...)     // ?role=admin&status=active
app.get("/products", ...) // ?category=electronics&maxPrice=500&sort=price
app.get("/search", ...) // ?q=laptop&page=2

If the route still makes complete sense without the value — “give me all products,” just unfiltered — it belongs as an optional query parameter.

A practical example combining both

app.get("/users/:id/orders", (req, res) => {
const userId = req.params.id; // which user
const status = req.query.status; // optional filter: only their pending orders, for example

res.send(`Orders for user ${userId}, filtered by status: ${status || "all"}`);
});

Requesting /users/42/orders?status=pending combines both naturally: 42 identifies whose orders, and status=pending filters which of that user's orders to return.

Final Takeaway

URL parameters and query strings both live in the same URL, but they answer two genuinely different questions. A URL parameter says “this route is about this specific thing” — a user, a product, an order — and belongs directly in the path, accessed through req.params. A query string says "shape the response this particular way" — sorted, filtered, paginated — and belongs after the ?, accessed through req.query, typically with sensible defaults for when it's left out. Getting this distinction right from the start makes route design in Express feel obvious instead of arbitrary.

Frequently Asked Questions

Can a single route use both URL parameters and query strings together?

> Yes, and it’s extremely common — a URL parameter identifies the specific resource (like a user), while a query string filters or modifies something about that resource’s related data (like their orders), exactly as shown in the combined example above.

Are URL parameters always required, and query strings always optional?

> As a strong convention, yes — but it’s not a strict technical rule. The practical guideline is: if a route is meaningless without the value, make it a URL parameter; if the route still works sensibly without it (just less filtered), make it a query parameter with a default.

What type is req.params.id — a number or a string?

> Always a string, even if it looks like a number in the URL. If you need to perform numeric operations on it, convert it explicitly (for example, with Number(req.params.id)) before using it.

What happens if I request a route without providing an expected query parameter?

> req.query simply won't include that key at all — accessing it returns undefined, which is exactly why providing a fallback default value (as shown in the pagination example) is a common, sensible practice.

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.