PATCH is the FHIR verb with the widest gap between what the spec says and what servers actually do. A client that sends a JSON Patch to one server and an XML Patch to another and a FHIRPath Patch to a third will produce three different behaviors. Knowing what actually works cross-implementation saves the client team a lot of surprise.
The site's FHIR RESTful API cheatsheet marks PATCH support explicitly. For the wider REST context, our FHIR coverage has related pieces.
The Three Patch Formats
FHIR PATCH supports three body formats:
- JSON Patch, using the standard IETF format with operations like replace, add, remove
- XML Patch, the XML sibling with equivalent semantics
- FHIRPath Patch, which uses FHIRPath expressions to target and modify content
Each has a Content-Type. Each has different server support. Each has different edge-case behavior.
What Actually Works
JSON Patch is the most reliable cross-implementation. Most major FHIR servers support it, and the behavior across implementations is close enough that a well-written client can rely on it. Start there.
XML Patch is technically supported by most servers but rarely used in practice. There is no strong reason to prefer it unless the calling application is already XML-native, and even then the ecosystem has drifted toward JSON.
FHIRPath Patch is powerful and expressive, and support is uneven. Some reference implementations have it, some do not, and behavior on the edge cases — repeated elements, extensions, deep paths — varies. Use it when the target server documents FHIRPath Patch specifically and when JSON Patch is too clunky for the change you need. Otherwise start with JSON Patch.
Test the CapabilityStatement, Do Not Assume
The FHIR CapabilityStatement declares which PATCH formats a server supports. Every serious client should read it once at startup and dispatch accordingly. The reality is that some servers under-declare and some over-declare, so a test suite that actually issues a small PATCH per format per server is worth building once.
PATCH Plus Conditional Update
The concurrency story is the same as with PUT. If-Match on the PATCH request gets you optimistic concurrency; the 412 recovery path is the same. For that pattern, conditional update: preventing lost writes without pessimistic locking is the deep dive.
Never issue a PATCH without an If-Match unless the caller has explicitly opted into last-write-wins semantics. The default has to be safe.
Where PATCH Beats PUT
The PATCH win is bandwidth and semantic clarity. Updating a single extension on a Patient with PUT means reading the whole resource, modifying the extension, and writing the whole resource back. PATCH updates the extension directly. Round trip is smaller, the change intent is explicit, and audit logs record what changed rather than the full replaced resource.
Where PATCH loses is on structural changes that cross multiple deep paths. Then a PUT with the full resource is often simpler and safer.
The Trap: Silent Non-Support
A server that does not support the PATCH format you send may return an OperationOutcome, may return a 400, may accept the PATCH as if it were a PUT, or may silently drop the operations that it does not understand. All four are in the wild. Do not trust a 200 without inspecting the resource state after.
Test the write. Read the resource back if audit assurance matters.
Where This Fits
For the survey of REST verbs, the FHIR REST verbs that actually differ from vanilla REST is the entry. For the client-shape implications, designing a FHIR client that survives the tricky verbs is where these details land.
Sources
- HL7 canonical section on PATCH interactions in FHIR - HL7 canonical section on PATCH interactions in FHIR