Conditional Create (If-None-Exist)¶
Conditional create lets you create a resource only if no matching resource exists. This is the core primitive for idempotent writes.
Basic usage¶
from pymedplum.fhir import Patient
patient = Patient(
identifier=[{"system": "http://hospital.org/mrn", "value": "MRN-12345"}],
name=[{"family": "Smith", "given": ["John"]}],
)
resource = client.create_resource_if_none_exist(
patient,
if_none_exist="identifier=http://hospital.org/mrn|MRN-12345",
)
print(f"Patient ID: {resource['id']}")
Notes:
- The return value is the resource (dict or model), not the HTTP status. The server may return 201 (created) or 200 (existing).
if_none_existaccepts:- a plain query string (
"identifier=system|value") - a leading
?(stripped automatically) - a full URL (query portion extracted)
Common patterns¶
Prevent duplicate patient records¶
def ensure_patient_exists(mrn: str, name: dict) -> dict:
patient = Patient(
identifier=[{"system": "http://hospital.org/mrn", "value": mrn}],
name=[name],
)
return client.create_resource_if_none_exist(
patient,
if_none_exist=f"identifier=http://hospital.org/mrn|{mrn}",
)
Reference data¶
def ensure_organization_exists(npi: str, name: str) -> dict:
org = {
"resourceType": "Organization",
"identifier": [{"system": "http://hl7.org/fhir/sid/us-npi", "value": npi}],
"name": name,
}
return client.create_resource_if_none_exist(
org,
if_none_exist=f"identifier=http://hl7.org/fhir/sid/us-npi|{npi}",
)
Error handling¶
If multiple resources match, the server returns HTTP 412 (Precondition Failed):
from pymedplum.exceptions import PreconditionFailedError
try:
client.create_resource_if_none_exist(
patient,
if_none_exist="family=Smith",
)
except PreconditionFailedError:
print("Search criteria matched multiple resources")
Bulk conditional creates¶
conditional_create_batch creates many resources idempotently in one
call: it builds type: batch bundles of POST entries carrying
ifNoneExist, chunked (default 50 per bundle), and classifies every
entry’s response.
entries = [
(
{"resourceType": "Patient", "identifier": [
{"system": "http://example.org/mrn", "value": str(n)}
]},
f"identifier=http://example.org/mrn|{n}",
)
for n in range(200)
]
result = client.conditional_create_batch(entries, chunk_size=50)
for created in result.created: # 201 — newly created
print(created.index, created.resource_id)
for existed in result.existed: # 200 — an existing resource matched
print(existed.index, existed.resource_id)
for failed in result.failed: # anything else — collected, not raised
print(failed.index, failed.status_code, failed.outcome)
Entry index values refer to positions in your original entries
sequence, across all chunks. accounts= stamps meta.accounts on
every entry’s resource; on_behalf_of= applies per call.
Two properties worth knowing:
- Replay-safe by construction. A conditional create is the one POST
shape a replay cannot duplicate — the retry is a server-side no-op —
so the SDK marks these batch requests replay-safe: an ambiguous 5xx
or mid-batch network failure retries safely, and re-invoking the
whole call after an error converges (already-committed entries
classify as
existedon the second pass). - The query is the correctness boundary. A
200 existedreturns whatever already matched the query — use a globally unique business key you own, and treat a match as found, not created: verify identity before attaching local state.
Retry-safety note¶
The retry policy replays a POST on ambiguous statuses (502/503/504)
only when it carries a real If-None-Exist query — a bare
create_resource POST is never replayed there, because those statuses
can arrive after the origin already committed the write. When a create
must be safe under retry, conditional create is the tool, not the
status class.