Build a FHIR Enrichment Service with AI and an Afternoon
How a solo developer can use AI coding assistants to discover the FHIRfly SDK and build production-quality FHIR data enrichment in a single session.

Most healthcare IT teams have more work than people. If you're a solo developer at a rural health network, a two-person consulting shop, or a contractor brought in to "make the FHIR data usable," you know the feeling. There's a medication list that needs enriching, provider records that need NPI lookups, diagnosis codes that need human-readable descriptions — and the budget for a 10-person engineering team doesn't exist.
AI coding assistants are changing the math. Tools like Claude Code and Codex don't just autocomplete your brackets — they can discover APIs, read documentation, write integration code, and produce something testable in a single session. Combined with the right APIs, a solo developer can build in an afternoon what used to take a team a week.
This post walks through a real workflow: using an AI coding assistant to discover and integrate the FHIRfly Terminology SDK into a TypeScript service that enriches FHIR patient data with drug names, diagnosis descriptions, and provider details.
The Scenario
You're a contractor at a rural health clinic. They receive FHIR R4 patient bundles from their EHR, but the data is full of codes — NDC numbers, ICD-10 codes, NPI identifiers. The clinical staff needs a daily report that translates these codes into readable information: drug names, diagnosis descriptions, prescriber details.
The bundle looks something like this:
{
"resourceType": "Bundle",
"type": "collection",
"entry": [
{
"resource": {
"resourceType": "MedicationRequest",
"medicationCodeableConcept": {
"coding": [{ "system": "http://hl7.org/fhir/sid/ndc", "code": "0069-0151-01" }]
},
"requester": { "identifier": { "value": "1234567890" } }
}
},
{
"resource": {
"resourceType": "Condition",
"code": {
"coding": [{ "system": "http://hl7.org/fhir/sid/icd-10-cm", "code": "E11.9" }]
}
}
}
]
}
NDC 0069-0151-01. ICD-10 E11.9. NPI 1234567890. Meaningless to anyone without a reference database open. Your job: turn this into something the care team can read.
Step 1: Tell the AI What You Need
Open your AI coding assistant and describe the problem:
"I need a TypeScript service that reads a FHIR R4 Bundle, extracts medication NDCs, diagnosis ICD-10 codes, and prescriber NPIs, then enriches each one with human-readable names. I want to use a healthcare terminology API — find me an SDK that can do NDC, ICD-10, and NPI lookups."
A capable AI assistant will search npm, find @fhirfly-io/terminology, read its documentation, and start writing code. Here's what the generated service looks like.
Step 2: The Generated Code
The AI produces a working enrichment service. The key insight: this isn't a prototype or a demo. It's deterministic code that calls well-documented APIs and returns structured data. Every lookup goes to a production-grade reference database — not an LLM making guesses about drug names.
import { Fhirfly } from "@fhirfly-io/terminology";
const client = new Fhirfly({ apiKey: process.env.FHIRFLY_API_KEY! });
interface EnrichedMedication {
ndc: string;
brand_name: string;
generic_name: string;
dosage_form: string;
prescriber?: { name: string; specialty: string };
}
interface EnrichedCondition {
icd10: string;
display: string;
category: string;
}
interface EnrichmentReport {
medications: EnrichedMedication[];
conditions: EnrichedCondition[];
generated_at: string;
}
async function enrichBundle(bundle: fhir4.Bundle): Promise<EnrichmentReport> {
// Extract codes from FHIR resources
const ndcs: string[] = [];
const icd10s: string[] = [];
const npis: string[] = [];
for (const entry of bundle.entry ?? []) {
const resource = entry.resource;
if (resource?.resourceType === "MedicationRequest") {
const ndc = resource.medicationCodeableConcept?.coding
?.find(c => c.system === "http://hl7.org/fhir/sid/ndc")?.code;
if (ndc) ndcs.push(ndc);
const npi = resource.requester?.identifier?.value;
if (npi) npis.push(npi);
}
if (resource?.resourceType === "Condition") {
const code = resource.code?.coding
?.find(c => c.system === "http://hl7.org/fhir/sid/icd-10-cm")?.code;
if (code) icd10s.push(code);
}
}
// Batch lookups — one API call per code type
const [drugResults, dxResults, npiResults] = await Promise.all([
ndcs.length > 0 ? client.ndc.lookupMany(ndcs) : null,
icd10s.length > 0 ? client.icd10.lookupMany(icd10s) : null,
npis.length > 0 ? client.npi.lookupMany(npis) : null,
]);
// Build medication list with prescriber details
const medications: EnrichedMedication[] = [];
if (drugResults) {
for (const result of drugResults.results) {
if (result.status !== "ok") continue;
const med: EnrichedMedication = {
ndc: result.input,
brand_name: result.data.brand_name,
generic_name: result.data.generic_name,
dosage_form: result.data.dosage_form,
};
medications.push(med);
}
}
// Attach prescriber info from NPI lookups
if (npiResults) {
for (const result of npiResults.results) {
if (result.status !== "ok") continue;
const name = `${result.data.name.first} ${result.data.name.last}`;
const specialty = result.data.taxonomies?.[0]?.description ?? "Unknown";
// Attach to all medications (simplified — real code would match per-request)
for (const med of medications) {
med.prescriber = { name, specialty };
}
}
}
// Build condition list
const conditions: EnrichedCondition[] = [];
if (dxResults) {
for (const result of dxResults.results) {
if (result.status !== "ok") continue;
conditions.push({
icd10: result.input,
display: result.data.display,
category: result.data.chapter_description ?? "Unknown",
});
}
}
return {
medications,
conditions,
generated_at: new Date().toISOString(),
};
}
Three API calls. Three different reference databases. Every result is deterministic — the same NDC always returns the same drug name, the same ICD-10 code always returns the same description. No hallucination risk.
What the Output Looks Like
Feed that FHIR bundle through the enrichment service and you get:
{
"medications": [
{
"ndc": "0069-0151-01",
"brand_name": "Lipitor",
"generic_name": "Atorvastatin Calcium",
"dosage_form": "TABLET, FILM COATED",
"prescriber": {
"name": "James Smith",
"specialty": "Internal Medicine"
}
}
],
"conditions": [
{
"icd10": "E11.9",
"display": "Type 2 diabetes mellitus without complications",
"category": "Endocrine, nutritional and metabolic diseases"
}
],
"generated_at": "2026-03-26T14:30:00.000Z"
}
From opaque codes to a readable medication list with prescriber credentials and diagnosis descriptions. The care team can now review this without cross-referencing three different databases.
Why This Works: AI + Deterministic APIs
The AI coding assistant is valuable here because it handles the integration boilerplate — parsing FHIR resources, structuring batch requests, mapping response fields. But the actual data resolution is deterministic. The SDK calls reference APIs that return authoritative data from the FDA NDC Directory, NLM RxNorm, CMS NPI Registry, and WHO ICD-10.
This is the pattern that works for healthcare: let the AI write the plumbing, let the APIs provide the facts.
The alternative — asking an LLM to "look up NDC 0069-0151-01" from its training data — is unreliable. Drug codes change. Providers move. Diagnosis code sets get annual updates. A reference API always returns current data. An LLM might return data from its training cutoff, or worse, confabulate a plausible-sounding but incorrect drug name.
Scaling Up: What Else You Can Build
Once you have the SDK integrated, the same pattern extends to other workflows a small health IT shop encounters:
Lab result enrichment — LOINC code lookups to add test names and reference ranges:
const lab = await client.loinc.lookup("2345-7");
// → "Glucose [Mass/volume] in Serum or Plasma"
Vaccine registry validation — CVX code lookups for immunization records:
const vaccine = await client.cvx.lookup("208");
// → "SARS-COV-2 (COVID-19) vaccine, mRNA, spike protein, LNP, preservative free, 30 mcg/0.3mL dose"
Drug interaction screening — pull FDA-approved interaction text for a medication list:
const interactions = await client.ddi.referenceMany(
["warfarin", "simvastatin", "metformin"]
);
// Returns FDA label interaction sections + drug classes for each
Provider directory enrichment — batch NPI lookups with taxonomy and address data:
const providers = await client.npi.lookupMany(["1234567890", "9876543210"]);
// Returns name, specialty, practice address, credentials
Each of these is a single SDK call returning structured, sourced data. The AI assistant can scaffold the integration code; the API provides the ground truth.
Key Takeaways
- AI coding assistants are force multipliers for small teams. A solo developer with Claude Code or Codex can build production-quality healthcare data services that would otherwise require a dedicated team.
- Deterministic APIs are the right complement to AI code generation. Let the AI handle integration logic and boilerplate. Let reference APIs handle data accuracy. Don't ask an LLM to be a drug database.
- Batch lookups keep things fast. The FHIRfly SDK supports batch requests across all endpoints — NDC (up to 500), ICD-10 (up to 100), NPI (up to 100). One API call replaces hundreds of individual lookups.
- FHIR bundles are a common starting point. If you're working with EHR data, you're probably already receiving FHIR resources. The enrichment pattern shown here — extract codes, batch lookup, merge results — applies to any FHIR-based workflow.
- The data is public domain. FDA NDC data, CMS NPI data, and ICD-10 classifications are all public domain. No license fees for the underlying data.
Further Reading