Fhir Rest Notes

The FHIR REST Verbs That Actually Differ From Vanilla REST

Every FHIR client team eventually has the same meeting. Someone says "REST is REST, we know REST." Someone else says "but FHIR REST is different." And then they spend a sprint discovering exactly which verbs behave the way they thought and which do not. The good news is the surprises live in a small, well-defined set of interactions.

The site's FHIR RESTful API cheatsheet is the one-pager version of what follows. For the wider context, the FHIR knowledge desk has related material.

POST for Create, Sure, But With a Twist

A vanilla REST POST creates something, and the location comes back in the Location header. FHIR agrees, but the server assigns the id, and if you want to control the id you use a PUT to a client-supplied logical id. Sending POST to a resource type endpoint is create-with-server-assigned-id. Sending PUT to an instance endpoint is create-or-update, called upsert everywhere else.

That distinction — POST for server ids, PUT for client ids — is the first line the vanilla REST intuition trips over.

PUT Is Update, But Also Create

A PUT to a FHIR instance endpoint updates the resource if it exists, or creates it at that id if it does not. Vanilla REST tools sometimes assume PUT will fail on a missing resource. FHIR does not. If you want a strict update, use a conditional update — covered in conditional update: preventing lost writes without pessimistic locking.

Conditional Everything

FHIR turns conditional interactions into first-class citizens:

  • Conditional read via If-None-Match returns 304 when the resource has not changed
  • Conditional read via If-Modified-Since works the same but on time
  • Conditional create via If-None-Exist plus a search query prevents duplicates
  • Conditional update via If-Match uses ETag for optimistic concurrency
  • Conditional delete accepts a search query and removes matches

Vanilla REST supports the headers but almost never wires them up to search semantics. FHIR does, and the wins are real. For the read side, conditional read: If-None-Match and the caching wins it enables is the deep dive.

Search Is Its Own Verb

Search in FHIR is a GET, or a POST if the query is too long or the parameters carry sensitive data. That switch is a common mistake — a client that only knows GET search hits URL-length limits on complex Bundle-style queries and blows up in production long after the demo passed. For the split, GET vs POST search in FHIR: when to switch and why covers the specifics.

Search parameters are also part of the resource type's contract in a way that vanilla REST query parameters are not. They are declared in the CapabilityStatement, they have canonical names, and clients that make up their own parameters get quietly ignored.

Operations Are the Escape Hatch

FHIR reserves the dollar-sign prefix for operations that fall outside CRUD. Bulk data uses one, terminology uses several, patient everything uses one. Operations are POST by convention but can be GET when idempotent. They live at the type or instance level, and the URL space is explicit: something like $expand, $validate, $export. They are the FHIR answer to the vanilla REST hack of routing custom actions through query parameters.

PATCH Is Real, But Uneven

FHIR supports PATCH with JSON Patch, XML Patch, or FHIRPath Patch. Cross-implementation reality is that JSON Patch is most reliable, XML Patch is rare, and FHIRPath Patch is powerful but not universally implemented. For a survey of what actually works, PATCH support in FHIR: what actually works cross-implementation walks through the split.

Bundles Change the Verb Semantics

A transaction Bundle to POST / runs multiple operations atomically. A batch Bundle to the same endpoint runs them independently. Both share a URL and a shape, and only the Bundle.type field decides which. That is the second most common surprise for teams new to FHIR REST. For the atomicity picture, Bundle transactions vs batch: the atomicity boundary is the entry.

The short version: FHIR REST looks like vanilla REST until it doesn't, and the surprises live exactly where the standard says they will. Build the client around the tricky verbs, not around the easy ones. Designing a FHIR client that survives the tricky verbs is the practical follow-up.

Chalk-blackboard diagram of a FHIR REST verb decision tree branching from resource-type endpoints through create, read, update, delete, search, and operation branches, drawn as white chalk sketches with pastel accents on slate

Sources