Why building on FHIR is hard
FHIR is a brilliant interoperability spec. It is a strange foundation for one team building one product. Here’s the gap — and what bonfireDB does about it.
FHIR was built to move records between distrustful organizations — not for one team building one product.
So when you build an app on it, you inherit a federation protocol. Referential integrity becomes something you enforce. Write semantics assume strangers are racing you. Search isn’t a query language — it’s a portability contract. You set out to ship an app, and you end up operating a FHIR server.
Six places FHIR makes you the operator, not the builder
Every theme below is a real, documented failure mode — with the spec, issue tracker, and vendor receipts. And what bonfireDB does instead.
Write semantics assume strangers
“I called conditional-create once and somehow I have two Patients with the same MRN. The spec literally says the race 'usually can’t be fully eliminated.' My GET-edit-PUT just clobbered a teammate’s concurrent edit because PUT is a full replacement. And my 'atomic' transaction Bundle wasn’t atomic under load.”
The duplicate-Patient race is real and acknowledged upstream (HAPI #3141, Microsoft #1382, IBM #2051) — and the failure surfaces as a 412, not a 500, so it slips past your error handling. Current Bonfire has transactional canonical writes, immutable history, an atomic projected-write primitive, and governed proposals. The complete typed write API and universal route coverage are target work.
FHIR is a data model, not an access-control system
“I set a security label to mask a record, then it leaked back out through GraphQL, $export, and history. Consent is just a schema — there’s no engine that enforces it. HealthLake has no per-resource ABAC; IAM gates the whole datastore or nothing. And Azure won’t even auto-write AuditEvents for me.”
A schema for security labels and Consent is not an enforcement engine — masking famously leaks via GraphQL, $export, and _history, and managed FHIR services punt authz to the perimeter. Current Bonfire: practice, resource, role, and purpose policy with Postgres RLS, governed proposals, and hash-chained receipts. Patient/consent scope and universal every-surface audit are the target.
Realtime guarantees nothing
“rest-hook is best-effort, so I silently lost events. The ones that arrive come out of commit order. Bulk $import emits zero events, so my search index never learns about imported data. And once, the subscription channel fell days behind — my analytics and search index rotted and nobody noticed.”
FHIR subscriptions are best-effort with no ordering and no coverage for bulk $import — your search index and analytics drift silently until something breaks in front of a clinician. Current Bonfire has an atomic projected-write primitive plus a framework-free LISTEN/NOTIFY query store. Universal governed-write freshness, browser/React adapters, and one receipt spanning projections and derived indexes are target work.
Terminology conscripts you into running a terminology server
“To validate a code I need $expand and $validate-code. To run those I need to stand up a terminology server, load multi-gigabyte SNOMED and LOINC, and chase weekly RxNorm drift. I’m a two-person app team now maintaining a code-system pipeline.”
Real-world FHIR validation often requires a terminology service. SNOMED CT is free for US use through the NLM UMLS license; licensing costs apply internationally and in non-member countries. Bonfire's target clinical primitives carry typed, coded fields so common writes can validate without operating a full terminology server. The complete primitive API is not shipped yet.
App-state homelessness
“FHIR has nowhere to put a draft, UI state, or a workflow flag — so I stood up a second database. Now I’m in sync hell between the two. And I can’t query by arbitrary fields, joins, or aggregates, so even a simple list screen needs me to hand-build a denormalized projection.”
FHIR has no home for drafts, UI state, or workflow, and no general query surface — so teams bolt on a second DB and a manual projection per screen. bonfireDB: app state lives in one canonical Postgres next to your clinical data (canonical FHIR R4 as JSONB), with real queries, joins, and aggregates — and the operational read models give you list-screen projections without a second store to sync.
Raw FHIR is the wrong agent interface
“I pointed an agent at the FHIR record and it overflowed the context window. It missed linked resources, filtered on display text instead of codes, and dropped an early value when I asked for the first.”
Our MIMIC-IV-on-FHIR comparison measured the problem: raw FHIR scored 0/262 when records exceeded the 32k context window. In two later paired runs, question-aware selection beat blunt projection by 9.5 and 8.3 points. QT-4 then improved microbiology accuracy from 10/44 to 25/44 on its registered subset; the other 330 holdout questions were byte-identical controls. Adding traversal reached 29/44, but the incremental result was unresolved (p=.219) and not promoted. These results support narrow selection and vocabulary mechanisms — not Bonfire, traversal on natural charts, graph storage, or the still-building governed product layer.
What every layer of building costs you on raw FHIR — and what bonfireDB does
From first command to running in production, here’s where the operator tax shows up at each layer.
| Layer | The pain on raw FHIR | What bonfireDB does |
|---|---|---|
| Get started | Stand up a server, pick a profile pack, wire auth — days before line one of product. | Early-access core on Postgres; the complete typed SDK and deployment workflow are still rolling out. |
| Model | Bend your domain into 145+ generic resources and references. | Canonical FHIR R4 and SQL-on-FHIR projections today; app-native primitives and clean Bundle export are target work. |
| Write | Conditional-create duplicate races, PUT lost-updates, non-atomic Bundles. | An atomic projected-write primitive today; unified governed routes and freshness receipts are target work. |
| Read for the UI | No arbitrary queries; hand-build a denormalized projection per screen. | Rebuildable SQL-on-FHIR projections plus a framework-free LISTEN/NOTIFY query store; universal governed-write freshness and browser/React adapters are target work. |
| Search | Search is a portability contract, not a query language; semantic search is on you. | Postgres full-text search over dedicated search documents, citations, and a development vector signal; production semantic ranking is planned. |
| Agents | Large raw FHIR records overflow context, lose references/codes, and hide the evidence the question needs. | Agents read clean, cited, permission-aware evidence packets via scoped context tools; writes are propose-only. |
| Authorize & comply | Labels and Consent leak; no enforcement engine; managed FHIR has no FHIR-aware authz. | Practice/resource/purpose policy, RLS, governed proposals, and hash-chained receipts today; patient/consent scope and complete read audit are target work. |
| Interop | You’re already running a FHIR server just to talk to one partner someday. | Canonical FHIR R4 with immutable history today; clinical.fhir.export(patientId) is a planned governed Bundle API. |
| Operate | You’re the operator: terminology server, subscription drift, two-DB sync hell. | Run the Apache-2.0 core in your own AWS, or the managed tier where we sign a BAA. |
Specifics cited descriptively. These benchmark figures come from a secondary external comparison (MIMIC-IV-on-FHIR, our FHIR-AgentBench fork) and describe agent performance over raw FHIR/projection/sandbox interfaces, not a benchmark of bonfireDB itself. “FHIR-compatible / FHIR-native” is used descriptively; FHIR® is a registered trademark of Health Level Seven International, and HL7® does not endorse bonfireDB.
You build the app. Bonfire is the clinical data layer underneath.
Start with the current core: canonical FHIR R4, immutable history, projections, policy, citations, and governed proposals. Typed clinical verbs, universal fresh-on-commit routes, and the full query-aware context compiler are the target contract.
Frequently asked questions
Why is building an app on FHIR so hard?
FHIR was designed to move records between distrustful organizations, not for one team building one product. Build an app on it and you inherit a federation protocol: you enforce referential integrity yourself, fight concurrent-write races, and treat search as a portability contract — so you end up operating a FHIR server instead of shipping your app.
FHIR vs an app backend — what’s the difference?
FHIR is a data and interoperability model. The Bonfire core keeps canonical FHIR R4 and projections in Postgres; patient/consent policy, the complete typed app API, and Bundle export remain target work.
Why is raw FHIR a bad default interface for agents?
Large raw-FHIR records exceeded the benchmark’s 32k context window. In two paired A6a runs, deterministic question-only selection beat frozen query-blind projection by 9.5 and 8.3 points. That is a measured selection result, not a Bonfire product result.
Can I use plain Postgres instead of a FHIR server?
Postgres gives an app transactions and ordinary queries; Bonfire adds canonical FHIR R4, history, projections, governed proposals, cited search, and context packets. Clean patient Bundle export is on the roadmap, not shipped yet.
Why does FHIR access control leak even with security labels and Consent?
A schema for security labels and Consent is not an enforcement engine. Bonfire's current core enforces practice, resource, role, and purpose policy with Postgres RLS and records hash-chained receipts for implemented operations. Patient and consent enforcement plus universal every-surface audit are target work.