On this page:

14.1CDA JavaScript Templating System

 

Before you can create a CDA document, you will need to create a CDA document template.

Included in Smile CDR is an API with many Fhir and CDA specific functions that you can read all about in the JavaScript Execution Environment (JSEE) section of the docs. You also have access to the ES5 JavaScript standard library for your templates. The JSEE currently only supports ES5 syntax, but ES6 will be supported in the future.

Keep in mind that the JavaScript Cda template is only responsible for creating a Fhir Composition resource from your existing Fhir repository. If you already have a properly formatted Composition, or you have your own workflow for creating Compositions, you can skip this step.

14.1.1The simplest CDA template script

 

The only requirement for your template script to work is a function called handle that returns a Composition.

Thus, the most basic Cda template script would look like this:

function generateCdaExchangeComposition() {
	return ResourceBuilder.build('Composition');
}

If you were to post this script to your cda module and then apply it, you would successfully create a CDA document! Of course, this CDA document would not be very useful since there is no information in it.

You can learn more about ResourceBuilder here.

14.1.2Adding header information to our CDA template script

 

To make your CDA document more useful, we should populate our Composition with some resources. A Composition resource created with ResourceBuilder has some additional functionality beyond that which is available to other resource types. This additional functionality has been designed specifically to assist with the creation of CDA document templates. All methods performed on the composition variable are documented in the Composition Resource and Composition Section docs under the JSEE.

function generateCdaExchangeComposition() {
	var composition = ResourceBuilder.build('Composition');
	composition.setType('ContinuityOfCareDocument');

	composition.setSubject('Patient/123');
	composition.setAuthor('Device/124');
	composition.setCustodian('Organization/125');
	composition.setEncounter('Encounter/126');

	return composition;
}

setType: The CDA module will create a Continuity of Care C-CDA document by default, but this is not guaranteed to always be the case, and so we should explicitly set the document type in our script. Additionally, if you were to want to persist the intermediate Composition and/or Bundle created by this module, setting the type here would ensure that the appropriate fields are populated in your Fhir Composition. You can see a full list of available document types in the Available Types and Mappings section of the docs.

set*: The additional setters here populate the appropriate fields of the Composition which will in turn be used to populate the appropriate tags in the US Realm Header C-CDA template.

If you were to replace the reference strings in the template above with valid references in your Fhir database, you would be able to generate a CDA document with a valid and fully populated US Realm Header section. However, all of your sections would be empty because you did not explicitly populate them. The Cda parser creates empty sections for every empty required section for your selected document type even if you did not add them to the Composition.

14.1.3Adding and populating a section in our CDA template script

 

In order to make our Cda document truly meaningful, we need to add and populate sections with the Patient's data.

You can see a full list of available sections and what Fhir Resources can be used to populate them in the Available Types and Mappings section of this document.

function generateCdaExchangeComposition() {
	var composition = ResourceBuilder.build('Composition');
	composition.setType('ContinuityOfCareDocument');

	composition.setSubject('Patient/1359');
	composition.setAuthor('Device/1453');
	composition.setCustodian('Organization/1452');
	composition.setEncounter('Encounter/1454');

	var allergySection = composition.addSection('allergyintolerance');
	allergySection.populate([Fhir.read('AllergyIntolerance/1366'), Fhir.read('AllergyIntolerance/1367')]);

	return composition;
}

Composition.addSection: Attaches a section to the Composition and returns that section.

Section.populate: Adds resource references to the section.

Additional information about Composition sections in the can be found in the Composition Section portion of the JavaScript Execution Environment docs.

If the references in the example above existed in your Fhir database, then your resultant Cda document would have a populated Allergies and Intolerances section, along with 5 auto-generated empty sections. With a slight modification, you could repeat the section code for all other mandatory Continuity of Care sections to have them all populated as well.

14.1.4Using the Fhir.search() API to dynamically populate sections

 

Hard-coding section entries will work, but it is not particularly useful if you want to create your CDA documents dynamically.

Instead, you can use the Fhir.search() API to create search parameter maps that are executed at runtime. This allows you to specify the rules by which to populate a section ahead of time and then get the most up to date resources at the time of document creation.

function generateCdaExchangeComposition() {
	var composition = ResourceBuilder.build('Composition');
	composition.setType('ContinuityOfCareDocument');

	composition.setSubject('Patient/1359');
	composition.setAuthor('Device/1453');
	composition.setCustodian('Organization/1452');
	composition.setEncounter('Encounter/1454');

	var allergySection = composition.addSection('allergyintolerance');
	
	var allergyResources = Fhir
	  .search()
	  .forResource('AllergyIntolerance')
	  .where('patient', 'Patient/1359')
	  .asList();

	allergySection.populate(allergyResources);

	return composition;
}

