Back to Blog
Lesson 36 of the Node.js: Build Your First Server & CLI course
Node.jsAugust 24, 20263 min read

Working with Query Parameters: Filtering and Pagination in Express

Master query parameters in Express.js. Learn how to access req.query, implement search filtering, and add pagination to your REST API endpoints.

Node.jsExpressAPIBackendPaginationFiltering
A close-up view of a laptop displaying a search engine page.

Previously in this course, we covered implementing input validation to ensure our data integrity. In this lesson, we shift our focus from validating what clients send to how clients request specific subsets of data using query parameters.

When you build a robust API, you rarely want to return every record in your database at once. Query parameters allow clients to modify their requests, enabling features like search filtering and pagination without needing to create hundreds of individual route paths.

Accessing req.query in Express

In Express, any part of the URL following the ? symbol is automatically parsed into the req.query object. If a user visits /tasks?status=completed&priority=high, your req.query object will look like this:

JAVASCRIPT
{
  status: CE9178">'completed',
  priority: CE9178">'high'
}

Because req.query is a standard JavaScript object, accessing the parameters is as simple as accessing a property. Let’s look at a practical implementation for our running project.

Implementing Simple Search Filtering

Suppose we want to filter our tasks based on a search term. If the user provides a q parameter, we want to filter our database results.

JAVASCRIPT
app.get(CE9178">'/tasks', async (req, res) => {
  const { q } = req.query;
  let query = {};

  if (q) {
    // Case-insensitive search using a Regular Expression
    query.title = { $regex: q, $options: CE9178">'i' };
  }

  const tasks = await Task.find(query);
  res.json(tasks);
});

Here, we check if q exists. If it does, we construct a MongoDB query object that uses $regex to perform a partial match on the title field. This is the foundation of filtering collections in any professional API.

Implementing Basic Pagination

Returning thousands of records will crash your client's browser and strain your database. We implement pagination by using two parameters: limit (how many items to return) and page (which slice of data to return).

JAVASCRIPT
app.get(CE9178">'/tasks', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const skip = (page - 1) * limit;

  const tasks = await Task.find()
    .skip(skip)
    .limit(limit);

  res.json({
    page,
    limit,
    data: tasks
  });
});

By calculating the skip value, we instruct the database to jump over a specific number of documents before returning the requested batch. For more complex requirements, you might explore sorting collections to provide better user control over data ordering.

Hands-on Exercise

Update your existing /tasks GET route to support both filtering and pagination simultaneously.

  1. Capture q, page, and limit from req.query.
  2. Apply the regex filter if q is provided.
  3. Use .skip() and .limit() on your Mongoose model query.
  4. Test your endpoint using Postman with URL: /tasks?q=urgent&page=1&limit=5.

Common Pitfalls

  • Type Coercion: Remember that all values in req.query are strings. Always use parseInt() for numbers like page or limit.
  • Security: Never pass raw user input directly into database queries without sanitization. While Mongoose helps, be wary of "NoSQL Injection" if your filter objects are constructed dynamically from user input.
  • Default Values: Always provide sensible defaults for pagination. If a user provides an invalid page number, the API should gracefully fall back to page 1.

FAQ

What if a parameter isn't provided? The property will be undefined in the req.query object. That's why we use logical checks (like if (q)) or default values (like || 1).

Can I have multiple values for the same key? Yes. If a user sends ?tag=work&tag=urgent, Express will parse req.query.tag as an array: ['work', 'urgent'].

Should I use query parameters for everything? Use query parameters for filtering, sorting, and pagination. Use path parameters (e.g., /tasks/:id) for identifying specific resources. For deeper insights, review the introduction to query parameters.

Recap

We've successfully made our API dynamic by reading from req.query. We implemented a search filter using MongoDB regex and managed result sets using pagination logic. These tools allow your API to scale as your data grows.

Up next: We will dive into Advanced Mongoose Queries to master sorting and population.

Similar Posts