feat(client): conditional create via If-None-Exist - #151
Merged
Conversation
…cond resource `create()` could only POST unconditionally, so every caller that needs an idempotent write had to drop out of the client and hand-roll a fetch to set `If-None-Exist` — which is how a third copy of a FHIR writer gets written. It is now an option on `create()`: pass the search string and the server creates only when nothing matches, returning the existing resource otherwise. The header takes the raw search, deliberately not url-encoded, because encoding the `|` makes the search match nothing and silently turns every retry into another copy. A match is allowed to answer 200 with no body, where the id exists only in `Location`. Parsing the body alone would throw exactly on the idempotent path, so the response reader falls back to `Location` and errors only when neither carries an id. Additive: `create(resource)` is unchanged, and the three clients are regenerated from the template rather than edited.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds conditional create to
FhirResourceWriter:The server creates the resource only when that search matches nothing, and returns the existing one otherwise.
create(resource)is unchanged, so this is purely additive.Why
Without it, any caller that needs an idempotent write has to leave the client and hand-roll a fetch just to set one header. That is how a repo ends up with its own FHIR writer sitting next to this one.
The concrete case: maxhealth.tech provisions a member into FHIR as a resumable job (Person → Patient → Consent → Coverage). Every write has to be safe to retry, because the job runs again after any failure — and "one human, one Person" is enforced by nothing except the conditional create. Running that job twice must produce one Person, not two.
Two details worth reviewing
The header takes the raw search, not url-encoded.
If-None-Existis a header, not a query string, and HAPI parses it raw. Encoding the|makes the search match nothing, which silently turns a conditional create into an unconditional one — every retry adds another copy. Documented on the option and asserted in a test.A match may answer 200 with no body. The server has nothing new to report, and the id then exists only in
Location. Reading the body alone would throw exactly on the idempotent path, so the response reader falls back toLocationand errors only when neither carries an id.Changes
scripts/client-codegen/template/writer.ts—CreateOptions.ifNoneExist, plus theLocationfallbackscripts/client-codegen/template/writer.test.ts— 5 new cases;mockFetchnow servestextandheadersas well asjsonpackages/client-{r4,r4b,r5}/src/writer{,.test}.ts— regenerated, not editedVerification
npm run generate:clients:checkis clean, so the generated files match the templateThe four
TS2307: Cannot find module '@babelfhir-ts/smart-auth'errors fromtsc --noEmitinclient-r4are present identically on a cleandevelop— the repo declares no npmworkspaces, so a root install never links the sibling packages. Unrelated to this change.