In the example above, instead of specifying specific AllergyIntolerance resources, we used the search feature to find the ones where the patient is Patient/1359. There are many more advanced searches we could perform. Please refer to the Fhir.search() API section of the docs for a full list of available search features.

14.1.5Specifying input parameters

 

The example above would work great if we were only ever interested in Patient/1359. In order to make this template more versatile, we can add an argument to our handle function called inputMap. This argument is a JavaScript Object and is essentially a collection of key-value pairs. The following example uses the inputMap and assumes the key patient will be provided.

function generateCdaExchangeComposition(inputMap) {
	var composition = ResourceBuilder.build('Composition');
	composition.setType('ContinuityOfCareDocument');

    var patient = inputMap['patient'];
	composition.setSubject(patient);

	composition.setAuthor('Device/1453');
	composition.setCustodian('Organization/1452');
	composition.setEncounter('Encounter/1454');

	var allergySection = composition.addSection('allergyintolerance');
	
	var allergyResources = Fhir
	  .search()
	  .forResource('AllergyIntolerance')
	  .where('patient', patient)
	  .asList();

	allergySection.populate(allergyResources);

	return composition;
}

If a user were to call the apply rest method for this template and include the following body param, the result would be the same as calling the previous hard-coded version with no params:

{
  "inputParams": {
    "patient": "Patient/1359"
  }
}

However, now this template could be used to report on the allegies of any patient in the Fhir database. A minor modification to the script would allow us to parametrize Author, Custodian, and Encounter as well.

14.1.6More complicated input parameters

 

Your input parameters do not have to be resource references. The following example would require additional input parameters, but makes the template more modular as well. In particular, pay attention to the use of maxAllergies and maxAllergyAge.

function generateCdaExchangeComposition(inputMap) {
	// store our inputMap params in their own variables
    var patient = inputMap['patient'];
    var author = inputMap['author'];
    var custodian = inputMap['custodian'];
    var encounter = inputMap['encounter'];
    var maxAllergies = inputMap['maxAllergies'];
    var maxAllergyAge = inputMap['maxAllergyAge'];
	
	var composition = ResourceBuilder.build('Composition');
	composition.setType('ContinuityOfCareDocument');

	composition.setSubject(patient);
	composition.setAuthor(author);
	composition.setCustodian(custodian);
	composition.setEncounter(encounter);
	
    // using JavaScript's native Date object, we construct a string 
    // to find only allergies asserted within the last `maxAllergyAge` days
    var xDaysAgo= new Date();
    xDaysAgo.setDate(xDaysAgo.getDate() - maxAllergyAge);
    // this will be of the form 'ge2018-07-31'
    var xDaysAgoIsoString = 'ge' + xDaysAgo.toISOString().slice(0,10);
	
	var allergySection = composition.addSection('allergyintolerance');
	var allergyResources = Fhir
	  .search()
	  .forResource('AllergyIntolerance')
	  .where('patient', patient)
	  .where('date', xDaysAgoIsoString)
	  .count(maxAllergies)
	  .sort('-date')
	  .asList();

	allergySection.populate(allergyResources);

	return composition;
}

With these additional parameters, we are able to modify the allergy search by specifying the maximum age in days of an allergy as well as the maximum number of allergies to report. We add the descending sort by date to ensure that the most recent allergies are included if the total number of matching allergies exceeds the maximum specified.

An example call for this script could include the following inputParams argument:

{
  "inputParams": {
    "patient": "Patient/1359",
    "author": "Device/1453",
    "custodian": "Organization/1452",
    "encounter": "Encounter/1454",
    "maxAllergies": 50,
    "maxAllergyAge": 30    
  }
}

There are many additional body params one could include when calling /apply for a template. For complete details on how the REST api works, check out the CDA Exchange Endpoint documentation.

14.1.7Putting it all together

 

From the examples above, it should be fairly clear how to extend the functionality and modularity of a Cda Template script. To learn more about what functions are available to you for use in your script, please read the JavaScript Execution Environment docs. The sections most relevant to Cda Templating would be the JavaScript FHIR REST API, the Composition Resource section, and the Composition Section section.

Once you have a template script you are happy with, the next step is to add it to your Smile CDR Cda module. A high level overview is available in the next section of these docs, or to view the more granular details, check out the CDA Exchange Endpoint part of the JSON Admin API docs.