Sort Order That Survives Resource Updates

Editorial illustration in vaporwave-pastel style depicting two paginated FHIR queries showing unstable _sort=date vs stable _sort=date,_id pagination

FHIR sort is a _sort parameter with a comma-separated list of parameter names, optionally negated with a leading dash for descending order. The mechanic looks simple. It becomes not-simple the moment two resources tie on the sort key and the underlying set changes between page fetches. A page that returns the same ten rows on every call is one thing; a page whose order shifts under concurrent writes is another. The site's FHIR search query auditor flags unstable sort shapes. For the wider FHIR framing, the FHIR comparison hub has more.

The Basic Shape

  • _sort=date sorts ascending by date
  • _sort=-date sorts descending by date
  • _sort=date,category sorts by date then category as a tiebreaker
  • _sort=-date,_id sorts by date desc, then _id as a stable tiebreaker

The rule that saves pagination: always end the sort list with a strictly unique field. _id is the safe choice on every server. That single addition turns "sometimes stable" into "always stable".

The Tie Problem

Two Observations with the same effectiveDateTime are ties on _sort=date. Without a tiebreaker, the server picks an order that may not be repeatable across pages. Page one returns them in order A, B; page two might see them in order B, A, and the client sees resource A twice or misses B entirely.

The fix: _sort=date,_id. The _id is guaranteed unique, so ties resolve consistently. For the pagination side of the same problem, using _count and _offset without losing consistency is the entry.

Not Every Parameter Is Sortable

_sort accepts search parameters that the server has indexed for sorting. Not all search parameters are sortable — reference parameters usually are not, chained parameters usually are not, composite parameters vary by implementation.

If the server does not support sort on a parameter, some return an error, some silently ignore the _sort clause and return an implementation-defined order. The second failure mode is the more dangerous one — the query looks fine, the results are wrong.

Check the CapabilityStatement for searchParam entries with the sortable flag, or test explicitly. For the broader picture of quiet-wrong queries, the FHIR searches that look correct but are wrong covers the pattern.

Sort And Updates

A running query that sorts by effectiveDateTime and a concurrent client that updates one of the returned resources' dates will see:

  • The updated resource move within the sort order
  • Pagination that used offset will see it appear on the new page and possibly skip the resource that took its place
  • Pagination that follows the next cursor may see it once, twice, or not at all depending on the server's cursor semantics

The stable pattern is: sort with _id as the last tiebreaker, follow the next link for pagination, treat pagination as a snapshot not a live query.

Descending Sort And Nulls

Descending sort on a nullable field — _sort=-date where some resources have no date — behaves inconsistently. Some servers put nulls first, some last, some scatter them. Filter with date:missing=false to keep the result set consistent when descending on a nullable element.

Auditor Behavior

The auditor at /dev-tools/audit-fhir-query/ flags sort clauses that end on a non-unique field, sorts on parameters that are not commonly sortable, and combinations with pagination that are likely to produce inconsistent results. For the wider rule set, linter rules to catch the ten most common search mistakes covers them.

The Short Version

Always end _sort with _id. Filter out nulls when sorting descending on a nullable field. Not every parameter is sortable. Pagination is a snapshot, not a live query.

Vaporwave-pastel diagram of two paginated queries — one with unstable sort producing overlapping page contents, one with _id-tiebreak producing consistent pages — on a soft dusk-purple background

Sources