Filtering, Sorting, and Search
Objectives
By the end of this lesson, you should be able to:
- Filter a collection endpoint by an exact field value
- Sort a collection by any field, ascending or descending
- Add a simple text search across a field, and combine it with filtering and sorting
💡 Why this matters: A real client rarely wants an entire collection, it wants products in one category, sorted cheapest first, or every result matching a search term. Query parameters (Module 5) are the standard way a REST API exposes this.
⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.
Filtering by an Exact Value
app.get('/products', (req, res) => {
let results = [...products];
if (req.query.category) {
results = results.filter(p => p.category === req.query.category);
}
res.json(results);
});
curl "http://localhost:4505/products?category=accessories"
[{"id":1,"name":"Keyboard","category":"accessories","price":45},{"id":3,"name":"Mechanical Keyboard","category":"accessories","price":89},{"id":4,"name":"Webcam","category":"accessories","price":60}]
[...products] (Module 2’s spread) copies the array first, so filtering never mutates the original data, req.query.category, only applied if it’s present, means the endpoint still returns everything when no filter is requested, filtering stays entirely optional.
Simple Text Search
if (req.query.q) {
const term = req.query.q.toLowerCase();
results = results.filter(p => p.name.toLowerCase().includes(term));
}
curl "http://localhost:4505/products?q=key"
[{"id":1,"name":"Keyboard","category":"accessories","price":45},{"id":3,"name":"Mechanical Keyboard","category":"accessories","price":89}]
q (a common, short convention for a search query parameter) is lowercased on both sides (.toLowerCase(), Module 2’s string methods) before comparing with .includes(), making the search case-insensitive, "key" correctly matches both "Keyboard" and "Mechanical Keyboard".
Sorting
if (req.query.sort) {
const desc = req.query.sort.startsWith('-');
const field = desc ? req.query.sort.slice(1) : req.query.sort;
results.sort((a, b) => {
if (a[field] < b[field]) return desc ? 1 : -1;
if (a[field] > b[field]) return desc ? -1 : 1;
return 0;
});
}
curl "http://localhost:4505/products?sort=price"
[{"id":1,"name":"Keyboard","price":45,...},{"id":4,"name":"Webcam","price":60,...},{"id":3,"name":"Mechanical Keyboard","price":89,...},{"id":2,"name":"Monitor","price":210,...}]
curl "http://localhost:4505/products?sort=-price"
[{"id":2,"name":"Monitor","price":210,...},{"id":3,"name":"Mechanical Keyboard","price":89,...},{"id":4,"name":"Webcam","price":60,...},{"id":1,"name":"Keyboard","price":45,...}]
A leading - on the sort value is a common convention for descending order, sort=price sorts ascending, sort=-price sorts descending, this reads naturally (-price as “negative price,” conceptually “highest first”) and avoids needing a second query parameter just for direction.
Combining Filtering, Search, and Sorting
curl "http://localhost:4505/products?category=accessories&sort=-price"
[{"id":3,"name":"Mechanical Keyboard","price":89,...},{"id":4,"name":"Webcam","price":60,...},{"id":1,"name":"Keyboard","price":45,...}]
Because each feature checks its own req.query field independently, and reassigns the same results variable, they compose naturally, filtering by category, then sorting the filtered results, exactly as shown, no special coordination code needed between them.
Try It
- Add filtering by an exact field (like
categoryabove) to a collection endpoint of your own, and test it with and without the filter applied. - Add a
qsearch parameter searching across a text field, case-insensitively, and confirm it matches partial, differently-cased input correctly. - Add
sortsupport, including the-fielddescending convention, and confirm both directions produce correctly ordered results. - Combine all three (filter, search, sort) in a single request, and confirm the result is correct for that combination.
Recap
- Filtering checks a query parameter against an exact field value, applied only when the parameter is present, keeping it optional.
- A
qparameter, lowercased and matched with.includes(), provides simple, case-insensitive text search. sort=fieldsorts ascending,sort=-field(a leading-) sorts descending, a common, self-documenting convention.
Next lesson: REST best practices, tying every convention in this module together.