Synthea (MITRE's open-source patient simulator) is the default source of synthetic FHIR data. We generated two patients (java -jar synthea-with-dependencies.jar -p 2 -s 4242 Massachusetts) and loaded them into Anpheros, the FHIR backend I work on. The first bundle had 221 entries.
What did not fit: 39 entries of types the server does not store — Claim (13), ExplanationOfBenefit (13), SupplyDelivery (9), Device (2), ImagingStudy (1), MedicationRequest (1). Synthea models US billing in detail; a clinical record usually doesn't need it.
The loader asks the server which types it supports and posts the rest as one transaction:
import { readFile } from 'node:fs/promises';
import { Anpheros, apiKey } from '@anpheros/sdk';
const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) }); // a sandbox key, sk_test_…
const SUPPORTED = new Set(((await anpheros.fhir.metadata()) as any).rest[0].resource.map((r: any) => r.type));
const bundle = JSON.parse(await readFile(process.argv[2], 'utf8')); // one Synthea patient file
const kept = bundle.entry.filter((e: any) => SUPPORTED.has(e.resource.resourceType));
const out: any = await anpheros.fhir.transaction({ ...bundle, entry: kept } as any);
console.log('created', out.entry.length);
182 resources were created in one transaction, urn:uuid references resolved, and the patient's International Patient Summary came out with 15 problems, 6 immunizations, 15 results and 28 vital signs.
Three things to watch if you do the same with any server:
-
Conditional references. Synthea points to practitioners and organisations as
Practitioner?identifier=…. Servers differ: ours stored them as written and did not resolve them. Check yours before following those links. -
Removed is not converted. Dropping
MedicationRequestmeans the medication disappears. Convert it toMedicationStatementfirst if you need it. -
Names with numbers (
Marcelo725 Gulgowski816) are deliberate, so no one mistakes synthetic people for real ones.
Full guide, including when to use Synthea vs hand-made personas and what good test data must contain: Synthetic patient data for testing.
Top comments (1)
The counts check out: 13+13+9+2+1+1 = 39, and 221 - 39 = 182. One thing the type filter alone does not guarantee is that the 182 you keep don't point at the 39 you dropped. Synthea bundles use urn:uuid references between entries, and some kept resources can reference dropped ones (a DiagnosticReport pointing at an ImagingStudy, a Procedure at a Device, anything that referenced the MedicationRequest). A transaction can succeed while those references are left dangling, depending on how the server treats a urn:uuid that matches no entry.
A cheap check to put in the loader: collect the fullUrl of every dropped entry, then scan the kept resources for any reference equal to one of them and print the count. If it is zero, you can say the 182 are self-contained; if not, you know which links to strip or convert.
Which did Anpheros do with the conditional references you mention: store them as written and return them unchanged on read? And did you run the bundle through $validate before posting, or is the transaction the first time the server saw it?