Card 07/ 11

ConceptDifficulty: Intermediate2 min

Filtering, Sorting and Searching With Query Parameters

The path names the collection, and the query string narrows, orders and searches it.

Filtering, sorting and searching a collection belong in the query string, the part of the URL after ?, because they describe which members of one resource to return, not a different resource. The path names the collection; parameters such as status=open, sort=-createdAt and q=blue pen narrow it, order it and search it.

What a client wants from the orders collection, and the query string that expresses it
IntentQuery string
Open orders onlyGET /orders?status=open
Open or held ordersGET /orders?status=open,held
Totals of at least 100GET /orders?minTotal=100
Newest firstGET /orders?sort=-createdAt
By customer name, then oldest firstGET /orders?sort=customerName,createdAt
Orders mentioning "blue pen"GET /orders?q=blue%20pen
Open orders over 100, newest firstGET /orders?status=open&minTotal=100&sort=-createdAt

Filters by field name. A parameter named after the field it filters, status=open, needs no documentation to read. Ranges need a convention; minTotal and maxTotal are plain, and some APIs use total[gte]=100. Pick one style and use it everywhere.

One sort parameter. A comma-separated list of fields, each optionally prefixed with - for descending, covers almost every case. Without a sort, say what the default order is, because clients will depend on it.

Search is a separate parameter. q for free text tells the client it is a fuzzy match, unlike filters, which are exact.

Why not the path. GET /orders/open/100/newest turns optional criteria into required segments in a fixed order. Adding a criterion changes every path, and /orders/open now looks like an order with id "open".

Limits. Document which fields may be filtered and sorted on, and reject unknown ones with a 400, so a typo such as stauts=open is not silently ignored and the client does not receive every order. Searches too large for a URL belong in a request body, with QUERY or POST.

So let the path say which collection, and the query string say which members and in what order.