FHIR r4 Adaptor
Language support for FHIR version 4. This adaptor is compatible with any FHIR R4 compliant server.
The FHIR 4 adaptor is implemented in TypeScript and comes complete with full TypeScript definitions.
This adaptor is strictly only compatible with FHIR R4. But all versions are supported by our generic fhir adaptor!
About FHIR
FHIR stands for Fast Healthcare Interoperability Resources. It is a standard for representing and exchanging healthcare data electronically.
Learn more about FHIR and FHIR for heath data exchange.
Authentication
The FHIR standard does not directly prescribe authentication and authorization methods. Instead, it provides security guidelines and leaves the choice of implementation to the developers of FHIR servers and clients. See the FHIR docs for the latest security-related recommendations. Depending on the FHIR systems being integrated via OpenFn, you might employ a Basic Auth, API key, or OAuth authentication scheme.
See this adaptor's Configuration docs for more on the required authentication parameters.
See platform docs on managing credentials for how to configure a credential in OpenFn. If working locally or if using a Raw JSON credential type, then your configuration will look something like this to define your target endpoint and FHIR version:
{
"baseUrl": "https://hapi.fhir.org", //fhir endpoint
"apiPath": "baseR4" //fhir version
}
Code Assist
The OpenFn Inspector provides full code-assist and code-complete for all FHIR resource types.
Pressing ctrl + space
will open up the Suggestions list, which provides
contextual auto-completion. Use this to more easily create FHIR resources.
Note that code assist will only provide suggestions for commonly used properties, but custom properties can also be passed directly and will be written to the new resource
Bundles
The FHIR-4 adaptor provides support for bundles.
You can create resources and add them to a bundle:
addToBundle($.resource);
Where $.resource
is a FHIR resource, or an array of resources, on the state
object.
This will add the resources to a bundle resource, under the entry
key, on
state under a key called bundle
. To save the bundle under a different key,
pass a string as the second argument:
addToBundle($.resource, 'patients-bundle');
Each item in the bundle will be given a request
object with the PUT
method
and a URL. For example:
addToBundle({
id: 'x',
resourceType: 'Patient',
});
Will create a state object like this:
{
"bundle": {
"resourceType": "bundle",
"entry": [
{
"resource": {
"id": "x",
"resourceType": "Patient"
},
"request": {
"method": "PUT",
"url": "Patient/x"
}
}
]
}
}
To send the Bundle to the FHIR server defined in state.configuration.baseURL
,
call uploadBundle()
.
You can pass the name of a key on state to upload a specific bundle:
uploadBundle('patients-bundle');
Or you can pass your own bundle object in directly:
uploadBundle($.bundle);
Builder Functions
The FHIR-4 adaptor provides builder functions which make it easier to define FHIR resources.
Builders provide short-hand properties and default values, reducing the boiler-plate needed to create FHIR resources. They also enable code-assist to be provided by code editors.
The basic pattern is something like this:
create(
builders.patient({
id: 'example',
identifier: [
{
value: '12345',
type: ['MR', 'http://terminology.hl7.org/CodeSystem/v2-0203'],
system: 'urn:oid:1.2.36.146.595.217.0.1',
period: { start: '2001-05-06' },
},
],
active: true,
name: [
{
family: 'Chalmers',
given: ['Peter', 'James'],
},
],
managingOrganization: 'Organization/1',
})
);
Basic Usage
All builders are available on the b
(short for builders
) namespace within
your OpenFn expression.
Every resource in FHIR should have a corresponding builder function on the b.
namespace (check the reference for details): for example, b.patient()
,
b.observation()
, b.encounter()
and b.invoice()
.
The first argument for the builder is an object with properties corresponding to FHIR json:
b.patient({
identifier: $.data.patientId,
});
Which will generate the following data structure:
{
"resourceType": "Patient"
"id": "123",
}
You don't have to use builder functions - you can just write our your FHIR structures in-line. But the builders come with some key benefits:
- They backed by full TypeScript definitions, so you'll get validation and code-assist as you type
- They simplify some FHIR data structures
The builders are designed to take a fuzzy, relaxed definition of a FHIR resource and convert it to correct JSON. For example, a Reference type can be passed as a fully FHIR reference object, a string id, or an existing FHIR resource. The builder will figure out how to parse and map it to a Reference with reasonable defaults.
Builders are not operations, so they don't sit at the top of your job code. You can either pass them as an argument to an operation:
create(b.patient($.data));
Or within a callback function (like fn()
):
fn(state => {
state.newPatient = b.patient(state.data);
return state;
});
Cardinality
Resource builders will automatically embed values into arrays where appropriate, simplifying cardinality handling.
For example, a Patient's identifier
property contains an array of identifiers.
But when using a builder function, you can pass a single identifier:
b.patient({
identifier: {
value: 'abc',
},
});
Which will be embedded into an array for you:
{
"resourceType": "Patient",
"identifier": [
{
"value": "abc"
}
]
}
DataTypes Builders
The builders have special handling and helpers for FHIR datatypes, like
identifier
and coding
.
Like resource builders, they exist on the b.
or builders.
namespace. Unlike
the other builders, they are hand-written and not auto-generated from the spec.
This means they have a slightly different API.
The most important datatype builders are detailed below.
When you pass an object into a builder, internally that object will be passed to a datatype builder.
So you can either pass a reference directly:
b.patient({
identifier: {
value: 'abc',
},
});
Or use a builder explicitly:
b.patient({
identifier: b.id('abc'),
});
You may want to use a builder to pass an extension.
b.patient({
identifier: b.identifier('abc', {
url: 'http://hospital.example.org/fhir/StructureDefinition/identifier-issuingDepartment',
value: 'Emergency Department',
}),
});
Identifiers
Use b.identifier(id, ...extensions)
or its shorthand,
b.id(id, ...extensions)
, to create an Identifier object.
You can pass a single string value to create an identifier without a system:
b.identifier('ip_request1.1');
More commonly, you'll pass a { value, system }
pair:
b.identifier({
system: 'http://moh.gov.et/fhir/hiv/identifier/MRN',
value: 'MRN12345671',
});
Any additional arguments will be treated as extensions:
b.identifier(
{
system: 'http://moh.gov.et/fhir/hiv/identifier/MRN',
value: 'MRN12345671',
},
{
url: 'http://hospital.example.org/fhir/StructureDefinition/identifier-issuingDepartment',
valueString: 'Emergency Department',
},
{
url: 'http://hospital.example.org/fhir/StructureDefinition/identifier-issuedDate',
valueDateTime: '2023-06-15T14:30:00Z',
}
);
Resulting in the following JSON structure:
{
"resourceType": "Patient",
"identifier": [
{
"system": "http://moh.gov.et/fhir/hiv/identifier/MRN",
"value": "MRN12345671",
"extension": [
{
"url": "http://hospital.example.org/fhir/StructureDefinition/identifier-issuingDepartment",
"valueString": "Emergency Department"
},
{
"url": "http://hospital.example.org/fhir/StructureDefinition/identifier-issuedDate",
"valueDateTime": "2023-06-15T14:30:00Z"
}
]
}
]
}
Codings
Use b.coding(code, system, extra)
or its shorthand,
b.c(code, system, extra)
, to create a Coding object
You can create a coding directly with the coding
builder:
b.coding('MR', 'http://terminology.hl7.org/CodeSystem/v2-0203');
// returns { code: 'MR', system: 'http://terminology.hl7.org/CodeSystem/v2-0203' }
You can also use the shorthand form, c
:
b.c('MR', 'http://terminology.hl7.org/CodeSystem/v2-0203', {
display: 'Medical record number',
});
// returns { code: 'MR', system: 'http://terminology.hl7.org/CodeSystem/v2-0203' }
You can pass any extra properties to the coding to add them to the object. Standard FHIR properties are type-supported and code-assisted.
b.c('MR', 'http://terminology.hl7.org/CodeSystem/v2-0203', {
display: 'Medical record number',
});
// returns {
// code: 'MR',
// system: 'http://terminology.hl7.org/CodeSystem/v2-0203',
// display: 'Medical record number' ,
// }
Codeable Concepts
Use b.concept(codings, extra)
or its shorthand, b.cc(codings, extra)
, to
create a Codeable Concept with one or more codings.
Pass one or more codings, as tuples or objects, and optionally extra keys to add to the concept:
b.concept(codings, props);
For example, to create a concept with a single coding:
b.concept(['MR', 'http://terminology.hl7.org/CodeSystem/v2-0203']);
// outputs { coding: [{ type: 'MR', system: 'http://terminology.hl7.org/CodeSystem/v2-0203' }]}
Or for multiple codings (note the extra array!)
b.concept([
['M', 'http://terminology.hl7.org/CodeSystem/v3-MaritalStatus'],
['02', 'http://national-registry.example.org/marital-status'],
]);
You can add extra props to the concept, like text:
b.concept($.codings, { text: 'Married' });
// outputs { text: 'Married', coding: [...]} }
Where a CodeableConcept is expected, it can be represented as a "tuple", or an
array of two strings where the first value is the code, and the second is the
system. The tuple will be expanded to a { code, system }
Coding object.
For example, an Identifier's type
accepts a CodeableConcept. We can create a
patient like this:
builders.patient({
identifier: [
{
type: ['MR', 'http://terminology.hl7.org/CodeSystem/v2-0203'],
},
],
});
Which will generate a resource like:
{
"resourceType": "Patient",
"identifier": [
{
"type": {
"code": "MR",
"system": "http://terminology.hl7.org/CodeSystem/v2-0203"
}
}
]
}
You can also pass a concept object directly, with full type support.
References
Some FHIR resources reference others. Reference types in the builders provide a number of short-hands.
For example, a FHIR reference looks like this:
"managingOrganization": {
"reference": "Organization/1"
}
In the patient builder, we can provide this reference in a few ways.
You can pass the reference string directly, which will be "expanded" into a reference.
b.patient({
managingOrganization: 'Organization/1',
});
You can pass an existing reference object (useful when mapping FHIR to FHIR)
b.patient({
managingOrganization: $.patient.managingOrganization,
});
Or you can pass an existing FHIR resource, if you happen to have access to the one you want to reference:
b.patient({
managingOrganization: $.myOrg,
});
Where supported, you can also pass an array of references.
You can use b.reference()
to create your own reference with the above rules
(particularly useful when dealing with extensions which aren't typed).
b.patient({
managingOrganization: b.ref($.org), // $.org can be a string, resource or reference
});
Multiple Types
Some FHIR properties are polymorphic and support multiple data types. For
example, a Patient's deceased
property can be of type boolean
or datetime
.
In FHIR json, this results in a type suffix being appended to the key. For example:
{
"resourceType": "Patient",
"deceasedBoolean": false,
"deceasedDateTime": "2025-01-01"
}
Builder functions allow you to specify the base property name and not worry about typing the key.
For example, these both work in a FHIR-compliant way:
b.patient({
deceased: true,
});
b.patient({
deceased: '2025-01-01',
});
Internally, the builder will determine the type of the incoming value and generate the correct key.
Note that passing suffixed keys still works just fine.
b.patient({
deceasedDateTime: '2025-01-01',
});
Extensions
FHIR is highly extensible, and the adaptor builder APIs have many features to support this.
Any property can be prefixed with _
to provide an extension, e.g,,:
b.identifier({ _use: 'custom use' });
You can add an extension to any resource using the
addExtension(resource, extensionUrl, extensionValue)
builder.
For example, the following code:
fn(state => {
const patient = b.patient({});
b.addExtension(
patient,
'http://hl7.org/fhir/StructureDefinition/patient-religion',
// declare a codeableConcept
b.cc(
['1023', 'http://terminology.hl7.org/CodeSystem/v3-ReligiousAffiliation'],
{ text: 'Muslim' }
)
);
return state;
});
Creates a patient with an religious affiliation
extension:
{
"resourceType": "Patient",
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/patient-religion",
"valueCodeableConcept": {
"coding": [
{
"code": "1023",
"system": "http://terminology.hl7.org/CodeSystem/v3-ReligiousAffiliation"
}
],
"text": "Muslim"
}
}
]
}
You can call addExtension
Multiple times for a given resource. It will
intelligently manage the extension
array on the target resource for you.
In addition, some datatype builders, like, identifier
, allow extensions to be
passed as extra arguments. For example:
b.identifier('12345', {
url: 'http://hospital.example.org/fhir/StructureDefinition/identifier-issuingDepartment',
value: 'Emergency Department',
});
Produces an identifier like:
{
"extension": [
{
"url": "http://hospital.example.org/fhir/StructureDefinition/identifier-issuingDepartment",
"valueString": "Emergency Department"
}
],
"value": "12345"
}
System Mappings
FHIR builders allow system
strings in identifiers and codings to be mapped.
This allows you to specify short-form system strings, or more easily map
resources between FHIR systems.
This is done using the util.setSystemMap(map)
function. This takes an object
which maps source strings to destinations. Keys in the object are values you
want to use in your job code - the values you pass to the builders. The values
of the object are the target values - the systems you want to appear in your
generated resources.
Set the system map at the top of every step that needs a system map (note that mappings are NOT remembered between steps)
util.setSystemMap({ icd10: 'http://hl7.org/fhir/sid/icd-10' });
Because systems tend to be long strings, it can be convenient to define a short-hand syntax, which simplifies mapping code.
For example:
b.setSystemMap({
smartcare: 'http://moh.gov.et/fhir/hiv/identifier/SmartCareID',
});
create(
b.patient({
identifier: { value: 'xyz', system: 'smartcare' },
})
);
This will generate a resource with a full system:
{
"resourceType": "Patient"
"identifier": [
{
"system": "http://moh.gov.et/fhir/hiv/identifier/SmartCareID",
"value": "xyz"
}
],
}
You can also use the datatype helpers directly:
util.setSystemMap({ icd10: 'http://hl7.org/fhir/sid/icd-10' });
fn(state => {
const headache = b.coding('G44.1', 'icd10');
// ...
return state;
});
This is also useful when mapping resources from one FHIR system to another. Simply configure a system map and source systems will auto-map to the destinations:
// map incoming CDR systems to MOH systems
util.setSystemMap({
'http://cdr.aacahb.gov.et/Encounter':
'http://moh.gov.et/fhir/hiv/identifier/encounter',
});
b.patient({
identifier: $.patient.
});