Fhir Rest Notes

Designing a FHIR Client That Survives the Tricky Verbs

The FHIR REST client that survives production is the one built around the tricky verbs, not the easy ones. GET a resource is a copy of any other REST GET. Conditional update, transaction Bundle, PATCH with format negotiation, POST search — those are the interactions that decide whether the client is durable or brittle. Designing for them from day one is cheaper than retrofitting.

The site's FHIR RESTful API cheatsheet is the reference the design leans on. For the wider context, the FHIR reference shelf has related pieces.

Read the CapabilityStatement Once, Cache Everything

The CapabilityStatement is the client's map of what the server actually supports. Read it at startup, cache the answer, and dispatch behavior against the cached view:

  • Which PATCH formats the server accepts
  • Whether the server supports transaction Bundles or batch only
  • Which search parameters are declared per resource type
  • Whether conditional interactions are supported

Clients that never read the CapabilityStatement re-invent this every incident.

Wire the ETag Cache From the Start

Every read stashes the returned ETag against the resource id and version. Every write that follows uses If-Match with that ETag. Every 412 triggers a re-read plus retry. Building that around a single client object turns concurrency safety into a default rather than an opt-in. For the mechanic, conditional update: preventing lost writes without pessimistic locking is the deep dive.

Skip this and every write is a lost-write vulnerability waiting to bite.

Automate the GET vs POST Search Switch

The client should have a single search entry point that inspects the constructed URL length and body size, and switches transport automatically. The threshold is server-side and configurable, and defaults around eighteen hundred characters are safe. Callers do not choose the transport; they call search() and the client decides.

Model Transaction and Batch Explicitly

Two distinct methods, two distinct semantic contracts. Do not overload one Bundle-send method with a runtime type flag. The transaction method signals atomic intent; the batch method signals independent intent. The compiler catches the confusion at call site rather than the server catching it at runtime.

Retry With Backoff Only on Specific Codes

Retry logic that fires on any 5xx is dangerous — it can amplify server incidents and it can commit non-idempotent operations twice. Retry on 429 with the server's Retry-After. Retry on 503 with exponential backoff and a low ceiling. Do not retry on 500 automatically; escalate it. For the status coding picture, HTTP status codes FHIR reuses vs redefines covers the FHIR-specific reads.

Parse OperationOutcome Even on 200

The OperationOutcome resource is where FHIR puts diagnostics. Warnings on a 200, structured errors on a 400, expression pointers on validation failures. A client that only inspects the status code loses that information. Parse the body when it is FHIR-shaped, and surface warnings up the call chain, not just errors.

Do Not Hide Server Ids

Every resource has a logical id and a version id. The client should surface both to callers. Hiding them behind opaque handles makes conditional interactions harder and audit trails muddier. The extra field on the response object is a small price for a clean interoperability surface.

Test the Bundle Round Trip

The Bundle interaction is where the most subtle bugs live. Reference rewriting on transaction Bundles, per-entry status parsing on batch Bundles, and OperationOutcome collection on failed transactions all need integration tests against a real server, not just contract tests against a mock. For the atomicity story, Bundle transactions vs batch: the atomicity boundary is the reference.

The Short Version

Design the client around the tricky verbs, cache the CapabilityStatement, wire ETag from day one, keep transaction and batch semantically distinct, and parse OperationOutcome everywhere. The verbs that differ from vanilla REST are covered in the FHIR REST verbs that actually differ from vanilla REST, and the cheatsheet at the FHIR RESTful API cheatsheet is the one-pager the client team keeps open.

Chalk-blackboard diagram of a FHIR client architecture with CapabilityStatement cache, ETag store, search transport switcher, and transaction vs batch dispatch as separate modules, drawn as white chalk sketches with pastel accents on slate

Sources