C-CDA

Read the C-CDA a vendor actually sent.

@cosyte/ccda reads real, vendor-quirky C-CDA documents into typed clinical models and turns each deviation into a stable coded warning. Where a document contradicts itself, it withholds the value and says so instead of guessing.

@cosyte/ccda
npm install @cosyte/ccda

Need it integrated? Talk to us.

The standard

C-CDA is the US template set for clinical documents on CDA R2.

  • HL7's Consolidated CDA guide is used with the CDA Release 2 standard to implement a set of clinical documents, including the Continuity of Care Document, Discharge Summary, Referral Note, Progress Note and History and Physical. Source: HL7 C-CDA implementation guide
  • The guide was developed within ONC's Standards and Interoperability Framework to give the US Realm one harmonized set of CDA templates. Source: HL7 C-CDA implementation guide
  • ONC's transitions of care criterion requires certified health IT to create a transition of care or referral summary using the Continuity of Care Document, Referral Note and, in inpatient settings, Discharge Summary templates. Source: ONC test method, 170.315(b)(1)

@cosyte/ccda

Typed clinical data out of a real document.

A generic XML parser hands you a DOM and leaves every clinical judgement to you. A conformance validator rejects the document and returns nothing. This library reads what the sender produced and tells you what it had to tolerate.

  • Fourteen entry families: problems, medications, allergies, results, vital signs, immunizations, procedures, encounters, smoking status, plan of treatment, functional status, mental status, family history and past medical history.
  • Every deviation becomes a stable coded warning, so a document a validator would reject still yields the data it carries.
  • Observation values are a discriminated union, with UCUM units checked and the document's own unit kept intact.
  • Required-section validation per document type, and each type reports how much of its obligation was verified.
  • buildCcda emits a CCD, a Referral Note or an inpatient Discharge Summary; editCcda replaces whole sections.
  • Round-trip: re-parsing and re-serializing a document it wrote changes nothing.
  • A terminology adapter you supply checks codes at the problem, medication, allergen, route and vaccine slots.
build-and-read.tsts
import { buildCcda, parseCcda } from "@cosyte/ccda";

// A synthetic CCD, built here so the example stands on its own. In your integration this is the
// document a sending system handed you.
const xml = buildCcda({
  patient: { mrn: "MRN001", given: ["Jane"], family: "Doe", gender: "F", birthTime: "19800101" },
  problems: [{ problem: { code: "59621000", displayName: "Essential hypertension" } }],
  medications: [
    {
      drug: { code: "314076", displayName: "Lisinopril 10 MG Oral Tablet" }, // RxNorm
      dose: { value: 1, unit: "{tablet}" },
      route: { code: "C38288", displayName: "Oral" }, // NCI Thesaurus
    },
  ],
  allergies: [
    {
      allergen: { code: "7980", displayName: "Penicillin G" }, // RxNorm
      reaction: { code: "247472004", displayName: "Hives" }, // SNOMED CT
    },
  ],
}).toString();

const doc = parseCcda(xml);
const problem = doc.getProblems()[0];
const allergen = doc.getAllergies()[0]?.allergies[0]?.allergen;

console.log(doc.documentType, doc.getMrn(), doc.getPatient()?.name?.family);
console.log("problem", problem?.problems[0]?.value?.code, problem?.status);
console.log("medication", doc.getMedications()[0]?.drug?.code);
console.log("allergy", allergen?.code, allergen?.displayName);
console.log("warnings", doc.warnings.length);
// The emit half is a fixed point: re-parsing and re-serializing changes nothing.
console.log("round trip unchanged", parseCcda(doc.toString()).toString() === xml);

// The values printed above, which the test suite asserts on every run:
doc.documentType; // => "ccd"
doc.getMrn(); // => "MRN001"
problem?.problems[0]?.value?.code; // => "59621000"
problem?.status; // => "active"
doc.getMedications()[0]?.drug?.code; // => "314076"
allergen?.code; // => "7980"
doc.warnings; // => []
parseCcda(doc.toString()).toString() === xml; // => true

From the @cosyte/ccda README on GitHub, verbatim. Every value in it is synthetic.

Limits

What it does not do.

Its README lists every limit, stated plainly. The main ones:

  • Building covers 3 of the 12 US Realm document types. The other nine throw rather than emit something that only resembles the type.
  • Editing is whole-section only: no entry-level append and no section removal.
  • Required-section validation under-warns. A quiet parse is not a conformance result.
  • A terminology adapter is consulted at five coded slots only, and no value-set member codes are bundled.
  • UCUM validation is grammatical, over a curated set of atoms, and LOINC deprecation is a curated list.
  • One runtime dependency: @xmldom/xmldom.

The full status and limits, in the README

Alternatives

What else you could use.

Each description comes from the project’s own documentation or npm listing, linked below.

JavaScript library

BlueButton.js

Helps developers parse and generate health data formats such as C-CDA. Its npm package, bluebutton, was last published in September 2015.

Source: npm, bluebutton

TypeScript converter

Medplum C-CDA (@medplum/ccda)

Converts between C-CDA and FHIR, using the International Patient Summary as a bridge. If FHIR is your target model, it goes there directly; @cosyte/ccda reads C-CDA into models of its own.

Source: Medplum documentation, C-CDA

Converter

Microsoft FHIR Converter

Converts C-CDA (and HL7 v2, JSON and FHIR STU3) to FHIR R4 with Liquid templates, through the $convert-data operation of Azure's FHIR service.

Source: Microsoft Learn, $convert-data

Works with

  • De-identification@cosyte/deid applies a HIPAA Safe Harbor policy to a parsed C-CDA document.
  • Terminology@cosyte/terminology can back the terminology adapter @cosyte/ccda consults.
  • @cosyte/synthGenerates spec-clean synthetic C-CDA documents (CCD and Referral Note) for your tests.

Try @cosyte/ccda on your own messages.

It is free and MIT-licensed. If it saves you a day, a star on GitHub helps other engineers find it.

Need it integrated? Talk to us, or see what our integration services cover.