Write clinical.notes.create() โ not POST /DocumentReference. bonfireDB gives you typed clinical functions that read like the workflow you're building, and generate FHIR R4 underneath when you need it.
FHIR makes you translate every clinical action into a resource POST, a reference graph, and a write that assumes strangers. bonfireDB gives you the verb directly. A note is a note.
No reverse-engineering which resource a clinical note maps to. No hand-wiring the encounter reference. No guessing whether your write spawned a duplicate.
// One typed call. Returns a freshness object. await clinical.notes.create({ patientId, encounterId, text })
Every primitive is a real TypeScript method with real types โ arguments, returns, and the shape of what comes back. Your editor autocompletes the clinical model. A typo is a compile error, not a malformed Bundle you debug in production.
The same typed function is also an HTTP endpoint and an MCP tool. Write it once; it shows up everywhere.
// Reactive, fresh on commit โ no manual refetch. const notes = useClinicalQuery( api.notes.listByPatient, { patientId } )
Record a PHQ-9 with one call. bonfireDB writes the QuestionnaireResponse and the scored Observation underneath โ correctly referenced, correctly coded โ so your app stays interoperable without you hand-assembling the resource graph.
Structured instruments, structured scores. Both generated, both queryable, both exportable.
// -> QuestionnaireResponse + Observation await clinical.assessments.record( "PHQ-9", { patientId, answers } ) // Same pattern for any observation. await clinical.observations.record({ patientId, code: "tic-severity", value, effective })
The same primitive that powers your list screen also produces the audit trail and the interop record. You don't maintain three representations of a clinical fact โ bonfireDB keeps app reads, the audit log, and the FHIR projection in sync from a single source of truth in Postgres.
No second database for "app state." No drift between what your UI shows and what you can export.
// Clean FHIR R4 Bundle on demand. await clinical.fhir.export(patientId)
You build against verbs that match your product. bonfireDB keeps the corresponding FHIR R4 in sync underneath, so export and integration are there when you need them โ not bolted on later. Call clinical.fhir.export(patientId) and get a clean Bundle whenever you hand records to another system.
Related: FHIR underneath ๐ฅ ยท Always fresh โก
// Every write returns its freshness lifecycle. { status: "committed", views: { notesByPatient: "fresh", timeline: "fresh" }, indexes: { semanticSearch: "pending", agentContext: "pending" } }
Start with typed primitives that read like your product and generate FHIR R4 underneath.
It means you write against typed clinical verbs that match your product โ like clinical.notes.create() โ instead of POSTing FHIR resources. bonfireDB owns a Postgres-first data plane and generates FHIR R4 underneath, so you build your app and get interop without hand-assembling resource graphs.
bonfireDB is designed to give you Convex-style typed functions for clinical data: each primitive is a real TypeScript method that's also an HTTP endpoint and an MCP tool from a single definition, with reads that stay fresh on commit. The difference is it generates FHIR R4 underneath for export and interop. It's pre-launch / early access.
They're real TypeScript methods for clinical actions โ clinical.notes.create(), clinical.assessments.record("PHQ-9", ...), clinical.observations.record() โ with typed arguments and returns. Your editor autocompletes the clinical model, and a typo is a compile error instead of a malformed FHIR Bundle you debug in production.
No. You call the verb (a note is a note) and bonfireDB keeps the corresponding FHIR R4 in sync underneath. One assessment call, for example, is designed to write both the QuestionnaireResponse and the scored Observation โ correctly referenced and coded โ so you stay interoperable without wiring the resource graph yourself.
Call clinical.fhir.export(patientId) to get a clean FHIR R4 Bundle on demand. The same primitive that powers your app reads also produces the audit trail and the interop record from one source of truth in Postgres, so there's no second app-state database and no drift between your UI and what you export.