Every team that ships FHIR search queries eventually notices the same pattern: two developers write the same-shaped query with different conventions, and the second one costs a review round. Codifying the conventions is small work up front and saves the round trips permanently. The site's FHIR search query auditor is the public checker; a team-local convention document is what turns it into consistent output. For the wider FHIR framing, the SDC and forms reference collects supporting material.
What A Convention Document Covers
- Parameter naming rules (canonical FHIR names only, no shortcuts)
- Value encoding rules (percent-encode reserved chars at build time)
- Date encoding rules (always UTC with
Z, always second precision or better) - Sort defaults (always end with
_id) - Pagination default (
nextlink, not_offset) - Modifier defaults (
:missingexplicit when relevant,:notguarded) - Error handling (what to do on 400, 404, 412, 422)
- Retry semantics (which codes retry, which do not)
That is eight sections and roughly one page of prose. Every team has them de facto; writing them down is the work.
Format That Actually Gets Read
A convention document in a wiki that nobody opens is not a convention. The formats that actually get used:
- A short markdown file in the repo root, next to the code
- Rules encoded as lint tests that fail CI on violation
- Named helper functions in the client wrapper that enforce the rules by construction
The third is the strongest — a client.searchObservations({subject: patient.id, category: "vital-signs"}) helper that always emits canonical parameter names, encodes values, and adds the standard sort tail can prevent whole classes of mistake without a doc.
Encode the Rules in the Client Wrapper
The pattern that survives team rotation is a thin wrapper around the raw HTTP client that knows the conventions. Callers pass structured parameters, the wrapper emits the compliant query string. That means:
- Reserved-character encoding is centralized
- Date-timezone marker is added automatically
_sortgets_idappended if missing- Pagination uses the
nextlink by default - The wrapper reads
Bundle.linkand provides an iterator that follows it
That is a few hundred lines of code. It saves a large multiple of that in review time and incident time.
Wire the Linter Into CI
Every query string emitted by the codebase should pass the linter in CI. The rules from linter rules to catch the ten most common search mistakes are a solid starting set. The CI check runs the linter against a captured set of production-shaped queries and fails on new violations.
The trap: a linter that runs against test-shaped queries is not the same as one that runs against production-shaped queries. Capture real queries, run the linter, iterate the rules.
Enforce Sort And Pagination Together
Sort and pagination interact. A convention that specifies "always end sort with _id" only pays off if the pagination convention says "follow next, not _offset". For the two mechanics, sort order that survives resource updates and the pagination article are the entries.
What Happens Without Conventions
- Two developers write the same-shape query with different parameter names
- Bug reports blame the server when the query is silently wrong
- Rewrites happen every time a new server implementation is added
- Onboarding takes twice as long
For the class of quiet-wrong queries these conventions prevent, the FHIR searches that look correct but are wrong is the entry.
The Short Version
Write the conventions down, encode them in a client wrapper, wire the linter into CI. Small work, permanent savings.

Sources
- HL7 canonical FHIR Search specification underpinning any - HL7 canonical FHIR Search specification underpinning any team convention
