Route Parameters & Query Strings — Order ID Dhoondo
URLs can carry data through route parameters (/biryani/2) and query strings (/biryani?spice=High). Learn how to read both in Express.
Route parameters are named segments in the URL that capture dynamic values. Think of them like Swiggy order IDs — each order has a unique number, and you use that number to look up the order.
Syntax: /biryani/:id — the colon (:) tells Express to capture whatever value is at that position.
import express from 'express';
const app = express();
app.use(express.json());
let biryaniMenu = [
{ id: 1, name: 'Chicken Biryani', price: 250, spice: 'Medium' },
{ id: 2, name: 'Mutton Biryani', price: 350, spice: 'High' },
{ id: 3, name: 'Veg Biryani', price: 200, spice: 'Low' }
];
// GET /biryani/2 — fetch biryani with id = 2
app.get('/biryani/:id', (req, res) => {
const id = parseInt(req.params.id); // Convert string to number!
const biryani = biryaniMenu.find(item => item.id === id);
if (!biryani) {
return res.status(404).json({
success: false,
message: 'Biryani not found! Yeh ID exist nahi karti bhai!'
});
}
res.json({ success: true, data: biryani });
});
app.listen(3000);
Testing:
GET /biryani/1→ Returns Chicken BiryaniGET /biryani/2→ Returns Mutton BiryaniGET /biryani/99→ Returns 404 (not found)
Important: req.params.id is always a string — even if the URL has a number! Use parseInt() or Number() to convert before comparing with numeric IDs.
Query strings come after the ? in the URL and are used for filtering, sorting, pagination — not for identifying specific resources.
Syntax: /biryani?spice=High&sort=price
Query strings are key-value pairs separated by &. In Express, they're available at req.query as an object.
import express from 'express';
const app = express();
let biryaniMenu = [
{ id: 1, name: 'Chicken Biryani', price: 250, spice: 'Medium' },
{ id: 2, name: 'Mutton Biryani', price: 350, spice: 'High' },
{ id: 3, name: 'Veg Biryani', price: 200, spice: 'Low' },
{ id: 4, name: 'Prawn Biryani', price: 400, spice: 'High' }
];
// GET /biryani?spice=High — filter by spice level
app.get('/biryani', (req, res) => {
let result = biryaniMenu;
// Filter by spice level
if (req.query.spice) {
result = result.filter(item =>
item.spice.toLowerCase() === req.query.spice.toLowerCase()
);
}
// Filter by max price
if (req.query.maxPrice) {
result = result.filter(item =>
item.price <= parseInt(req.query.maxPrice)
);
}
res.json({
success: true,
count: result.length,
filters: {
spice: req.query.spice || 'all',
maxPrice: req.query.maxPrice || 'none'
},
data: result
});
});
app.listen(3000);
Testing:
GET /biryani?spice=High→ Returns only High spice biryanisGET /biryani?maxPrice=250→ Returns biryanis 250 or lessGET /biryani?spice=High&maxPrice=350→ Combined filters!
Query strings are optional — the route still works without them (req.query is just an empty object).
req.params is an object containing all route parameters defined in the URL pattern. Each colon-prefixed segment becomes a key in this object.
How Express parses params:
// Route pattern: /biryani/:id
// Actual URL: /biryani/42
// req.params: { id: '42' }
// Multiple params: /restaurant/:restId/menu/:itemId
// Actual URL: /restaurant/5/menu/12
// req.params: { restId: '5', itemId: '12' }
Using params in a route:
app.get('/biryani/:id', (req, res) => {
// req.params.id is a string — convert to number!
const id = Number(req.params.id);
// Find the item
const item = menu.find(b => b.id === id);
if (!item) {
return res.status(404).json({ error: 'Not found' });
}
res.json({ data: item });
});
Common mistakes:
| Mistake | Wrong | Right |
|---|---|---|
| Forgetting parseInt | item.id === req.params.id | item.id === parseInt(req.params.id) |
| Wrong param name | URL: /biryani/:id, code: req.params.biryaniId | req.params.id matches :id |
| Missing 404 | Send empty response when not found | Return res.status(404).json(...) |
Param name rule: The name after the colon in the route pattern MUST match the key in req.params. If your route is /biryani/:biryaniId, use req.params.biryaniId.
req.query is an object containing all query string parameters from the URL. Express automatically parses the query string for you — no manual parsing needed!
How URLs break down:
Full URL: http://localhost:3000/biryani?spice=High&sort=price:asc
|_____________| |_____| |________________________|
Base URL Path Query String
Query string: spice=High&sort=price:asc
| | | |
key val key val
req.query: { spice: 'High', sort: 'price:asc' }
Practical example — filtering and sorting:
app.get('/biryani', (req, res) => {
let result = [...biryaniMenu];
// Filter by spice
if (req.query.spice) {
result = result.filter(b =>
b.spice.toLowerCase() === req.query.spice.toLowerCase()
);
}
// Sort by price
if (req.query.sort === 'asc') {
result.sort((a, b) => a.price - b.price);
} else if (req.query.sort === 'desc') {
result.sort((a, b) => b.price - a.price);
}
res.json({ count: result.length, data: result });
});
Testing query combinations:
curl "http://localhost:3000/biryani?spice=High&sort=asc"
Note: Query strings are always strings. If you expect a number, use parseInt() or parseFloat(). Boolean values come as strings ('true', 'false').
Route params and query strings often work together. Params identify which resource, queries control how to present it.
Combined example — get one biryani with optional fields:
app.get('/biryani/:id', (req, res) => {
const id = Number(req.params.id);
const biryani = biryaniMenu.find(b => b.id === id);
if (!biryani) {
return res.status(404).json({ error: 'Not found' });
}
// Optional: ?include=recipe adds extra data
if (req.query.include === 'recipe') {
return res.json({
...biryani,
recipe: 'Basmati rice, chicken, spices, saffron, ghee...'
});
}
res.json({ data: biryani });
});
// Example: GET /biryani/1?include=recipe
Real Swiggy-like example — paginated menu with filters:
// GET /restaurant/5/menu?category=Biryani&page=1&limit=10
app.get('/restaurant/:restId/menu', (req, res) => {
const restId = Number(req.params.restId);
const page = Number(req.query.page) || 1;
const limit = Number(req.query.limit) || 10;
const category = req.query.category;
// Find restaurant
const restaurant = restaurants.find(r => r.id === restId);
if (!restaurant) {
return res.status(404).json({ error: 'Restaurant not found' });
}
// Filter menu
let items = restaurant.menu;
if (category) {
items = items.filter(item =>
item.category.toLowerCase() === category.toLowerCase()
);
}
// Paginate
const start = (page - 1) * limit;
const paginatedItems = items.slice(start, start + limit);
res.json({
restaurant: restaurant.name,
page,
totalItems: items.length,
totalPages: Math.ceil(items.length / limit),
data: paginatedItems
});
});
// Example: GET /restaurant/5/menu?category=Biryani&page=1&limit=10
This pattern — params for resource identification + queries for filtering/pagination — is standard in all production REST APIs, including Swiggy's.
Key Takeaways
- ✅ Route params (/biryani/:id) capture dynamic URL segments — req.params.id gives the value.
- ✅ req.params values are ALWAYS strings — use parseInt() or Number() to convert.
- ✅ Query strings (/biryani?spice=High) are for filtering/pagination — req.query.spice gives the value.
- ✅ Query strings are optional — req.query is {} if none provided.
- ✅ The param name in the route (:id) must exactly match req.params.id.
- ✅ Always handle 404 when a resource with the given ID is not found.
- ✅ Combine params (identify resource) + query (filter/present) for full REST APIs.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login