2. Endpoints
Routes#
SV2.1 The following routes MUST be served.
{version}is a FHIR version namespace (r3,r4,r5);{rtype}is a resource type name.Route Methods /{version}/metadataGET/{version}/{rtype}GET(search),POST(create)/{version}/{rtype}/{id}GET,PUT,DELETE/{version}/{rtype}/{id}/_historyGET/{version}/{rtype}/{id}/_history/{vid}GET(vread)SV2.2 A request naming a version this process has not mounted MUST say which versions are mounted. "Not found" for an unmounted version is indistinguishable from a typo in the resource type, and the two have different fixes.
Content type#
- SV2.3 Responses carrying FHIR content MUST use
application/fhir+json. Plainapplication/jsonis wrong enough that conformance tooling rejects it.
Status codes#
SV2.4 These distinctions MUST each be preserved. They are the reason this crate exists as a translation layer rather than a thin proxy: the store draws every one of them, and losing one at the HTTP boundary discards information the store went to trouble to keep.
Code Means Must not be confused with 200read, search, history, vread succeeded 201created, with the server-assigned id 200— a client cannot tell whether it created204deleted 200with an empty body400the request is malformed 500— this is the client's fault and is safe to describe401no acceptable credential ( SV3)403, which this crate does not issue: it does not authorize404never existed 410410existed and was deleted 404— the sharpest of these. A client resolving a dangling reference needs to know the resource was deleted rather than mistyped, and a404invites a retry that will never succeed412If-Matchdid not match the stored version409500this server's fault 400503the store is unreachable 500— one is retryable, the other is notSV2.5
If-MatchMUST be honoured onPUT, and MUST take a weak ETag (W/"3"), which is what FHIR specifies. A header that is present but unparseable MUST be an error rather than ignored: silently dropping a precondition turns an optimistic-concurrency check into an unconditional write, which is the failure the header exists to prevent.
Errors#
SV2.6 An error response MUST carry an
OperationOutcome.SV2.7 An
OperationOutcomeMUST NOT echo a submitted value, and MUST NOT disclose stored data or internal structure. It describes the request.Restates
A7.11, retired with §7 and registered inC0.16. The store follows the same rule for itsUnsupportederror, and the reason is identical: an error is the one response an unauthenticated caller can reliably provoke, so it is the cheapest oracle in the system.
CapabilityStatement#
SV2.8
GET /{version}/metadataMUST return aCapabilityStatementgenerated from the mounted store's relational map, naming the FHIR version it actually serves.Restates
A7.12(C0.16).SV2.9 It MUST declare every interaction the router serves, and no others. Both directions are defects:
- Declaring more than is served makes a client attempt what will fail.
- Declaring less makes a conformance-driven client never attempt what would have worked.
The second is what happened: this crate advertised
read,vreadandsearch-typewhile the router had carriedPOST,PUTandDELETEsince it was written (F-57).A7.12is normally read as "do not declare what you cannot do", and every check written for it looked only for over-claiming.SV2.10 A test MUST assert the correspondence between the declared interactions and the routed methods. Neither side's own tests can catch a disagreement: each is self-consistent while contradicting the other, which is the shape
U11anames in the database specification.Met by
metadata_declares_every_interaction_the_router_serves.SV2.11
software.nameMUST name this crate. It readfhir-storeuntil 2026-08-03 — the name of the engine-agnostic persistence core after the split (F-45) — so every CapabilityStatement this service emitted identified itself as a different piece of software.
Search#
SV2.12
_count,_offsetand_totalare result parameters and MUST be treated as such; every other query parameter is a search parameter.SV2.13 A search parameter the store does not support MUST be refused, naming the parameter, rather than silently ignored. A filter that is dropped returns more results than asked for, and the caller cannot tell.
Not implemented#
SV2.14
If-None-Existconditional create is not served, and the capability exists: the store implementsconditional_create_audited. This is a route away from working. RestatesA7.10(C0.16); tracked as F-58.SV2.15
$export(Bulk Data) is not served. RestatesM8(C0.16), and it is one of the three §13 compliance rows that depends on this crate.
Part of the fhir-loco specification.