Terminology & validation
Every coded field — a diagnosis, a lab, a medication — quietly drags you into running a terminology server. bonfire validates codes on write, powers your pickers with code-search, and ships LOINC, RxNorm & ICD-10 bundled and version-pinned — SNOMED CT works with your own UMLS license. So “store a diagnosis” stops being a project.
FHIR validates the shape. It doesn’t know the code is real.
FHIR will happily tell you a CodeableConcept is well-formed. It won’t tell you the code actually exists in LOINC, or that the diagnosis you stored is a real ICD-10 code, or that the RxNorm identifier means the drug you think it means. That answer lives in a terminology server — $validate-code and $expand over LOINC, SNOMED CT, RxNorm, ICD-10 — and standing one up is its own infrastructure project. bonfire folds it into the write path.
Validate on write
A coded field is checked against its code system before it commits. Garbage codes fail at the door, not in a downstream report six weeks later.
Search for pickers
Type-ahead code search powers your dropdowns. Clinicians pick from real codes by name — they never memorize a numeric identifier.
Bundled & version-pinned
LOINC, RxNorm & ICD-10 ship with the package, pinned to a known release; SNOMED CT loads from your own UMLS license. No live call to an external server on the hot path, no silent drift.
Structure and semantics
FHIR checks the shape; bonfire checks the meaning. Both run, so a stored diagnosis is well-formed and a code that exists.
Validate a code in one call — on the write path
You don’t stand up a $validate-code endpoint, manage its uptime, or decide what to do when it times out mid-write. You ask bonfire whether a code is valid in its system, and the answer comes back with the display string the code resolves to.
$validate-code, without a terminology server to operate.
// is this a real LOINC code? what does it mean? const r = await clinical.terminology.validate( "LOINC", "4548-4" // Hemoglobin A1c ) r.valid // true r.display // "Hemoglobin A1c/Hemoglobin.total in Blood" r.system // "http://loinc.org" r.version // pinned release, not "whatever's live today" // a code that doesn't exist fails before it commits const bad = await clinical.terminology.validate( "LOINC", "0000-0" ) bad.valid // false → the write is refused
SNOMED CT works with your own UMLS license
Teams routinely assume SNOMED CT means a licensing bill. For use within the United States the license is free: the U.S. is a SNOMED International member, and the U.S. Edition is distributed at no cost through the National Library of Medicine under a (free) UMLS Metathesaurus License. We don’t redistribute SNOMED content — you bring your own UMLS license and bonfire validates and searches against it. (LOINC, RxNorm, and ICD-10 are bundled outright.) Outside the U.S., SNOMED licensing follows your country’s Member or Affiliate terms — check yours.
The assumption
“SNOMED is a paid vocabulary, so we’ll just store free-text diagnoses and clean it up later.” The cleanup never happens, and your data is uncoded.
The reality
The SNOMED CT US Edition license is free via NLM. Coded from the start costs you nothing — bring your UMLS license and bonfire wires SNOMED into the same validate-and-search path as the bundled sets.
“FHIR” and the names of code systems are descriptive references to their respective standards and owners (HL7®, SNOMED International, Regenstrief/LOINC, NLM/RxNorm, WHO/CDC ICD-10). Always confirm current license terms with the source before you ship — terms change, and your jurisdiction matters.
Code search is the picker
A clinician types “a1c” and gets the matching coded concepts back, ranked, ready to drop into a dropdown. The picker is just a thin call over the same bundled value sets — you don’t scrape a CSV, host a search index, or hand-maintain a list of “common codes.”
- ✓ Search by name or partial code — returns code, display, and system together.
- ✓ Backed by the pinned value set, so results match what validate will accept.
- ✓ Scope a search to the value set a given field allows, so pickers only offer valid choices.
The result of a search is, by construction, a code that will pass validation — the picker and the gate read the same source.
// autocomplete a lab picker from real LOINC const options = await clinical.terminology.search( "a1c", { system: "LOINC", limit: 8 } ) // [{ code, display, system }, …] options.map(o => ({ value: o.code, // "4548-4" label: o.display, // "Hemoglobin A1c…" system: o.system // "http://loinc.org" })) // whatever the user picks already validates — // picker and write-gate share one value set
Bundled and version-pinned, so codes don’t drift under you
Calling a live external terminology server on every write means your validation result depends on someone else’s uptime and someone else’s last release. bonfire bundles value sets into the package and pins them to a version. Your validation is deterministic across environments, your CI doesn’t fail because a remote server hiccuped, and a vocabulary update is a deliberate, reviewable bump — not a surprise.
Value sets ship in the package
The code systems you need come bundled and pinned. No terminology server to provision, secure under a BAA, and keep alive.
Validation reads the pinned set
Every validate and search resolves against that exact release — same answer in dev, CI, and prod.
Updates are a reviewed bump
A new vocabulary release is an explicit version change you can diff, test, and roll out — not a silent shift in what “valid” means.
Built-in terminology — not a full terminology service
bonfireDB is pre-launch / early access. The goal is to make the common coded fields outpatient apps reach for first a non-event: validate them, search them, store them coded. It is deliberately not a complete terminology service. Full SNOMED CT subsumption, every value set, hierarchy reasoning, and arbitrary $expand filters are still ahead — and where you need that depth, a dedicated terminology server still earns the job.
What’s designed in
- Validate-on-write across LOINC, RxNorm, and ICD-10 (bundled) — plus SNOMED CT under your own UMLS license
- Code-search / autocomplete for pickers, over the same pinned sets
- Bundled, version-pinned value sets (LOINC, RxNorm, ICD-10) — deterministic, no live dependency on the hot path
- Canonical display resolution, so the UI shows the official label
What it isn’t (yet)
- A complete terminology server with the full release of every code system
- SNOMED subsumption / hierarchy reasoning and arbitrary $expand filters
- Concept mapping between code systems ($translate / ConceptMap)
- A managed authority for non-US SNOMED licensing or exotic value sets
Where bonfire stops, a dedicated terminology server begins — and because the canonical record is FHIR R4 underneath, a code you stored here is a code you can hand to one cleanly.
What “store a diagnosis” actually costs
| Capability | FHIR data model alone | Roll your own terminology server | bonfireDB |
|---|---|---|---|
| Code exists in its system | Shape only, no check | If you build $validate-code | Validated on write |
| Picker / autocomplete | Your job to build | Stand up search yourself | Built-in code-search |
| Value-set hosting | N/A | You operate the server | Bundled, version-pinned |
| Deterministic across envs | Depends on you | Tied to server uptime/release | Pinned, reproducible |
| SNOMED CT (US) cost | N/A | Free via NLM, you wire it | Free via NLM — your UMLS license, wired in |
| Full subsumption / $expand filters | No | Yes, if you build it | Roadmap — scoped today |
bonfireDB is early-stage; this page describes product design and positioning, not a benchmark. Code-system names are descriptive references to their owners' standards; licensing (including SNOMED CT’s free US use via the NLM UMLS license) can change — verify current terms with the source for your jurisdiction.
Coded fields are the foundation the rest of the backend reads from
FHIR underneath
Validated codes flow straight into the canonical FHIR R4 beneath your primitives — clean to export.
Explore →App-native primitives
Assessments, observations, and diagnoses store coded by default — no free-text-now, fix-later.
Explore →Authorization & audit
Every coded write runs the same access gate and emits an AuditEvent, like every other write.
Explore →How it works
TypeScript + Postgres + pgvector, FHIR R4 underneath, no Redis — see the full picture.
Explore →You build the app. Bonfire is the clinical data layer underneath.
Validate codes on write, search them for your pickers, and store diagnoses coded from day one — without standing up a terminology server. Get early access to the core (Apache-2.0 planned).
Frequently asked questions
How do I validate medical codes without running a terminology server?
bonfireDB folds $validate-code into the write path: a coded field is checked against its code system before it commits, so invalid codes are refused at the door. You don’t stand up, secure, or keep alive a separate terminology server — value sets ship bundled and version-pinned in the package. (bonfireDB is pre-launch / early access; this describes the design.)
Do I need a terminology server to validate LOINC, SNOMED CT, RxNorm, and ICD-10 codes?
Not for the common coded fields outpatient apps reach for first. bonfireDB is designed to validate-on-write and code-search across LOINC, RxNorm, and ICD-10 from bundled, pinned value sets, plus SNOMED CT under your own UMLS license (we don’t redistribute SNOMED content). For full subsumption, every value set, or arbitrary $expand filters, a dedicated terminology server still earns the job.
Is SNOMED CT free to use in the United States?
For use within the US the license is free: the US is a SNOMED International member and the US Edition is distributed at no cost through the National Library of Medicine under a free UMLS Metathesaurus License. bonfireDB does not redistribute SNOMED content — you bring your own (free, for US use) UMLS license and bonfire validates and searches against it. Outside the US, licensing follows your country’s Member or Affiliate terms — verify current terms with the source for your jurisdiction.
How do I build a LOINC or ICD-10 code picker with autocomplete?
bonfireDB’s code-search is the picker: a clinician types a partial name or code and gets ranked coded concepts back with code, display, and system, ready to drop into a dropdown. Because search and the write-gate read the same pinned value set, whatever the user picks already validates — no scraped CSV or hand-maintained 'common codes' list.
Why bundle and version-pin value sets instead of calling a live terminology server?
Calling a live external server on every write makes validation depend on someone else’s uptime and last release. bonfireDB bundles value sets and pins them to a known version, so validation is deterministic across dev, CI, and prod, and a vocabulary update is a deliberate, reviewable bump — not silent drift.