Skip to content

Filtering & Sorting

Filtering narrows a collection to the rows a client cares about; sorting decides their order. Both are query parameters on the collection, and a little convention keeps them readable.

The simplest, most discoverable form is one query parameter per field:

GET /articles?status=published&author=42

For ranges and operators, a common convention is a bracketed or suffixed operator:

GET /articles?createdAt[gte]=2026-01-01&views[gt]=1000

Keep the operator vocabulary small and documented (gte, lte, gt, lt, ne, in). Whatever you pick, apply it the same way on every collection.

Accept a sort parameter listing fields, with a leading - for descending:

GET /articles?sort=-createdAt,title

That reads as “newest first, then by title ascending”. Define which fields are sortable (sorting on an unindexed column can be expensive) and reject the rest with 400.

JavaScript
Advantages (Filtering & Sorting)Trade-offs
Client fetches only what it needs — smaller payloadMany query parameters make URLs long and hard to read
Server-side filtering beats filtering all the data client-sideComplex filters need a query language (OData, custom)
Server-side sort uses a database index — faster than sorting in memoryFilter/sort parameters must be validated to prevent SQL injection
Filter and sort combine in a single requestMulti-field sort makes the URL more complex

Not validating filter parameters Symptom:

  • GET /users?sort=; DROP TABLE users--
  • SQL injected through the sort parameter
  • Allowlist the fields that can be filtered/sorted: if (!['name','email','createdAt'].includes(sort)) throw

Expensive filters with no index Symptom:

  • GET /orders?status=pending — runs WHERE status = 'pending' on a table with millions of rows
  • No index on the status column — a full table scan on every request
  • Add an index on frequently filtered fields, or restrict which filters are supported

💡 Real-world examples

GitHub API:

  • GET /repos/\{owner\}/\{repo\}/issues?state=open&labels=bug&sort=created&direction=desc
  • Multi-dimensional filter and sort in a single request

Stripe API:

  • GET /charges?created[gte]=1609459200&created[lte]=1640995199&limit=100
  • Range filter via bracket notation — filters between two dates

Sources: GitHub REST API — issues, Stripe API — pagination and filtering

What does `?sort=-createdAt,title` mean?
Why allowlist filterable and sortable fields?
Where do filter and sort parameters belong?