Section: 22

terminologies-systems.html

Code Systems

Maturity Level: 3Standards Status:Trial Use

The following names (URIs) may be used in the system element of the Coding data type. If a URI is defined here, it SHALL be used in preference to any other identifying mechanisms. If a code system is not listed here, the correct URI may be determined by working through the following list, in order:

See also the list of known identifier systems that can be used in the system element of the Identifier data type. Additional identifier systems may be registered on the HL7 FHIR registry at http://hl7.org/fhir/registry.

Important Notes:

Note that the code systems are available as FHIR resources - see the Downloads Page (as part of the FHIR definitions) (excluding the External code systems.

UMLS Note: The UMLS includes many code systems that do not have an entry in the table below, or in the HL7 OID registry, or on http://registry.fhir.org. Until these code systems are registered, implementers may use the pattern http://www.nlm.nih.gov/research/umls/[SAB], where [SAB] is the lowercase of abbreviation of the code system as provided by UMLS. e.g. The system for "Multum MediSource Lexicon" would then be http://www.nlm.nih.gov/research/umls/mmsl. Implementers choosing to use this pattern should be aware that very often, when the code system is registered, it will use a different system.

Don't see what you're looking here? See Registering Code Systems for further advice.

URI Source Comment OID (for non-FHIR systems)
Externally Published code systems
http://snomed.info/sct SNOMED CT (IHTSDO) See Using SNOMED CT with FHIR 2.16.840.1.113883.6.96
http://www.nlm.nih.gov/research/umls/rxnorm RxNorm (US NLM) See Using RxNorm with FHIR 2.16.840.1.113883.6.88
http://loinc.org LOINC (LOINC.org) See Using LOINC with FHIR 2.16.840.1.113883.6.1
http://unitsofmeasure.org UCUM: (UnitsOfMeasure.org) Case Sensitive Codes See Using UCUM with FHIR 2.16.840.1.113883.6.8
http://ncimeta.nci.nih.gov NCI Metathesaurus See Using NCI Metathesaurus with FHIR 2.16.840.1.113883.3.26.1.2
http://www.ama-assn.org/go/cpt AMA CPT codes See Using CPT with FHIR 2.16.840.1.113883.6.12
http://hl7.org/fhir/ndfrt NDF-RT (National Drug File – Reference Terminology) See Using NDF-RT with FHIR 2.16.840.1.113883.6.209
http://fdasis.nlm.nih.gov Unique Ingredient Identifier (UNII) See Using UNII with FHIR 2.16.840.1.113883.4.9
http://hl7.org/fhir/sid/ndc NDC/NHRIC Codes See Using NDC with FHIR 2.16.840.1.113883.6.69
http://hl7.org/fhir/sid/cvx CVX (Vaccine Administered) See Using CVX with FHIR 2.16.840.1.113883.12.292
urn:iso:std:iso:3166 ISO Country & Regional Codes See Using ISO 3166 Codes with FHIR 1.0.3166.1.2.2
http://hl7.org/fhir/sid/dsm5 DSM-5 Diagnostic and Statistical Manual of Mental Disorders, Fifth Edition (DSM-5) 2.16.840.1.113883.6.344
http://www.nubc.org/patient-discharge NUBC code system for Patient Discharge Status National Uniform Billing Committee, manual UB-04, UB form locator 17 2.16.840.1.113883.6.301.5
http://www.radlex.org RadLex (Includes play book codes) 2.16.840.1.113883.6.256
ICD-9, ICD-10 WHO) & National Variants See Using ICD-[x] with FHIR See ICD page for details
http://hl7.org/fhir/sid/icpc-1
http://hl7.org/fhir/sid/icpc-1-nl
http://hl7.org/fhir/sid/icpc-2
ICPC (International Classification of Primary Care) (PH3C)
NHG Table 24 ICPC-1 (NL)

2.16.840.1.113883.2.4.4.31.1
2.16.840.1.113883.6.139
http://hl7.org/fhir/sid/icf-nl ICF (International Classification of Functioning, Disability and Health) (WHO) 2.16.840.1.113883.6.254
http://terminology.hl7.org/CodeSystem/v2-[X](/v) Version 2 tables [X] is the 4 digit identifier for a table; e.g. http://terminology.hl7.org/CodeSystem/v2-0203
Note: only some tables may be treated in this fashion. For some tables, the meaning of the code is version dependent, and so additional information must be included in the namespace, e.g. http://terminology.hl7.org/CodeSystem/v2-0123/2.3+, as defined in the v2 table namespace list. Version 2 codes are case sensitive.
2.16.840.1.113883.12.[X]
http://terminology.hl7.org/CodeSystem/v3-[X] A HL7 v3 code system [X] is the code system name; e.g. http://terminology.hl7.org/CodeSystem/v3-GenderStatus. HL7 v3 code systems are case sensitive. see v3 list
http://www.whocc.no/atc Anatomical Therapeutic Chemical Classification System (WHO) 2.16.840.1.113883.6.73
urn:ietf:bcp:47 IETF language (see Tags for Identifying Languages - BCP 47) This is used for identifying language throughout FHIR. Note that usually these codes are in a code and the system is assumed
urn:ietf:bcp:13 Mime Types (see Multipurpose Internet Mail Extensions (MIME) Part Four - BCP 13) This is used for identifying the mime type system throughout FHIR. Note that these codes are in a code (e.g. Attachment.contentType and in these elements the system is assumed). This system is defined for when constructing value sets of mime type codes
urn:iso:std:iso:11073:10101 Medical Device Codes (ISO 11073-10101) See Using MDC Codes with FHIR 2.16.840.1.113883.6.24
http://dicom.nema.org/resources/ontology/DCM DICOM Code Definitions The meanings of codes defined in DICOM, either explicitly or by reference to another part of DICOM or an external reference document or standard 1.2.840.10008.2.16.4
http://hl7.org/fhir/sid/ca-hc-din Health Canada Drug Identification Number

A computer-generated eight digit number assigned by Health Canada to a drug product prior to being marketed in Canada. Canada Health Drug Product Database contains product specific information on drugs approved for use in Canada.

2.16.840.1.113883.5.1105
http://hl7.org/fhir/sid/ca-hc-npn Health Canada Natural Product Number

A computer-generated number assigned by Health Canada to a natural health products prior to being marketed in Canada.

2.16.840.1.113883.5.1105
http://nucc.org/provider-taxonomy NUCC Provider Taxonomy

The Health Care Provider Taxonomy code is a unique alphanumeric code, ten characters in length. The code set is structured into three distinct "Levels" including Provider Type, Classification, and Area of Specialization.

Copyright statement for NUCC value sets:

This value set includes content from NUCC Health Care Provider Taxonomy Code Set for providers which is copyright © 2016+ American Medical Association. For commercial use, including sales or licensing, a license must be obtained
2.16.840.1.113883.6.101
Code Systems for Genetics
http://www.genenames.org HGNC: Human Gene Nomenclature Committee 2.16.840.1.113883.6.281
http://www.ensembl.org ENSEMBL reference sequence identifiers Maintained jointly by the European Bioinformatics Institute and Welcome Trust Sanger Institute not assigned yet
http://www.ncbi.nlm.nih.gov/refseq/ RefSeq: National Center for Biotechnology Information (NCBI) Reference Sequences 2.16.840.1.113883.6.280
http://www.ncbi.nlm.nih.gov/clinvar/ ClinVar Variant ID NCBI central repository for curating pathogenicity of potentially clinically relevant variants not assigned yet
http://sequenceontology.org Sequence Ontology not assigned yet
http://varnomen.hgvs.org/ HGVS : Human Genome Variation Society 2.16.840.1.113883.6.282
http://www.ncbi.nlm.nih.gov/projects/SNP DBSNP : Single Nucleotide Polymorphism database 2.16.840.1.113883.6.284
http://cancer.sanger.ac.uk/
cancergenome/projects/cosmic
COSMIC : Catalogue Of Somatic Mutations In Cancer 2.16.840.1.113883.3.912
http://www.lrg-sequence.org LRG : Locus Reference Genomic Sequences 2.16.840.1.113883.6.283
http://www.omim.org OMIM : Online Mendelian Inheritance in Man 2.16.840.1.113883.6.174
http://www.ncbi.nlm.nih.gov/pubmed PubMed 2.16.840.1.113883.13.191
http://www.pharmgkb.org PHARMGKB : Pharmacogenomic Knowledge Base PharmGKB Accession ID 2.16.840.1.113883.3.913
http://clinicaltrials.gov ClinicalTrials.gov 2.16.840.1.113883.3.1077
http://www.ebi.ac.uk/ipd/imgt/hla European Bioinformatics Institute 2.16.840.1.113883.6.341
URI (all prefixed with http://hl7.org/fhir/) Description OID
URI (all prefixed with http://terminology.hl7​.org/CodeSystem/) Description OID

Note: V2 code systems may or may not be case sensitive. V2 Code systems will have the CodeSystem.caseSensitive correctly populated in a future version of this specification.


terminologies-valuesets.html

Value Sets Defined in FHIR

Maturity Level: N/AStandards Status:Informative

This table contains a list of all the value sets defined as part of the FHIR specification. Some of these value sets include codes defined elsewhere, some contain their own inline code system definitions, and some do both. Any implicit code systems are case sensitive, though FHIR will never define codes that only differ by case.

Note that these value sets are available as FHIR resources - see the Downloads Page (as part of the FHIR definitions)

URI Description OID

terminologies.html

Using Codes in Resources

Maturity Level: NormativeStandards Status:Normative

Many elements in the FHIR resources have a coded value: some fixed string (a sequence of characters) assigned elsewhere that identifies some defined "concept". The sequence of characters and its meaning may be defined in one of several places:

These methods of defining codes are collectively called "code systems". This list is far from complete; there are many ways to define code systems, and they vary widely in sophistication and size.

Throughout this specification, coded values are always treated as a pair composed of "system" and "code", where the system is a URL that identifies the code system that defines the codes. Note that system values are always case sensitive. Different code systems make their own rules as to whether the codes they define are case sensitive or not. Note that all the codes defined by FHIR itself are case sensitive and SHALL be used in the provided case (usually, but not always, lowercase).

The FHIR framework for using coded values is based on the fundamental framework defined in section 5 of the HL7 v3 Core Principles document, including the separation between code systems and value sets.

The general pattern for representing coded elements is using the following four elements:

system A URI that identifies the system (see below)
version Identifies the version of the system (see Code System Versioning)
code A string pattern that identifies a concept as defined by the code system
display A description of the concept as defined by the code system

The Coding data type represents this patern. This example shows a LOINC code with the LOINC system, the version of LOINC used for the definition, and the display assigned by LOINC:

  
{ 
  "system" : "http://loinc.org",
  "version" : "2.62",
  "code" : "55423-8",
  "display" : "Number of steps in unspecified time Pedometer"
}
When codes are carried in resources, one of several different data types is used:
codeThe instance represents the code only. The system is implicit - it is defined as part of the definition of the element, and not carried in the instance.
CodingA data type that has a code and a system element that identifies where the definition of the code comes from
CodeableConceptA type that represents a concept by plain text and/or one or more coding elements (See the datatype notes for a discussion of code translations and using text in CodeableConcept)
In addition, the following data types also carry coded values, or content that can be treated as a code and bound to a valueset:
QuantityThe instance has system and code elements for carrying a code for the type of unit, and these can be bound to a value set
stringThe instance carries a string. In some cases applications may wish to control the set of valid strings for a particular element, so the string value can be treated as a coded element (like code)
uriLike string, URIs can be treated as a coded element

Notes:

Coded Values, Code Systems, and Value Sets

The set of coded values that is allowed in an element is known as a "value set". Anywhere these data types are used, the specification "binds" a value set to the element, and for the types code, Coding, and CodeableConcept, always does.

The difference between a code system and a value set is an important distinction that is easily missed by implementers, since the difference is often overlooked in system design. For instance, it's not unusual to see an application table that is a mixed list of codes, containing some LOINC codes and also some additional in-house codes. Quite often, there is no explicit differentiation between them; only the fact that a code happens to look like a LOINC code betrays its origin.

For data exchange, on the other hand, explicitly tracking the source of the code is both important and necessary. In order to do this, each code system that defines codes is assigned a URL that identifies it, and all the codes it defines are actually a pair ("Code Pair": a name with a namespace). So in the case of this mixed list example from the previous paragraph, there are two code systems: LOINC (http://loinc.org) and a local one (let's say it has been given the URL: http://example.com/codesystems/additional-test-codes). The application table is a single value set (a set of Code Pairs) that includes codes from each of those two namespaces. The value set itself is given its own URL as an identifier (e.g. "http://example.com/fhir/ValueSet/test-codes"). This identifies the set of Code Pairs, but is never used as the namespace in an actual code pair, or in an instance. In FHIR, Code Pairs are always represented as "code" and "system", except for the simple data type code where the namespace (e.g. the system element/property) is fixed in the schema and not represented explicitly.

Note that for some code systems, there is a single correct mechanism by which to represent codes defined by the system as a single URL. These single URLs are used in the context of the RDF format to enable ontological reasoning. The URL is often a direct reference to a web source that can provide additional definitional material about the concept. Where the mechanism is known, and defined by the code system, it is described in this specification.

Choosing a system

The URL in a system is always a reference to a code system, not to a value set. The system ensures that codes can be unambiguously traced back to their original definition, and that logical comparisons, matching and inferences can be performed consistently by different systems. For this reason, choice of the correct URI for the system attribute is critical.

The correct value to use in the system for a given code system can be determined by working through the following list, in order:

If a code system is not resolved by this list, and there is no publisher to consult, implementers must choose a URI to use. The priority should be to choose a unique value that won't accidentally be used by another implementer for a different purpose - or a very similar purpose with a different scope. See Registering Code Systems for further advice.

For publishers of code systems, the following considerations should be kept in mind when defining the correct URI to use:

Note: if the code system is made available packaged inside a ValueSet resource, the correct URL for the system value is ValueSet.codeSystem.system, not ValueSet.uri.

Complex Code Systems

All code systems define a set of concepts, assign specific codes to them, and provide definitional material to guide implementers in the correct use and understanding of the codes. Many code systems define relationships between the different concepts - is-a, part-of, classifies-with, and many other relationships. These features are represented in the CodeSystem resource, and exchanged using one of the code data types described above.

Some code systems define rules for how complex expressions can be built using the basic concepts defined by the code system. This is sometimes referred to as "post-coordination". Some of the more notable code systems that define grammars for expressions are:

There are many others. Any expression defined by the code system is still regarded as a 'code' and represented as such.

This example shows a SNOMED CT expression:

  
{ 
  "system" : "http://snomed.info/sct",
  "code" : "128045006:{363698007=56459004}"
}

Note that there is no display defined for SNOMED CT expressions.

Controlling the use of Coded Values

When an element is bound to a value set, the binding has these properties:

NameA descriptive name used when presenting information about the binding
StrengthHow the binding should be understood - see below
ReferenceA URL that defines the value set. Usually, this is a direct reference to a ValueSet resource, but can be a more indirect reference, where the value set is inferred
DescriptionA text description of the use of the codes. If there is no reference, this must be populated. When there is a reference, this can be used to make additional notes about the use and implementation of the value set

A binding is always represented using an ElementDefinition.binding.

Value Set References

There are a number of places in the specification where value sets are referenced in order to bind a coded value to a value set:

ElementDefinition.binding.valueSetUsed to bind a defined element to a value set
ConceptMap.source[x] and .target[x]Used to indicate the scope of the mapping in the Concept Map - from one value set to another
Questionnaire.item.answerValueSetIndicates that answers to a set of questions come from a value set
ValueSet.compose.include.valueSetThe content of a value set includes the content in the imported value set too
OperationDefinition.parameter.binding.valueSetUsed to bind a defined parameter to a value set
ValueSet Reference ExtensionIndicates that a particular coded value was chosen from the specified value set

When referencing value sets, the reference is usually made to the definition of a value set - that is, a value set that defines what codes are in the value set. A terminology server is required to convert this definition to the actual expansion that specifies what codes are in the value set in the context of operation.

There are two types of value set references in this list, direct and logical.

Direct Value Set references

A direct value set reference has the type Reference, and refers directly to a ValueSet based on a URL, usually to a terminology server running a FHIR RESTful API. When accessing a value set based on this kind of reference, a system should access the URL directly (after converting a relative reference to an absolute reference according to the local context). If this process fails, the system is unable to resolve the value set and must handle the error appropriately.

Example:

GET fhir/Questionnaire/234

<Questionnaire>
  ...
  <question>
    <options>
      <reference value="ValueSet/234234"/>
    </options>
  </question>
  ....
</Questionnaire>

This specifies that the values for a particular questionnaire come from the ValueSet with id 234234 on the same FHIR end-point. To resolve this, the system would GET fhir/ValueSet/234234

Typically, a direct reference like this is good for in-process references, in closed or carefully managed eco-systems. In a more general context, these references tend to be fragile over time because web URLs - including RESTful API URLS - are easily reassigned. For this reason, systems are encouraged to use logical value set references.

Logical Value Set references

A logical value set reference has the type uri, where an absolute URI is provided that matches the one in ValueSet.url. The value set URL can - and is preferred to be - a web address that actually resolves directly to a fixed web address that serves as the authoritative source for that value set. Alternatively, the system can query its terminology server(s) to resolve a value set with that URL as its identity.

Example:

<StructureDefinition>
  ...
  <element>
    ...
    <binding>
      ...
      <valueSet value="http://hl7.org/fhir/ValueSet/clinical-findings"/>
    </binding>
    ...
  </element>
  ....
</StructureDefinition>

This specifies that the element is bound to the value set with a ValueSet.url of http://hl7.org/fhir/ValueSet/clinical-findings. One way to access this value set is to try GET http://hl7.org/fhir/ValueSet/clinical-findings - which works, for this value set - http://hl7.org/fhir/ValueSet/clinical-findings returns the authoritative value set for this URL.

Alternatively, the value set could be resolved using a local terminology server. If that's running a FHIR Terminology Server, then this would work like this:

GET fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings

if the terminology server knows the value set, then it will return the value set. If the URL doesn't resolve to an authoritative value set, and the terminology server(s) don't know the value set, the system is unable to resolve the value set and must handle the error appropriately.

The value set URL is allowed to be a URI such as a UUID (e.g. urn:uuid:c0e0d027-1250-4278-8f44-33a49dc67916). These value sets can never be accessed directly, and must come from a terminology server. Note that this specification defines many value sets that have a logical URL that is not resolvable (examples for SNOMED CT, RxNorm, LOINC)

Using a logical reference which is a direct reference to the authoritative value set is the easiest and most reliable approach. However, this requires suitable hosting arrangements, and cannot always be guaranteed, so it is not required.

Version specific Logical References

A value set has a two part identifier: a url, and a version. Some value sets only ever have a single 'version'; a revision of the value set contents will cause a new url to be assigned. Others, however, maintain the same URL, and change the version. A terminology server may have multiple value sets for the same ValueSet.url with different versions.

To be precise about which version of a value set is being referred to in a value set reference, append the version to the canonical URL with a '|' like this:

<valueSet value="http://hl7.org/fhir/ValueSet/clinical-findings|0.8"/>

This is a version specific reference to a value set. Searching for this on a terminology server would look like this:

GET fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings&version=0.8

Note that if a value set reference does not have a version, and the server finds multiple versions for the value set, the system using the value set should pick the latest version of the value set and use that. Note that this applies to all conformance resources.

Unbound

Note that as a matter of ongoing development, a few elements that have coded data types are not bound to any value set at all. Bindings are to be provided for these elements.

Binding Strengths

Almost all of the elements that have a coded data type are bound to a value set. The bindings are associated with various degrees of flexibility as to how closely the value set should be followed:

The precise conformance criteria for 'required' and 'extensible' binding strengths vary by the data type to which they are applied, as described in the paragraphs below.

Irrespective of the binding strength, when a StructureDefinition is used to describe local usage, it can bind the element to a different value set in order to be much more precise about exactly which coded values can be used for these elements, and/or increase the strength of the binding. There are different rules for this, depending on the binding strength, as discussed below. Generally it is expected that jurisdictions, projects and vendors will work together to choose actual working value sets.

Required

To be conformant, codes in this element SHALL be from the specified value set.

In the standard, this is generally used for elements where the value needs to be strictly controlled so that everyone can interpret it with confidence. Generally, this is used for elements with type code:

The other place where this is used is when profiling resources, and there is agreement within a particular context of use that a particular set of codes are the only ones that can be used. In these cases, the data type SHALL contain one of the values in the value set.

The following rules apply when required bindings are used with the CodeableConcept data type:

If a required binding is applied to an element with maximum cardinality > 1, the binding applies to all the elements.

The following rules apply when required bindings are used with the code data type:

When an element is bound to a required value set, derived profiles may state rules on which codes can be used, including removing codes from allowed use, but cannot specify new or additional codes for these elements.

Extensible

To be conformant, codes in this element SHALL be from the specified value set if any of the codes within the value set can apply to the concept being communicated.

Note that it is the value set binding that is extensible, not the value set itself.

If there is no applicable concept in value set (based on human review), an alternate concept (either system/code pair, or text) may be used instead. The alternate concept can have any level of specificity in an is-a hierarchy (see Condition instance #2).

If there is at least one applicable concept in the extensibly bound value set with a meaning which includes but is more general than the meaning that is intended to be represented by the element in the resource instance, then the code that is used in the instance SHALL be taken from the value set and should be the closest available match for the intended element instance meaning (i.e. neither more general or more specific). However, a more specific code that more completely represents the intended meaning may also be included in the instance as an additional Coding if the data type is CodeableConcept, but it cannot be used instead of the code from the value set. This helps ensure that systems know which codes they should expect to receive and build logic for and it facilitates interoperability.

When a code for an applicable concept in the extensibly bound value set is used it should always be the closest available match for the intended element instance meaning, regardless of hierarchical (i.e. "is-a") relationships in the underlying code system(s) or a particular hierarchical "level" of the concept. However, as noted above, a code for a concept that is outside of the extensibly bound value set with a meaning which is "covered by" (i.e. is more specific than or has an "is-a" relationship to) a concept that is included in the value set cannot be used. In that case a code for a concept that is in the extensibly bound value set SHALL be used in the element instance and it should be the code for the concept that most closely matches the intended element instance meaning.

If the data type is CodeableConcept, then, as noted above, one of the Coding values SHALL be from the specified extensibly bound value set if a code in that value set applies, but if no applicable code exists in the value set alternate code(s) from outside the value set may be used instead. If no codes, including local codes, are available, then text alone (in CodeableConcept.text) may be used.

If the data type is Coding, then the code/system SHALL be from the specified value set if a code applies, but if no suitable code exists in the value set, an alternate code may be provided in its place.

If an extensible binding is applied to an element with maximum cardinality > 1, the binding applies to all the elements.

Identified gaps in value sets should be submitted to the organization administering the value set in order to improve interoperability in the future.

Extensible bindings are used when there is consensus at the specification or profiling level about the coded values that should be used, but it is impossible to create a bounded list of codes that are known to cover all use cases, including ones that are yet to arise.

When an element is extensibly bound to a value set, derived profiles may state rules on which codes can be used, but cannot select new or additional codes for these elements unless no codes with appropriate meanings are found in the base value set.

Note that if the valueset-reference extension is being used and the code in the element instance is from outside the extensibly bound value set, the extension must reference a different value set that the code was chosen from (or if no other value set reference is available the extension cannot be used in that instance).

Examples for Extensible Bindings

See examples to help explain the difficult but important subject of Extensible bindings.

Preferred

Instances are encouraged to draw from the specified codes for interoperability purposes but are not required to do so to be considered conformant.

If the data type is CodeableConcept, then one of the Coding values SHOULD be from the specified value set, but another code and/or text can be used in its place.

Preferred bindings are used when there is consensus at the specification level about the coded values that are the best to be used, but there is recognition that some implementation contexts are unable to use the recommended codes for a variety of reasons. Applications should consider adopting the preferred value set wherever possible, as these preferred value sets are the most likely to serve interoperability purposes in the future.

When an element is bound to a preferred value set, derived profiles may bind the element to any value set they choose.

Examples for Preferred Bindings

See examples of how Preferred bindings work.

Example Bindings

Instances are not expected or even encouraged to draw from the specified value set. The value set merely provides examples of the types of concepts intended to be included.

Example bindings are used when an element has a very broad meaning (such as List.code), or there is no consensus over the correct codes to be used. For these bindings:

Some other coded value MAY be used, or (for a CodeableConcept), a text alternative MAY be provided. Example value sets are provided to assist implementers to understand the correct use of an element. Value sets based on code systems such as SNOMED CT that have restrictive license terms will only be used as example bindings in the base FHIR specification, though implementation guides for particular jurisdictions may adopt value sets that require licenses. In addition, well-specified realm-specific ValueSets may also be used as example bindings in the base specification.

When an element is bound to an example value set, derived profiles may bind the element to any value set they choose.

Other notes

Binding String Values

In a few special cases, humans customarily use codes directly for elements that have type "string". A typical case is codes for states, and there are several places where a URI must come from a set of controlled values. An element of type string or uri can also be bound to a value set. When a string or URI is bound to a value set, the value property SHALL contain the code specified by the value set, and the system and display values are ignored.

Terminology Service

FHIR has defined a Terminology Service specification which sets requirements for systems that support the use of codes, value sets and code systems.


terminology-module.html

Work Group Standards Status:Informative

Terminology Module

Introduction

The Terminology Module provides an overview and guide to the FHIR resources, operations, coded data types and externally-defined standard and FHIR-defined terminologies that are used for representing and communicating coded, structured data in the FHIR core specification and profiles. Collectively, these capabilities are used to provide the terminology service functionality required for supporting the use of coded data in FHIR resources throughout the specification as described in the other modules.

The terminology resources and their relationships are shown below:

Image showing the terminology resources and relationships

The ElementDefinition type (shown with a dotted box) is described elsewhere in the specification in the Foundation and Conformance modules.

Index

The Terminology Module covers the following:

Resources

Terminology Service

Operations

  • CodeSystem
  • ValueSet
  • ConceptMap

Coded Data Types

Documentation

Security and Privacy

For security considerations for terminology services, see the Terminology Service page Security section. For more general considerations, see the Security and Privacy module.

Common Use Cases

Development Roadmap

The following three terminology resources have been tested and are being used in production tooling, and as such they have reached a maturity level where changes become less likely and are now considered to be normative candidates. We intend to progress these resources through to normative status via the May and September 2018 R4 ballots.

Other terminology resources are still in earlier stages of development. We intend to continue to develop and test these resources and advance them through the Maturity Levels at Connectathons and elsewhere.


terminology-service.html

Terminology Service

Maturity Level: 4Standards Status:Trial Use

This specification includes support for the provision of a terminology service - that is, a service that lets healthcare applications make use of codes and value sets without having to become experts in the fine details of code system, value set and concept map resources, and the underlying code systems and terminological principles. A server that supports all the functionality described here can be described as a "FHIR Terminology Service", and SHALL conform to the Terminology Service Capability Statement. Note that servers can declare that they provide terminology services in their capability statement:

<CapabilityStatement xmlns="http://hl7.org/fhir">
  <!-- snip -->
  <instantiates value="http://hl7.org/fhir/CapabilityStatement/terminology-server"/>
  <!-- snip -->
</CapabilityStatement>

Security

Generally, SSL SHOULD be used for all production health care data exchange. Even though terminology servers do not generally handle patient information directly, observers may still be able to infer information about patients by observing the codes and concepts used in terminology service operations, so encryption is still recommended.

A terminology server may choose not to authenticate the clients/users in any fashion, but might need to do so in order to limit or account for usage, or enforce agreement to licensing terms. For a value set maintenance server that allows terminologies to be edited, some form of authorization and/or authentication would be appropriate. This specification does not require any particular approach to security.

Basic Concepts

A FHIR terminology service is simply a set of functions built on the definitions provided by a collection of CodeSystem, ValueSet and ConceptMap resources, with additional inherently known terminologies providing support.

The terminology service builds on the basic principles for using terminologies in FHIR. Implementers should be familiar with:

In addition, implementers should be familiar with the operations framework. Further useful information about terminologies may be found in:

External Code Systems

In order to be used with a value set, code systems and their content must be defined somewhere. They can be defined explicitly using the code system resource, or they can be defined elsewhere, and then used in a value set by referencing the correct system url. The FHIR specification defines a set of namespaces for commonly encountered code systems, and defines how some work with FHIR (e.g. SNOMED CT, LOINC, RxNorm). These code systems are often large, and have many internally defined properties that are part of their formal definitions. The CodeSystem resource is not an appropriate way to distribute the contents of these code systems; the standard FHIR code system resource simply represents the properties of the code system. Instead, these terminologies provide their own distribution formats, and it is assumed that the content of these code systems are externally known to the terminology server.

Most useful terminology servers will make one or more of these external code systems available for use within the value sets that they manage. The list of additional terminologies that a terminology server supports beyond those defined in its value sets is published to clients by referencing code system resources in the server's Capability Statement.

{
 "resourceType" : "CapabilityStatement",
 "extension" : [
   {
     "url" : "http://hl7.org/fhir/StructureDefinition/capabilitystatement-supported-system",
     "valueUri" : "http://loinc.org"
   }]
}

This extension is added to the root Capability Statement.

Implementation Note

When a terminology server exposes an external code system, it makes a set of services available internally that serve the operational interfaces below. The internal server depends on the following logical information for a terminology:

The FHIR specification itself defines these things for common terminologies (including SNOMED CT, LOINC, RxNorm), and provides the CodeSystem infrastructure for supporting typical relatively simple small code systems.

Implementers interested in working with existing published terminologies for which the CodeSystem infrastructure is not suitable should discuss their needs with HL7 to get the list above extended.

Note: A terminology service may choose to expose additional external code system specific related functionality such as summation, or structured search, but these services are outside the scope of the FHIR terminology service.

Operations across all value sets

For some of the operations below, it can be useful to perform them across all value sets known to the system. For example, $expand using a text filter, and searching all value sets at once. A special value set is defined that means "all value sets known to the server":

http://hl7.org/fhir/ValueSet/@all

Technically, this value set automatically imports all the existing value sets on the server. Note that this URL has no fixed meaning - its interpretation is server specific (e.g. whether it includes all versions of all value sets). This URL can only be used as a parameter to the operations described on this page.

Terminology Maintenance

The terminology service uses the code systems and value set resources defined on the system - both the implicit ones associated with the external code systems and those explicitly available at the /CodeSystem and /ValueSet endpoints - to serve the operational interface defined below. As code systems and value sets are created, updated or deleted, the outcomes of the operational services change. A terminology server should validate incoming resources, and ensure integrity of the terminology services. Typically, servers provide a test and production environment, but there is no explicit notion of this in the interface itself.

Value Set Expansion

A value set describes a set of rules for what codes or concepts are considered to be in the value set. These rules might be simple (e.g. a direct list of codes from a specified version of a code system), or they might be quite complex (e.g. all codes with a particular property from an unspecified version of a code system).

A FHIR-enabled application can simply ask a terminology server to figure out all the details, and return a list of the current codes in the value set. This is known as "expanding" the valueset. As a summary, the client passes the server the following information:

The server returns a value set that contains the current list of codes that meet the filter criteria (or an OperationOutcome with an error if the expansion fails). Note that some value sets expand to many thousands of codes, or even an infinite number, and for these, the server SHOULD return an error code too-costly. In these cases the client can try again with a more specific text filter to reduce the number of codes returned - this may result in a valid expansion.

For further information, consult the definition of the operation.

The $expand operation has support for paging - for a client to retrieve a big expansion in a set of partial views, in order to present the most optimal user experience. The client specifies both an offset and a count - how many codes per page, and where in the sequence to start. The return expansion specifies the number of concepts in the expansion, and the offset at which this partial view starts. Note that all expansions SHOULD include the total code count, but the offset element SHALL only exist when paging is being used. Expansions that are hierarchical trees of concepts are not subject to paging and the server simply returns the entire expansion.

Some example uses for the expansion operation:

Examples

Expanding a value set that is already registered on the server as "23", with a text filter of "abdo":

GET [base]/ValueSet/23/$expand?filter=abdo

Expanding a value set that is specified by the client (using JSON):

POST [base]/ValueSet/$expand
[other headers]

{
  "resourceType" : "Parameters",
  "parameter" : [
     {
     "name" : "valueSet",
     "resource" : {
       "resourceType" : "ValueSet",
     [value set details]
     }
   }
  ]
}

The server responds with a value set (this example in XML):

HTTP/1.1 200 OK
[other headers]

<ValueSet xmlns="http://hl7.org/fhir">
  <!-- the server SHOULD populate the id with a newly created UUID
    so clients can easily track a particular expansion  -->
  <id value="43770626-f685-4ba8-8d66-fb63e674c467"/>
  <!-- no need for meta, though it is allowed for security labels, profiles -->

  <!-- other value set details -->
  <expansion>
    <!-- when expanded -->
    <timestamp value="20141203T08:50:00+11:00"/>
  <contains>
    <!-- expansion contents -->
  </contains>
  </expansion>
</ValueSet>

Concept Lookup / Decomposition

A system can ask a terminology server to return a set of information about a particular system/code combination using the lookup operation. The server returns information for both display and processing purposes. The client passes the server the following information:

The server returns some or all of the following information:

The recommended display for the code is a text representation of the code that the terminology server recommends as the default choice to show to the user, though a client may choose out of the other designations if it has reason to.

If the client does not ask for any particular properties to be returned, it is at the discretion of the server to decide which properties to return (though note that the "version" property is always returned if the code system has a version).

Examples

Looking up a code in a code system:

GET [base]/CodeSystem/loinc/$lookup?code=1963-8

Note that the logical id "loinc" is not a reliable identifier across systems; each server assigns logical ids to code system resources however it sees fit. A more reliable query is this:

GET [base]/CodeSystem/$lookup?system=http://loinc.org&code=1963-8&property=code&property=display&property=designations

Lookup the code system using a Coding (this example in XML):

POST [base]/CodeSystem/$lookup
[other headers]

<Parameters xmlns="http://hl7.org/fhir">
  <parameter>
    <name value="coding"/>
    <valueCoding>
      <system value="http://loinc.org"/>
      <code value="1963-8"/>
    </valueCoding>
  </parameter>
</Parameters>

The server responds with a set of information (JSON this time):

HTTP/1.1 200 OK
[other headers]

{
  "resourceType" : "Parameters",
  "parameter" : [
    {
    "name" : "name",
    "valueString" : "LOINC"
  },
  {
    "name" : "version",
    "valueString" : "2.56"
  },
  {
    "name" : "display",
    "valueString" : "Bicarbonate [Moles/volume] in Serum"
  },
  {
    "name" : "abstract",
    "valueString" : "false"
  }
  ]
}

Standard Properties

The following properties are defined for all code systems:

Name Usage
system The name of the code system
version The version of the code system used for the look up operation
display The recommended display for the code, if one is known
definition The definition for the code
designation Other designations for the code
lang.X Designations in language X (where X is an IETF Language code, see BCP-47
parent Parent codes for this code (for code systems with a defined hierarchy)
child child codes of this code (for code systems with a defined hierarchy)

In addition, any property codes defined by the code system (CodeSysem.property.code) can be used (and see the definitions for SNOMED CT, LOINC, and RxNorm).

Value Set Validation

One way to determine whether a code is in a value set is to expand the value set (as described above), and then look at the returned codes to see if the code is in the expansion. However this is not an efficient way to test whether a code is valid, and for some value sets (e.g. with infinite number of members), it cannot work. Instead, a FHIR terminology server provides a "validate-code" operation. The client passes the server the following information:

The server returns a true/false indicating whether the code/concept is valid, and a list of errors and warnings associated with it. The server should also return an appropriate display for the concept for use in a UI context.

Note that if the server is passed a CodeableConcept, the server is able to check whether any of the codes are valid against the value set, and also check whether multiple codings are allowed and/or the codings provided are consistent with each other.

Every code system has an implicit value set that is "all the concepts defined in the code system" (CodeSystem.valueSet). For some code systems, these value set URIs are defined in advance (e.g. for LOINC, it is http://loinc.org/vs). However for some code systems, they are not known. Clients can refer to these implicit value sets by providing the URI for the code system itself.

Examples

Simple validation of a code/system against a known value set:

GET [base]/ValueSet/23/$validate-code?system=http://loinc.org&code=1963-8&display=test

Validate a CodeableConcept against a client specified value set (this example in JSON):

POST [base]/ValueSet/$validate-code
[other headers]

{
  "ResourceType" : "Parameters",
  "parameter" : [
    {
    "name" : "coding",
    "valueCodeableConcept" : {
      "coding" : {
        "system" : "http://loinc.org",
          "code" : "1963-8",
      "display" : "test"
      }
    }
  },
  {
    "name" : "valueSet",
    "resource": {
      "resourceType" : "ValueSet",
    [etc.]
    }
  }
  ]
}

The server responds with validation information (JSON this time):

HTTP/1.1 200 OK
[other headers]

{
  "resourceType" : "Parameters",
  "parameter" : [
    {
    "name" : "result",
    "valueBoolean" : false
  },
  {
    "name" : "message",
    "valueString" : "The display \"test\" is incorrect"
  },
  {
    "name" : "display",
    "valueString" : "Bicarbonate [Moles/volume] in Serum"
  }
  ]
}

Subsumption testing

To test the subsumption relationship between code/Coding A and code/Coding B, perform a $subsumes operation. Subsumption testing is based on the CodeSystem definition of subsumption. The client passes the server the following information:

If the client passes Codings, it is allowed to use code system values that are different from the code system in which subsumption testing is to be performed. In this case, the server SHALL return an error unless the relationships between the various code systems is well defined.

If the concepts can be compared, then the server returns an outcome code:

equivalentConcepts A and B are equivalent
subsumesConcept A subsumes Concept B
subsumed-byConcept A is subsumed by Concept B
not-subsumedConcepts A and B are not related by any subsumption relationship

Examples

Test whether a SNOMED CT Concept 'Disorder of liver' (235856003) subsumes 'Viral hepatitis' (3738000):

GET [base]/CodeSystem/$subsumes?system=http://snomed.info/sct&codeA=235856003&codeB=3738000

Or using Codings:


POST [base]/CodeSystem/$subsumes
[other headers]

<Parameters xmlns="http://hl7.org/fhir">
  <!-- Subsumption testing - use SNOMED CT rules -->
  <parameter>
    <name value="system"/>
    <valueUri value="http://snomed.info/sct"/>
  </parameter>
  <!-- Australian distribution -->
  <parameter>
    <name value="version"/>
    <valueString value="http://snomed.info/sct/32506021000036107/version/20160430"/>
  </parameter>
  <parameter>
    <name value="codingA"/>
    <valueCoding>
      <system value="http://snomed.info/sct"/>
      <code value="235856003"/>
    </valueCoding>
  </parameter>
  <parameter>
    <name value="codingB"/>
    <valueCoding>
      <system value="http://snomed.info/sct"/>
      <code value="3738000"/>
    </valueCoding>
  </parameter>
</Parameters>

Server response:

HTTP/1.1 200 OK
[other headers]

{
  "resourceType" : "Parameters",
  "parameter" : [
    {
    "name" : "outcome",
    "valueCode" : "subsumes"
  },
  ]
}

Batch Validation

It is also possible to validate a set of concepts against their relevant value sets by using the $validate-code operation in a Batch interaction.

Example

A request to validate 2 concepts from a CDA document, with OIDs for value set identifiers:

POST [base]
[other headers]

{
  "ResourceType": "Bundle",
  "type": "batch",
  "entry": [{
    "request": {
      "method": "Get",
      "url": "ValueSet/$validate-code?system=http://loinc.org&code=2324-4&uri=urn:oid:1.2.3.4.6"
    }
  },
  {
    "request": {
      "method": "GET",
      "url": "ValueSet/$validate-code?system=http://snomed.info/sct&codes=22298006&uri=urn:oid:1.2.3.4.7"
    }
  }]
}

The server responds with a series of validation outcomes (JSON this time):

HTTP/1.1 200 OK
[other headers]

{
  "ResourceType": "Bundle",
  "type": "batch-response",
  "entry": [{
    "resource": {
      "resourceType": "Parameters",
      "parameter": [{
        "name": "result",
        "valueBoolean": false
      },
      {
        "name": "message",
        "valueString": "'2324-4' is not a valid LOINC code"
      }]
    }
  },
  {
    "resource": {
      "resourceType": "Parameters",
      "parameter": [{
        "name": "result",
        "valueBoolean": false
      },
      {
        "name": "message",
        "valueString": "The concept is not in the specified value set (\"Organisms\")"
      },
      {
        "name": "display",
        "valueString": "Myocardial infarction"
      }]
    }]
  }

Translations

A client can ask a server to translate a concept from one value set to another. Typically, this is used to translate between code systems (e.g. from LOINC to SNOMED CT, or from a HL7 V3 code to a HL7 V2 code). The client calls the translate operation and passes the following parameters:

The client passes either a concept map, or the value sets for the source and destination context. If there is no concept map, then the server may determine the appropriate map to use from context provided in the value sets. If there is no particular context, the appropriate value sets would be the value sets for the entire coding system at question (e.g. from http://snomed.info/sct to http://loinc.org/vs). The server performs the translation as it is able based on the concept maps that it knows about. If no single mapping can be determined, then the server returns an error. Some servers may require a concept map to use for the translation.

Example

Translate from FHIR Composition status to HL7 v3 Act Status (based on this defined concept map):

GET [base]/ConceptMap/$translate?system=http://hl7.org/fhir/composition-status
  &code=preliminary&source=http://hl7.org/fhir/ValueSet/composition-status
  &target=http://terminology.hl7.org/ValueSet/v3-ActStatus

The server responds with validation information:

HTTP/1.1 200 OK
[other headers]

{
  "resourceType" : "Parameters",
  "parameter" : [
    {
    "name" : "result",
    "valueBoolean" : true
    },
    {
      "name" : "outcome",
      "valueCoding" : {
        "system" : "http://terminology.hl7.org/CodeSystem/v3-ActStatus",
        "code" : "active",
      }
    }
  ]
}

Batch Translation

It is also possible to translate a set of concepts against their relevant value sets by using the $translate operation in a Batch interaction.

Example

A request to translate 2 concepts from a CDA document, with OIDs for value set identifiers:

POST [base]
[other headers]

{
  "ResourceType": "Bundle",
  "type": "batch",
  "entry": [{
    "resource": {
      "ResourceType": "Parameters",
      "parameter": [{
        "name": "concept",
        "valueCodeableConcept": {
          "system": "http://loinc.org",
          "code": "2324-4"
        }
      },
      {
        "name": "target",
        "valueUri": "urn:oid:1.2.3.4.6"
      }]
    },
    "request": {
      "method": "POST",
      "url": "ConceptMap/$translate"
    }
  },
  {
    "resource": {
      "ResourceType": "Parameters",
      "parameter": [{
        "name": "concept",
        "valueCodeableConcept": {
          "system": "http://snomed.info/sct",
          "code": "22298006"
        }
      },
      {
        "name": "target",
        "valueUri": "urn:oid:1.2.3.4.7"
      }]
    },
    {
      "request": {
        "method": "POST",
        "url": "ConceptMap/$translate"
      }
    }]
  }

The server responds with a series of translation outcomes:

HTTP/1.1 200 OK
[other headers]

{
  "ResourceType": "Bundle",
  "type": "batch-response",
  "entry": [{
    "resource": {
      "resourceType": "Parameters",
      "parameter": [{
        "name": "result",
        "valueBoolean": false
      },
      {
        "name": "message",
        "valueString": "'2324-4' is not a valid LOINC code"
      }]
    }
  },
  {
    "resource": {
      "resourceType": "Parameters",
      "parameter": [{
        "name": "result",
        "valueBoolean": true
      },
      {
        "name": "outcome",
        "valueCodeableConcept": {
          "coding": {
            "system": "http://example.com/codesystems/example",
            "code": "xxxx"
          }
        }
      }]
    }
  }]
}

Maintaining a Closure Table

The 5 operations Expand, Lookup, Validate, Subsumes, and Translate account for most operational requirements associated with terminology use. However, there is one difficult but important use case that they do not address, which is integrating terminologically based logic into application searches.

A typical example of this is a user that wants to find any observations for male patients over the age of 50 who attended a particular clinic within a particular 2 week period, with a diagnosis of gout, and who had an elevated serum creatinine.

In this case, both "diagnosis of gout" and "serum creatinine" involve value set and/or subsumption queries (e.g. against SNOMED CT and LOINC respectively). This search has to be executed by some logical processing engine that knows how to find patient related data in a given persistence store. Often, this is some kind of SQL query, though many other technological choices are available. However, this is done, the challenge with an operation like this is to integrate the terminological knowledge into a search execution that also covers other relationships expressed in the search criteria.

One approach to this problem would be to using the expand operation above, so that the system executing the search could generate expansions, and then search for these expansions. This has a couple of problems:

An alternative approach is to generate a transitive closure table which lists all the possible transitive subsumption relationships, and allows for rapid execution of these kind of queries . However, this has other problems:

This is the main reason why most systems do not support post-coordination or other forms of coded expressions.

In FHIR, this problem is solved by building a closure table on the fly as new codes are seen. This technique leaves the FHIR terminology server responsible for the terminological reasoning and the client responsible for the closure table maintenance. To the client, it doesn't matter whether the concept is post-coordinated or not. Here's a description of how the process works:

  1. the client defines a name associated with a particular context in which it wishes to maintain a subsumption based closure table
  2. the client registers this name with the FHIR Terminology server using the $closure operation (described below), with only one parameter, the name of the context
  3. any time the client system encounters a new Coding that is not entered in the closure table, it calls the $closure operation with the context name, and the Coding value it has encountered
  4. the server returns a ConceptMap resource with a list of new entries (code : system -> code : system) that the client should add to its closure table
    • the server can indicate that entries should be removed from the table by providing a (code : system -> code : system) with equivalence "unmatched" (though it's not known why that would be needed)
  5. the client copies these entries into its closure table
  6. to facilitate the initialization process, a client can call $closure with multiple Coding values

The $closure operation takes 2 parameters:

The operation returns a concept map which has a list of mappings that represent new entries to make in the closure table. The subsumption testing performed when building a closure table is the same as for the $subsumes operation, and is based on the CodeSystem definition of subsumption.

The closure table can be resynchronized by passing an additional "version" parameter, which is a value taken from the version in one of the delta responses. This is a request to replay all the mapping changes since that delta was sent.

Initializing a Closure Table

Before it can be used, a closure table has to be initialised. To initialise a closure table, POST the following to [base]/ConceptMap/$closure:

{
  "resourceType" : "Parameters",
   "parameter" : [{
     "name" : "name",
     "valueString" : "[name]"
  }]
}

A successful response is a 200 OK from the server, with an associated ConceptMap:

{
    "resourceType": "ConceptMap",
    "id": "[name]",
    "version": "0",
    "name": "Closure Table [name] Creation",
    "status": "active",
    "experimental": true,
    "date": "2015-12-20T23:10:55Z"
}

If there is an error (usually involving the closure name) the server returns a HTTP status 400 with an operation outcome:

{
  "resourceType": "OperationOutcome",
  "text": {
    "status": "generated",
    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>invalid closure name \"invalid-id!\":</p></div>"
  },
  "issue": [
    {
      "severity": "error",
      "details": {
        "text" : "invalid closure name \"invalid-id!\""
      }
    }
  ]
}

What closure names are valid is at the discretion of the server.

Adding to a Closure Table

When the consumer (client) encounters a new code, it POSTs the following to [base]/ConceptMap/$closure:

{
  "resourceType" : "Parameters",
  "parameter" : [{
    "name" : "name",
    "valueString" : "[name]"
  }, {
    "name" : "concept",
    "valueCoding" : {
       "system" : "http://snomed.info/sct",
       "code" : "22298006",
       "display" : "Myocardial infarction"
    }
  }]
}

Note that this example only includes one concept, but more than one is allowed:

{
  "resourceType" : "Parameters",
  "parameter" : [{
    "name" : "name",
    "valueString" : "[name]"
  }, {
    "name" : "concept",
    "valueCoding" : {
       "system" : "http://snomed.info/sct",
       "code" : "22298006",
       "display" : "Myocardial infarction"
    }
  }, {
    "name" : "concept",
    "valueCoding" : {
       "system" : "http://snomed.info/sct",
       "code" : "128599005",
       "display" : "Structural disorder of heart"
    }
  }]
}

The response varies depending on the conditions on the server. Possible responses: If the closure table has not been initialised: Return a 404 Not Found with

{
  "resourceType": "OperationOutcome",
  "text": {
    "status": "generated",
    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>invalid closure name \"[name]\":</p></div>"
  },
  "issue": [
    {
      "severity": "error",
      "details": {
        "text" : "invalid closure name \"[name]\""
      }
    }
  ]
}

If the closure table needs to be reinitialised: Return a 422 Unprocessable Entity with

{
  "resourceType": "OperationOutcome",
  "text": {
    "status": "generated",
    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p>closure \"[name\" must be reinitialised</p></div>"
   },
   "issue": [{
       "severity": "error",
       "details": {
         "text" : "closure \"[name]\" must be reinitialised"
       }
     }
   ]
}

The server should only send this when its underlying terminology conditions have been changed (e.g. a new version of SNOMED CT has been loaded). When a client gets this, it’s only choice is to initialise the closure table, and process all the codes in the closure table again (the assumption here is that the system has some external source of ‘all the codes’ so it can rebuild the table again). If the concept(s) submitted are processed ok, but there’s no new concepts, or no new entries in the table, return a 200 OK with :

{
    "resourceType": "ConceptMap",
    "id": "[name]",
    "version": "[version]",
    "name": "Updates for Closure Table [name]",
    "status": "active",
    "experimental": true,
    "date": "2015-12-20T23:12:55Z"
}

If there’s new entries in the closure table, the server returns a 200 OK with:

{
  "resourceType": "ConceptMap",
  "id": "b87db127-9996-4d0c-bda9-a278d7a24a69",
  "version": "[version]",
  "name": "Updates for Closure Table [name]",
  "status": "active",
  "experimental": true,
  "date": "2015-12-20T23:16:24Z",
  "group": [{
    "source": "http://snomed.info/sct",
    "target": "http://snomed.info/sct",
    "element" : {
      "code": "22298006",
      "target": [{
        "code": "128599005",
        "equivalence": "subsumes"
      }]
    }
  }]
}

Notes

Re-running Closure operation

Given the way that the closure operation functions, it’s possible for a client to lose a response from the server before it is committed to safe storage (or the client might not have particularly safe storage). For this reason, when a client is starting up, it should check that there has been no missing operations. It can do this by passing the last version (from the Concept Map response) it is sure it processed in the request:

{
  "resourceType" : "Parameters",
   "parameter" : [{
     "name" : "name",
     "valueString" : "[name]"
  }, {
     "name" : "version",
     "valueString" : "3"
  }]
 }

That’s a request to return all the additions to the closure table since version 3. The server returns its latest version in the concept map, along with anything added to the closure table since version 3 (not including version 3)

Notes:

Making use of the Closure Table

The client uses the result of the closure operation to maintain a closure table. Simplistically, it might look like this:

Scope Source Target
patient-problems http://snomed.info/sct|22298006 http://snomed.info/sct|128599005
patient-problems http://snomed.info/sct|24595009 http://snomed.info/sct|90560007
obs-code http://loinc.org|14682-9 http://loinc.org|LP41281-4

The client can then use a table like this as part of its general search conditions. Using the example from above: "Find any observations for male patients over the age of 50 who attended a particular clinic within a particular 2 week period, with a diagnosis of gout, and who had an elevated serum creatinine." This query could be done, for instance, with an SQL query like this:

 Select * from Observations, Patients, Encounters, Conditions, Observations as Obs2 where
   Observations.patient = Patients.Key and Patients.Age > 50 and
   Observations.encounter = Encounters.Key and Encounter.clinic = [key]
     and encounter.date >= [date] and encounter.date <= [date] and
   Conditions.patient = Patients.Key and Conditions.code
     in (select Source From ClosureTable
       where Scope = "patient-problems" and Target = "http://snomed.info/sct|90560007") and
   Obs2.patient = Patients.Key and Obs2.value > 0.19 and Obs2.code
     in (select Source From ClosureTable
       where Scope = "obs-code" and Target = "http://loinc.org|LP41281-4")

Note that in real clinical systems, tables are usually far more structured than this example implies, and the query is correspondingly more complex. The closure table would usually be normalised - this example is kept simple to demonstrate the concept.


testing.html

Testing FHIR

Maturity Level: 2Standards Status:Draft

The FHIR specification describes a set of resources, and several different frameworks for exchanging resources between different systems. Because of its general nature and wide applicability, the rules made in this specification are fairly loose. As a consequence and in order to insure interoperability between applications claiming conformance to this specification, a testing framework has been established within the FHIR specification. To this end, the TestScript resource provides an implementation-agnostic description of tests that allows test execution engines to evaluate if a FHIR implementation conforms with the FHIR specification. Providing a clear and concise test methodology for the FHIR specification through the TestScript resource helps to enable interoperability among various FHIR server and client implementations.

Furthermore, the TestScript resource provides clear examples of the appropriate use of the FHIR specification through test-based documentation. The TestScript resource stands as a form of executable documentation allowing developers to examine the operations defined by the tests in order to understand how various RESTful API interactions and resources should be used in coordination. The tests can also be automatically executed against systems under development to determine how well the systems adhere to the specification.

The TestScript resource contains:

Execution

Workflow

Pre-Processing

The TestScript execution workflow begins by determining if the test suite is appropriate for the server under test. This can be determined by evaluating if the interactions listed in the TestScript metadata "capabilities" section are supported by the server's conformance resource. If the capabilities are supported by the server, then the TestScript can be executed. Otherwise, a test engine will provide a comparison of the system under test�s conformance capabilities and allow a TestScript as a whole to be skipped based on this comparison.. See How to specify metadata capabilities.

If the server supports the requirements of the TestScript instance, any specified fixtures are loaded or retrieved. If the fixtures are marked as 'autocreate' then they are automatically created on the server(s) under test using 'create' operations. If any of the autocreate operations fail, then the tests in the TestScript are skipped.

Setup Execution

After the fixtures are loaded and autocreates are executed, the setup section is executed to establish the testing environment. The purpose of the setup section is typically to pre-load data (if it was not autocreated) or delete data required for the execution of the tests against the FHIR system(s) under test. The setup operations are executed once before all the tests are run (see Operation Execution). All operations in a setup section (including assertions) must complete successfully for the subsequent tests to be executed. If an assertion operation in the setup section fails, then execution and evaluation of the tests in the TestScript should be skipped. Technically, any operation (see the operations table for a complete listing) can be included in the setup section, but typical operations will be create, update, read, and vread.

Due to the possibility that the setup actions are not required on the server under test, the TestScript execution workflow MAY provide the capability of skipping or ignoring the setup section of the TestScript.

Test Execution

Once setup is complete, each test is executed. Tests contain a set of operations, and executing a test involves the evaluation of each operation listed in the test in the order defined by the test (see Operation Execution and the list of operations).

Teardown Execution

After all the tests have completed execution, the teardown section is executed. The purpose of the teardown section is to revert the FHIR server under test to a pre-test clean state. This requires removing any resources or artifacts generated as part of test suite setup or test execution. Technically, any operation (see the operations table for a complete listing) can be included in the teardown section, but the most often used operation will be delete. Assertions are not supported in the teardown section.

Due to the possibility that the teardown actions are not required on the server under test, the TestScript execution workflow MAY provide the capability of skipping or ignoring the teardown section of the TestScript.

Post-Processing

After the teardown section is executed, any fixtures that were marked 'autodelete' are removed from the server(s) under test. After this final stage, the execution of the TestScript is complete.

Fixtures

The fixtures section of the TestScript defines a set of resource instances that will be used as part of the setup, test, and teardown sections during TestScript execution. All defined fixtures are expected to be required in order for the test script to execute. Each fixture defines a resource instance by URI, and must be identified by an ID. The URI can be local or remote (i.e. another server than the one the TestScript resource resides), absolute or relative. The ID on the fixture is considered the "source" identifier of the fixture -- it is not the same thing as the resource ID on the server where it was hosted. The "source" identifier is used to define the fixture instance within the context of the TestScript. Operations reference the ID of a fixture to uniquely identify the fixture instance the operation is using ("sourceId") or acting against ("targetId"). Once a fixture has been instantiated on a server (typically by the use of a create operation), the fixture ID is mapped to the ID of the corresponding resource instance on the server. TestScript execution engines must maintain this relationship between fixture IDs and server resource IDs. The TestScript execution engine is responsible for translating the fixture IDs (whether provided to the operation as "source" or "target") to the ID of the resource on the server during execution.

Using the optional "autocreate" and "autodelete" elements (missing values default to false), fixtures can be configured to automatically be created during TestScript setup and automatically deleted during TestScript teardown. This means that additional "create" and "delete" operations in the TestScript.setup and TestScript.teardown sections are unnecessary.

Profiles

The profiles section of the TestScript defines a set of FHIR profiles (see StructureDefinition resource) that will be used as part of the setup or test sections during TestScript execution. Each profile defines a StructureDefinition instance by URI, and must be identified by an ID. The URI SHALL reference a known FHIR profile corresponding to the FHIR specification version being tested. As with Fixtures, the ID on the profile is considered the "source" identifier of the profile. Operations reference the ID of a profile to uniquely identify the profile instance the operation is using ("validateProfileId").

See Use Profiles for more information.

Variables

The variables section of the TestScript defines a set of expressions whose evaluations will be used in substitutions. These substitutions are performed in operation request headers and URL paths as well as assertion values.

Without variables, search parameters and request headers (such as If-Modified-Since) would be specified in outgoing requests as literal values. Variables allow the values to be managed externally in fixtures or dynamically in server response fixtures. They would be defined to hold path expressions against the fixtures. The path expressions would not change from one server to another but the fixture data could.

Using variables allows for the same test scripts to be executed against the same servers by different clients at the same time. Each client would change the fixture data (external to the test script) to make the data unique to that client. This ensures that the same delete/create/read operations executed concurrently by one client does not interfere with those of another client. That can be important within the context of a testing event such as a Connectathon. It can be very useful in year-round testing against public servers as well.

See Use Variables for more information.

Operation Execution

The setup, test, and teardown sections of a TestScript can contain operation elements. Operations are intended to be executed in sequence and they represent the logic defined by the TestScript. Operations define a type, sourceId, targetId, destination, responseId, contentType, and other parameters. The type of the operation aligns with a corresponding RESTful API interaction. The sourceId and targetId of an operation define the context of the fixture data the operation is acting against (see Fixtures). The destination defines the server the operation is executed on and is only required for tests that include multiple servers. The responseId specifies a fixture ID to use to map to the server response. The contentType defines the format (XML or JSON) and the corresponding mime-type (application/fhir+xml and application/fhir+json) to use in the RESTful operation (defaulting to XML). The parameters of an operation allow providing additional data required for execution.

TestScript execution engines must load the operation details and execute the operation against the server(s) under test. Operations that are expected to result in an error or exception shall immediately be followed by one or more "assertion" operations (that test for those error conditions), otherwise the test fails. This allows for "negative" testing (for example, the test script may perform operations that should return a 4XX or 5XX HTTP response code). Test execution is halted as soon as an operation or assertion fails. If an operation or assertion fails, then the test ends in failure and the test script execution proceeds to the next test. Once all tests have completed execution, the teardown section is executed. Once teardown completes, the suite execution is complete. If any setup or test operation or assertion failed, the test script is marked in failure. Failures in teardown are ignored.

Assertion Execution

The "assertion" evaluates the results of previous operations to determine if the server under test behaves appropriately. In order to evaluate an assertion, the request, response, and results of the most recently executed operation must always be maintained by the TestScript execution engine.

The TestScript execution engine must implement the behavior of each assertion to determine if the response of the last operation meets the conditions of the assertion.

If the conditions of the assertion are met execution of the test continues. If the conditions of the assertion are not met, the test being executed fails and execution of the test is halted. Test execution will then continue with the next test.

Conditional Asserts

The ability to conditionally evaluate an assert is not defined as part of the native behavior within the TestScript resource or execution engine. This means that all subsequent test asserts are not evaluated when a prior assert within the same test evaluates to a failure. The current workaround is to set warningOnly=true for those asserts that require conditional evaluation.

Another optional workaround is to provide the TestScript execution engine with access to an external Rules engine. The TestScript execution engine author is encouraged to build and publish this functionality within an appropriate FHIR ImplementationGuide.

How Tos

Mark a Resource as a Test

To mark a resource as a test resource, use the testing tag:

<Patient xmlns="http://hl7.org/fhir">
  <meta>
    <security>
      <system value="http://hl7.org/fhir/v3/ActReason"/>
      <code value="HTEST"/>
      <display value="test health data"/>
    </security>
  </meta>
</Patient>

Not all testing resources need to explicitly marked as test, but it may be necessary when performing testing on a production system, for instance. Note: This is a security label because different access control rules may apply to test resources.

Test create operation

To test if create operation is properly supported on a server, run the operation as part of TestScript.test.

First, define the fixture as a reference at the top of the test script. The fixture will hold the body of the POST request:

<fixture id="example-patient"> <resource> <reference value="Patient/patient-example.xml"/> </resource> </fixture>

Note that it is illegal for the fixture to contain a resource id in a create operation.

Point the sourceId element of the create operation to the fixture id just defined:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <sourceId value="example-patient"/> </operation> </action>

There are two ways to verify that the create operation returned the right status code:

  1. Use assert.response:

    <action> <assert> <response value="created" /> </assert> </action>
    See response codes for complete list.
  2. Use assert.responseCode explicitly:

    <action> <assert> <responseCode value="201" /> </assert> </action>

Test search operation

To test if search operation is properly supported on a server, run the operation as part of TestScript.test.

Use the resource element to specify the resource type and the params element to specify the search parameters:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient" /> <contentType value="json" /> <params value="?given=John&amp;family=Doe" /> <responseId value="R1" /> </operation> </action>

The contentType element is optional and will default to "xml" which will translate to HTTP request header "Content-Type" being set to "application/fhir+xml" by test engines. In this case, though, it was used to set it to "application/fhir+json".

The responseId element was used to store the response in a reference called "R1". This reference will hold both the response headers and the response body.

Verify that the search operation returned the right status code:

<action> <assert> <response value="okay" /> </assert> </action>

See response codes for complete list.

Verify that the search operation returned the right resource type:

<action> <assert> <resource value="Patient" /> </assert> </action>

There are many ways to verify that the search operation returned the right Patient:

  1. Explicitly compare the elements to known value:

    <action> <assert> <path value="fhir:Patient/fhir:birthDate/@value"/> <sourceId value="R1"/> <value value="1974-12-31" /> </assert> </action>

    The sourceId element is pointed to the responseId value of the search operation. If no sourceId is specified, then test engines will use the response of the last operation in the test script even if responseId was not specified in the operation.

    The path element holds an XPath or JSONPath expression against the response body contents.

  2. Compare the elements in response to elements in another fixture that is either dynamically set by responseId or defined statically by the fixture element at the top of the script:

    <action> <assert> <compareToSourceId value="F1"/> <compareToSourcePath value="fhir:Patient/fhir:birthDate/@value"/> <path value="fhir:Patient/fhir:birthDate/@value"/> </assert> </action>

    This time the birthDate value in the response is compared to the birthDate value in a fixture called 'F1'.

  3. Verify that the response contains all the element/content in another fixture pointed to by minimumId.

    <action> <assert> <minimumId value="F1"/> <sourceId value="R1"/> </assert> </action>

    Test engines will parse the 'body' of the F1 fixture and verify that each element and its value matches the corresponding element in the R1 response body. In other words, R1 is verified to be a 'superset' of F1. The resource id element in the body will be ignored during comparison. The headers will also be ignored.

    F1 can be statically defined or it can be the responseId for another operation. If sourceId is not specified, then test engines will use the response of the last operation. So the previous assertion could have been defined as:

    <action> <assert> <minimumId value="F1"/> </assert> </action>

Perform delete operation in teardown

Test scripts should clean up resources created as part of execution. The TestScript.teardown operations will get executed once before the test script execution completes.

Here are a couple of ways to run delete operation in TestScript.teardown:

  1. Use conditional delete operation in TestScript.teardown:
    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="delete"/> </type> <resource value="Patient" /> <params value="?given=John&amp;family=Doe" /> </operation> </action>
  2. Use delete operation with targetId fixture.

    To do that, the resource must have been created during TestScript.setup or TestScript.test:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <responseId value="create-response"/> <sourceId value="example-patient"/> </operation> </action>

    As part of TestScript.teardown, run the delete operation with targetId value pointed to sourceId value of the create operation:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="delete"/> </type> <targetId value="create-response"/> </operation> </action>

    Test engines will keep track of response headers and body of all operations.

    The delete operation's targetId value is expected to correspond to the responseId of a GET operation (such as search or read) or the sourceId of a POST/PUT operation (such as create).

    For targetId value corresponding to responseId of GET operations (such as search or read), test engines will use the resource type and id returned in the GET response body's resource to set the [type] and [id] in delete operation's URL, respectively.

    For targetId value corresponding to responseId of POST/PUT operations (such as create), test engines will use the resource type and id returned in the POST/PUT response "Location" header to set the [type] and [id] in delete operation's URL, respectively. This is the case in the example above.

    The targetId value cannot point to a statically defined fixture as the id in the fixture cannot be relied upon.


Perform delete operation in setup

Deletion of resources created during test script execution should be done using delete operation in TestScript.teardown. See Perform delete operation in teardown for details.

There might be left-over resource instances though on the server from prior executions of the script that terminated prematurely through an error. Resources can be deleted in TestScript.setup as well to ensure reliable test execution.

To delete a resource in setup, the server is required to support Conditional Delete operation.

Use the params element to specify the search criteria for the delete operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="delete"/> </type> <resource value="Patient" /> <params value="?family=Doe&amp;given=Joe"/> </operation> </action>

Test engines will append the contents of the params element to url after [type]: "[base]/[type]?[search parameters]". The resource element value ("Patient") will be used to replace [type] in the url.


Test conditional delete operation

To test if a server supports conditional delete operation, run a create operation prior to the delete using a sourceId that points to a fixture defined at the top of the script:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <sourceId value="example-patient"/> </operation> </action>

Then use the params element to specify the search criteria for the delete operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="delete"/> </type> <resource value="Patient" /> <params value="?family=Doe&amp;given=Joe"/> </operation> </action>

Test engines will append the contents of the params element to url after [type]: "[base]/[type]?[search parameters]". The resource element value ("Patient") will be used to replace [type] in the url.

To verify that the delete operation returned the right status code:

<action> <assert> <operator value="in" /> <responseCode value="200,204" /> </assert> </action>

To verify that the resource was indeed deleted on the server, run a search using the same parameters and verify that the status code is 404 (not found):

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient" /> <params value="?given=John&amp;family=Doe" /> </operation> </action>
<action> <assert> <response value="notFound" /> </assert> </action>

Test conditional create operation

To test if a server supports conditional create operation, use the 'If-None-Exist' request header:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <requestHeader> <field value="If-None-Exist" /> <value value="Patient?given=John&amp;Doe&amp;birthdate=1974-12-31" /> </requestHeader> <sourceId value="F1" /> </operation> </action>

The response code of 200 verifies that the resource already exists and did not get created:

<action> <assert> <responseCode value="200"/> </assert> </action>

Test update operation

Update operations require a resource id. The id must be present in the fixture (PUT body contents) as well as the URL. The values must match.

Because resource ids cannot be predicted on the server, it is best to retrieve the id on a resource freshly created as part of the script

There are many ways to do that. Below is a couple:

  1. Use update operation with targetId fixture pointing to create operation's responseId:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <responseId value="create-response" /> <sourceId value="example-patient"/> </operation> </action>
    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="update"/> </type> <resource value="Patient"/> <responseId value="R3" /> <sourceId value="john-doe-update"/> <targetId value="create-response"/> </operation> </action>

    Test engines will keep track of response headers and body of all operations.

    The update operation's targetId value is expected to correspond to the responseId of a GET operation (such as search or read) or the sourceId of a POST/PUT operation (such as create).

    For targetId value corresponding to responseId of GET operations (such as search or read), test engines will use the resource type and id returned in the GET response body's resource to set the [type] and [id] in update operation's URL, respectively. This is the case in the next example below.

    For targetId value corresponding to responseId of POST/PUT operations (such as create and update), test engines will use the resource type and id returned in the POST/PUT response "Location" header to set the [type] and [id] in update operation's URL, respectively. This is the case in the example above.

    The targetId value cannot point to a statically defined fixture as the id in the fixture cannot be relied upon.

  2. Use update operation with targetId fixture pointing to search operation's responseId:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <responseId value="R1"/> <sourceId value="john-doe-patient"/> <!-- Fixture must be defined at the top of the script --> </operation> </action>
    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe"/> <responseId value="R2" /> </operation> </action>
    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="update"/> </type> <resource value="Patient"/> <responseId value="R3" /> <sourceId value="john-doe-update"/> <targetId value="R2"/> </operation> </action>

After the update operation, test scripts would perform at least one more read/search operation to retrieve the contents of the updated resource and then perform assertions to verify that the data did indeed get updated on the server:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe"/> <responseId value="R4" /> </operation> </action>

Verify that the birthdate got updated and is being returned properly:

<action> <assert> <path value="fhir:Patient/fhir:birthDate/@value"/> <sourceId value="R4"/> <value value="1974-12-31" /> </assert> </action>

Test conditional update operation

Unlike a regular update operation, a conditional update operation does not require a resource id in the URL (or the body of the PUT).

To test conditional update, use params element in the operation instead of targetId. The resource element will be required in this case.

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="update"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe"/> <responseId value="R3" /> <sourceId value="john-doe-update"/> <!-- john-doe-update fixture will have the 'correct' birthDate --> </operation> </action>

Test engines will append the contents of the params element to url after [type]: "PUT [base]/[type]?[search parameters]". The resource element value ("Patient") will be used to replace [type] in the URL.

Verify that the birthdate got updated and is being returned properly:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe"/> <responseId value="R4" /> </operation> </action>
<action> <assert> <path value="fhir:Patient/fhir:birthDate/@value"/> <sourceId value="R4"/> <value value="1974-12-31" /> </assert> </action>

Test read operation

The read operation requires the resource id in the URL. Since resource ids are unpredictable on servers, it's best to create the resource within the test script prior to executing the read operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <responseId value="create-response"/> <sourceId value="example-patient"/> </operation> </action>

One way to execute the read operation is to run the read operation with targetId value pointed to responseId value of the create operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="read"/> </type> <targetId value="create-response"/> </operation> </action>

Test engines will keep track of response headers and body of all operations.

The read operation's targetId value is expected to correspond to the responseId of a GET operation (such as search or read) or the sourceId of a POST/PUT operation (such as create).

For targetId value corresponding to responseId of GET operations (such as search or read), test engines will use the resource type and id returned in the GET response body's resource to set the [type] and [id] in read operation's URL, respectively.

For targetId value corresponding to responseId of POST/PUT operations (such as create), test engines will use the resource type and id returned in the POST/PUT response "Location" header to set the [type] and [id] in read operation's URL, respectively. This is the case in the example above.

The targetId value cannot point to a statically defined fixture as the id in the fixture cannot be relied upon.


Test vread operation

The vread operation requires the resource id as well as the resource version id in the URL. Since resource ids and version ids are unpredictable on servers, it's best to create the resource within the test script prior to executing the vread operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <responseId value="create-response"/> <sourceId value="example-patient"/> </operation> </action>

One way to execute the vread operation is to run the vread operation with targetId value pointed to responseId value of the create operation:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="vread"/> </type> <targetId value="create-response"/> </operation> </action>

Test engines will keep track of response headers and body of all operations.

The vread operation's targetId value is expected to correspond to the responseId of a GET operation (such as search or read) or the sourceId of a POST/PUT operation (such as create).

For targetId value corresponding to responseId of GET operations (such as search or read), test engines will use the resource type, id and version id returned in the GET response body's resource to set the [type], [id] and [vid] in vread operation's URL, respectively.

For targetId value corresponding to responseId of POST/PUT operations (such as create), test engines will use the resource type, id and version id returned in the POST/PUT response "Location" header to set the [type], [id] and [vid] in vread operation's URL, respectively. This is the case in the example above.

The targetId value cannot point to a statically defined fixture as the id in the fixture cannot be relied upon.


Test history operation

The history operation can be executed in the following ways:

  1. GET [base]/[type]/[id]/_history{?[parameters]&_format=[mime-type]}

    Here the resource id is required in the URL. This is similar to read operation if targetId elemet is used. See Test read operation for details.

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="history"/> </type> <targetId value="create-response"/> <!-- or search-response or update-response--> </operation> </action>
    <action> <assert> <resource value="Bundle"/> </assert> </action>
  2. GET [base]/[type]/_history{?[parameters]&_format=[mime-type]}

    Here the resource id is not required in the URL.

    Instead of targetId element, the params element can be used to specify the search criteria for the history operation.

    In the following example, all history entries for John Doe patient would be returned by server:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="history"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe"/> </operation> </action>
  3. GET [base]/_history{?[parameters]&_format=[mime-type]}

    Here neither the resource type nor the resource id is required in the URL. In the following example, no more than 50 history entries would be returned by server:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="history"/> </type> <params value="?_count=50"/> </operation> </action>

Specify Accept header in request

The default "Accept" header that will be set on all GET operations (such as read, vread, search, history, etc.) will be "application/fhir+xml".

There are two ways to change the default "Accept" header:

  1. Use the accept element:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="read"/> </type> <accept value="json" /> <targetId value="F1" /> </operation> </action>

    Test engines will set the Accept header to "application/fhir+json" if "json" is specified and will use "application/fhir+xml" if "xml" is specified.

  2. Use the requestHeader element to set "Accept" explicitly:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="read"/> </type> <requestHeader> <field value="Accept" /> <value value="application/fhir+json" /> </requestHeader> <targetId value="F1" /> </operation> </action>

    Test engines will take values specified for requestHeader "as-is" and not transform them. This might be useful for negative testing, e.g. the value can be set explicitly to "applcation/xml" or an invalid value and verify server response.


Specify Content-Type header in request

The default "Content-Type" header that will be set on all POST/PUT operations (such as create, update, etc.) will be "application/fhir+xml".

There are two ways to change the default "Content-Type" header:

  1. Use the contentType element:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <contentType value="json" /> <targetId value="F1" /> </operation> </action>

    Test engines will set the Content-Type header to "application/fhir+json" if "json" is specified and will use "application/fhir+xml" if "xml" is specified.

  2. Use the requestHeader element to set Content-Type explicitly:

    <action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <requestHeader> <field value="Content-Type" /> <value value="application/fhir+json" /> </requestHeader> <targetId value="F1" /> </operation> </action>

    Test engines will take values specified for requestHeader "as-is" and not transform them. This might be useful for negative testing e.g. the value can be set explicitly to "applcation/xml" or an invalid value and verify server response.


Verify Content-Type header in response

There are two ways to verify the "Content-Type" header in response:

  1. Use the contentType element:

    <action> <assert> <contentType value="json" /> </assert> </action>

    Test engines will verify that "application/fhir+json" is present in Content-Type header if "json" is specified and will verify that "application/fhir+xml" is present if "xml" is specified.

  2. Use the requestHeader element to verify Content-Type explicitly:

    <action> <assert> <headerField value="Content-Type" /> <value value="application/fhir+json" /> </assert> </action>

    Test engines will take values specified for headerField "as-is" and not interpret them.

    Note that test engines will not verify contentType in response if assertions for contentType are missing.


Use Profiles

Profiles are defined against known FHIR profiles (see StructureDefinition resource) corresponding to the FHIR specification version being tested. They are used in the "assert.validateProfileId" and, when present in an assert, will invoke validation of the FHIR resource payload in either the request or response message.

Profiles would be defined at the top of the script.

Below is a profile that is defined against the FHIR Patient resource type and referenced by "P1":

<profile id="P1"> <reference value="http://hl7.org/fhir/StructureDefinition/Patient" /> </profile>

Test engines will not evaluate this at this point. They will store the profile reference in "P1" and will look for "P1" in an "assert.validateProfileId" element values during operation calls.

Here is a read operation for a Patient with a resource id of "example" followed by an assert that performs a FHIR validation using the base FHIR profile for the Patient resource type referenced by the "P1" profile. The validation will be performed against the response payload by default if the "assert.direction" is not present. If the validation returns any "fatal" or "error" messages, then this assert will be failed. Otherwise, if the validation returns any "warning" messages, then this assert will pass but with warnings. Finally, if the validation returns only "information" messages or no messages, then this assert will pass.

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="read"/> </type> <resource value="Patient"/> <accept value="xml"/> <params value="/example"/> </operation> </action> <action> <assert> <validateProfileId value="P1"/> </assert> </action>

Use Variables

Variables can be defined against static fixtures and dynamic operation responses. They can be used in "operation.params", "operation.requestHeader.value", "operation.url", and "assert.value" element values. As such they allow for the data used in operations and assertions to be externally defined. The data could be unique to each client involved in interactions with a server or could be unique to a given server database instance. This allows for multiple clients to execute the same test scripts concurrently against the same server.

Variables would be defined at the top of the script.

Below is a variable that is defined as the Location header to the response referenced by "R1":

<variable> <name value="V1" /> <headerField value="Location" /> <sourceId value="R1" /> </variable>

Test engines will not evaluate this at this point. They will store the expression in "V1" and will look for "${}" in "operation.params", "operation.requestHeader.value", and "operation.url" element values during operation calls.

Here is a read operation that will use the V1 variable. The variable expression was "Location against R1 response" (defined above). If a prior operation has not set R1 to be the responseId of the operation, then test engine will error out. Otherwise, V1 will be set to the Location header value of R1 response and that value will be substituted for ${V1} below. In other words, the read will be performed against the Location header value of R1 response.

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="read"/> </type> <accept value="json" /> <responseId value="R2" /> <url value="${V1}" /> </operation> </action>

Below are three variables defined as path expressions against the static fixture referenced by "F1". The expressions are against the given name, family name, and birthDate of a patient resource. The resource data will be managed external to the test script.

<variable> <name value="PatientGivenName" /> <path value="fhir:Patient/fhir:name/fhir:given/@value" /> <sourceId value="F1" /> </variable> <variable> <name value="PatientFamilyName" /> <path value="fhir:Patient/fhir:name/fhir:family/@value" /> <sourceId value="F1" /> </variable> <variable> <name value="PatientDOB" /> <path value="hir:Patient/fhir:birthDate/@value" /> <sourceId value="F1" /> </variable>

Again, test engines will not evaluate the path expression at this point. They will look for anything wrapped in '${}' in "operation.params", "operation.requestHeader.value", "operation.url", and "assert.value" element values and substitute the placeholders with the evaluated expressions.

Here is a conditional create operation that will set the requestHeader using the PatientGivenName, PatientFamilyName, and PatientDOB variables defined above. The variable expressions were path expressions against the statically defined F1 fixture. They will be evaluated against the fixture body (containing resource) and the corresponding values will be extracted from the fixtures and used to substitute the variables in the requestHeader value below.

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="create"/> </type> <requestHeader> <field value="If-None-Exist" /> <value value="Patient?given=${PatientGivenName}&amp;${PatientFamilyName}&amp;birthdate=${PatientDOB}" /> </requestHeader> <sourceId value="F1" /> </operation> </action>

Here is a search operation that will perform a search using the PatientGivenName, PatientFamilyName, and PatientDOB variables defined above. The variable expressions were path expressions against the statically defined F1 fixture. They will be evaluated against the fixture body (containing resource) and the corresponding values will be extracted from the fixtures and used to substitute the variables in the params value below.

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient" /> <accept value="json" /> <params value="?given=${PatientGivenName}&amp;family=${PatientFamilyName}&amp;birthdate=${PatientDOB}" /> <responseId value="R3" /> </operation> </action>

Here are the assertions that verify that the search was successful:

<action> <assert> <path value="Patient/name/given" /> <value value="${PatientGivenName}" /> </assert> </action> <action> <assert> <path value="Patient/name/family" /> <value value="${PatientFamilyName}" /> </assert> </action> <action> <assert> <path value="Patient/birthdate" /> <value value="${PatientDOB}" /> </assert> </action>

Test server support for '_format'

Servers are required to support "_format" in the request url to determine the response mime-type. See Content Type and Encodings

Use the params element to specify the _format:

<action> <operation> <type> <system value="http://hl7.org/fhir/testscript-operation-codes"/> <code value="search"/> </type> <resource value="Patient"/> <params value="?family=Doe&amp;given=Joe&amp;_format=application/fhir+json"/> <responseId value="R1" /> </operation> </action>

Use the requestHeader element to verify Content-Type explicitly:

<action> <assert> <headerField value="Content-Type" /> <value value="application/fhir+json" /> </assert> </action>

How to specify metadata capabilities

If the capabilities are supported by the server, then the TestScript can be executed. Otherwise, a test engine will provide a comparison of the system under test�s conformance capabilities and allow a TestScript as a whole to be skipped based on this comparison..

Here's how to specify that the test script requires the server to support Patient create and delete operations:

<metadata> <capabilities> <required value="true"/> <description value="Patient Create and Delete Operation"/> <conformance> <reference value="/Conformance/PatientCreateDelete.xml" /> </conformance> </capabilities> </metadata>

The contents of PatientCreateDelete.xml would be a minimal capability statement to indicate what sections need to be present in server capability statement:

<Conformance xmlns="http://hl7.org/fhir"> <rest> <mode value="server"/> <resource> <type value="Patient"/> <interaction> <code value="create"/> </interaction> <interaction> <code value="delete"/> </interaction> </resource> </rest> </Conformance>

When the metadata capabilities section is defined at TestScript.metadata and the server's capability statement does not contain the elements defined in the minimal capability statement, then all the tests in the TestScript are skipped.

The "metadata.capabilities.required" and "metadata.capabilities.validated" elements only indicate whether the capabilities are the primary focus of the test script or not. The do not impact the skipping logic. Capabilities whose "metadata.capabilities.validated" flag is true are the primary focus of the test script.


Operations

This table presents a summary of the constraints applicable to TestScript.setup.action.operation, TestScript.test.action.operation, and TestScript.teardown.action.operation elements. The operation elements should be configured consistently with the FHIR RESTful API summary.

read vread search history create update transaction conformance delete
resource The resource element is required to specify the resource ([type]) in the request URL when params element is used. Will be ignored if targetId or url are specified. In the case of targetId, the resource type will be extracted from the fixture. If targetId is specified, then [type] for request URL will be determined from targetId's fixture and resource element will be ignored. Otherwise, resource type will be extracted from sourceId's fixture if specified. For conditional updates, resource is required. N/A N/A If targetId is specified, then [type] for request URL will be determined from targetId and resource element will be ignored. For conditional deletes, resource is required.
accept The accept element can be used to specify the "Accept" header in the outgoing HTTP request. If "json" is specified, then "Accept" value of "application/fhir+json" will be set in the request header. If "xml" is specified, then "application/fhir+xml" will be used. N/A
contentType The contentType element can be used to specify the "Content-Type" header in the outgoing HTTP request. If "json" is specified, then "Content-Type" value of "application/fhir+json" will be set in the request header. If "xml" is specified, then "application/fhir+xml" will be used. N/A
destination If the TestScript is testing more than one FHIR server simultaneously, the destination identifies which test system the operation will receive this request message using zero-based indexing. The destination value MUST be equal to a defined TestScript.destination".
encodeRequestUrl The encodeRequestUrl element, if "true", can be used to specify that the destination test system will receive this request message URL query parameters in HTML (Percent) encoded UTF-8 format.
origin If the TestScript is testing more than one FHIR server simultaneously, the origin identifies which test system the operation will send this request message using zero-based indexing. The destination value MUST be equal to a defined TestScript.origin".
params The params element can be used to specify the [id] using variable substitutions and the rest of the highlighted portion in the request URL:

[base]/[type]/[id] {?_format=[mime-type]}

If used, then resource is required and targetId and url must not be specified.
The params element can be used to specify the [id] and [vid] using variable substitutions and the rest of the highlighted portion in the request URL:

[base]/[type]/[id]/_history/[vid] {?_format=[mime-type]}

If used, then resource is required and targetId and url must not be specified.
The params element can be used to specify the highlighted portion in the request URL:

[base]/[type]{?[parameters]{&_format=[mime-type]}}

If used, then resource is optional and targetId and url must not be specified.
The params element can be used to specify the [id] using variable substitutions and the rest of the highlighted portion in the following request URLs:
[base]/[type]/[id]/_history{?[parameters]&_format=[mime-type]}
[base]/[type]/_history{?[parameters]&_format=[mime-type]}
[base]/_history{?[parameters]&_format=[mime-type]}

If used, then resource is optional and targetId and url must not be specified.
N/A The params element can be used in conditional update operation to specify the highlighted portion of the request URL:

[base]/[type]?[search parameters]

If used, then resource is required and targetId and url must not be specified.
N/A The params element can be used to specify the highlighted portion in the request URL:

[base]/metadata {?_format=[mime-type]}

If used, then resource is ignored and targetId and url must not be specified.
The params element can be used to specify the [id] using variable substitutions in the request URL:

[base]/[type]/[id]

If used, then resource is required and targetId and url must not be specified.
requestHeader The requestHeader element allows for request headers to be specified explicitly. Test engines will take values specified for requestHeader "as-is" and not transform them. This allows for testing using:
requestId N/A N/A N/A N/A The requestId element can be used to reference the operation request containing response body and headers. If specified, the value can later be used in assertion sourceId to evaluate path (XPath/JSONPath) and headerFields against the request sent for an operation. N/A N/A
responseId The responseId element can be used to reference the operation response containing response body and headers. If specified, the value can later be used in assertion sourceId to evaluate path (XPath/JSONPath) and headerFields against the response received for an operation. N/A
sourceId N/A N/A N/A N/A The sourceId element points to a fixture to be used for the created resource. The fixture cannot contain the id element. The sourceId element points to a fixture to be used for the updated resource. Has to correspond to the responseId of an operation executed upstream in the test script. The response body must contain a resource with a resource id. The sourceId fixture cannot be statically defined because the id cannot be relied upon. Fixture to be used for the transaction. Has to be a Bundle. N/A N/A
targetId The targetId element can be used to specify the [type] and [id] in the request URL.

If used, then params and url must not be specified.

The targetId value has to correspond to the responseId of an operation executed upstream in the test script. The response body must contain a resource with a resource id. The targetId fixture cannot be statically defined because the id cannot be relied upon.
The targetId element can be used to specify the [type], [id], and [vid] in the request URL.

If used, then params and url must not be specified.

The targetId value has to correspond to the responseId of an operation executed upstream in the test script. The response body must contain a resource with a resource id. The targetId fixture cannot be statically defined because the id and vid cannot be relied upon.
The targetId element cannot be used as it's not allowed with params element The targetId element can be used to specify the [type], [id], and [vid] in the request URL.

If used, then params and url must not be specified.

The targetId value has to correspond to the responseId of an operation executed upstream in the test script. The response body must contain a resource with a resource id. The targetId fixture cannot be statically defined because the id and vid cannot be relied upon.
N/A. The [type] for the request URL will be extracted from sourceId. N/A. The [type] for the request URL will be extracted from sourceId. N/A. N/A. The targetId element can be used to specify the [type] and [id] in the request URL.

If used, then params and url must not be specified.

The targetId value has to correspond to the responseId of an operation executed upstream in the test script. The response body must contain a resource with a resource id. The targetId fixture cannot be statically defined because the id cannot be relied upon.
url The url element will contain the full HTTP URL for the operation. This should rarely be used in test scripts. One possible application would be to test if the Location header returned in a response is pointing to an expected resource. See testscript-example-search example.

Assertions

Assertion Valid operator values Description
compareToSourcePath equals | notEquals Asserts that compareToSourcePath against the response body of compareToSourceId fixture evaluates to a value that is equal or notEqual to the evaluated value of path which must be present also.
contentType equals | notEquals | contains | notContains Asserts that the "Content-Type" in response header is or is not the specified value for contentType element depending on the operator used.
headerField equals | notEquals | in | notIn | greaterThan | lessThan | empty | notEmpty | contains | notContains Asserts that the header specified for headerField element in the response contains, not contains, is equal, not equal, in, not in, greater than, or less than the value specified for value element if present.
If the operator is "empty" or "notEmpty" then value will be ignored.
If sourceId is not specified, then headerField will be evaluated against the last operation's response headers.
minimumId N/A Asserts that the response contains all the element/content in another fixture pointed to by minimumId element. This can be a statically defined fixture or one that is dynamically set via responseId.
navigationLinks N/A Asserts that the response Bundle contains or does NOT contain first, last, and next links depending on whether or not navigationLinks element is set to true or false.
path equals | notEquals | in | notIn | greaterThan | lessThan | empty | notEmpty | contains | notContains Asserts that path against the response body evaluates to a value that contains, not contains, is equal, not equal, in, not in, greater than, or less than the value specified for value element if present.
If the operator is "empty" or "notEmpty" then value will be ignored.
If sourceId is not specified, then path will be evaluated against the last operation's response body.
resource equals | notEquals Asserts that the resource returned in the response body is or is not of the specified value for resource element.
response equals | notEquals Asserts that status code in the response is or is not one of the enumerated values in response abbreviations.
responseCode equals | notEquals | in | notIn | greaterThan | lessThan Asserts that status code in the response is equal, notEqual, in, not in, greater than, or less than the specified value(s) for responseCode element
validateProfileId N/A Asserts that the response is valid according to the profile specified by validateProfileId element.

toc.html

Full Table of Contents

FHIR Infrastructure Work GroupMaturity Level: N/AStandards Status: Informative

During ballot, this Table of Contents shows, for each page, which ballot it is part of. Legend:


todo.html

Outstanding Issues

FHIR Infrastructure Work GroupMaturity Level: N/AStandards Status: Informative

This specification is currently in its third round of trial use. While some parts of the specification are mature and stable, and HL7 is focusing on moving these to a formal standard, much work remains to be done in other areas. The following general areas of functionality have been deferred to a future version, or are incompletely covered in this version:

For some of these, some draft content is included in the specification for implementer consideration.

In addition, there are a number of specific notes in the specification requesting feedback from implementers:


ucum.html

Using UCUM with FHIR

Maturity Level: 5Standards Status:Trial Use

The Units of Measure Coding System (UCUM) is recommended for use with the Quantity data type.

Summary

SourceUCUM is made available by the Regenstrief Institute, Inc and The UCUM Organization at http://unitsofmeasure.org
SystemThe URI http://unitsofmeasure.org identifies UCUM codes
VersionWhere a version is used, it should be the standard UCUM version e.g. 1.9. There is no need to use version in the Coding data type, only in Value sets that use UCUM codes
CodeValid expressions using the case sensitive symbols
DisplayThere is no defined display; the UCUM code is used directly for the display
InactiveA few codes in UCUM are marked as deprecated (e.g. ppb, pptr)
SubsumptionNo Subsumption relationships are defined by UCUM
Filter PropertiesTwo filter properties are defined as described below

Copyright

UCUM is Copyright © 1999-2013 Regenstrief Institute, Inc. and The UCUM Organization, Indianapolis, IN. All rights reserved. See TermsOfUse for details.

UCUM Expressions

UCUM is inherently a compositional code system; almost all UCUM codes are expressions composed using the UCUM expression syntax. Many servers, however, do not implement full support for the syntax, and rely on a large library of pre-built valid UCUM codes (FHIR includes a common UCUM codes value set for this purpose).

UCUM us always documented as a compositional code system. If a server does not support the full grammar, it should document that in its Terminology Capabilities Statement.

UCUM Filter Properties

This section documents the property filters that can be used with the UCUM code system in value set composition statements.

Property filter

DescriptionRestricts the expression to a describe a particular UCUM base property
Property Nameproperty
Operations Allowed=
Values Allowed[string name of property]
CommentsRestricts expressions to any expression that is comparable to a base unit with a matching property value. Note: this does not match the property on units, only base units

Canonical Filter

DescriptionAllows for any expression that is comparable to the named unit
Property Namecanonical
Operations Allowed= / in
Values AllowedUCUM expression
CommentsThis allows any expression that is comparable to the given unit(s)

Implicit Value Sets

Implicit value sets are those whose specification can be predicted based on the grammar of the underlying code system, and the known structure of the URL that refers to them. There is one set of implicit value sets defined for UCUM: By Canonical Value.

If any value set resources exist with an identifier that conforms to the URL patterns specified below, the content of the resource must conform to the template provided. Profiles and other value set references are allowed to reference these value sets directly.

The value set identifier http://unitsofmeasure.org/vs is a value set that contains all UCUM codes.

UCUM Comparable Value Set

A value set with an identifier of "http://unitsofmeasure.org/vs/[expression]" must conform to this template, where [expression] is a valid UCUM expression:

<ValueSet xmlns="http://hl7.org/fhir">
  <text>
    <status value="generated"/>
    <div xmlns="http://www.w3.org/1999/xhtml">
      [some html that identifies that this value set 
      includes all UCUM expressions that are comparable to the provided expression]
    </div>
  </text>
  <identifier value="http://unitsofmeasure.org/vs/[expression]"/>
  <version value="[optional - but recommended - UCUM version]"/>
  <name value="Ucum Expressions comparable to [expression]"/>
  <description value="Ucum Expressions comparable to [expression]"/>
  <status value="active"/>
  <date value="[optional date of UCUM release]"/>
  <compose>
    <include>
      <system value="http://unitsofmeasure.org"/>
      <filter>
        <property value="comparable"/>
        <op value="="/>
        <value value="[expression]"/>
      </filter>
    </include>
  </compose>
</ValueSet>

unii.html

Using Unique Ingredient Identifier (UNII) with FHIR

Maturity Level: 3Standards Status:Trial Use

Summary

SourceFDA Substance Registration System
SystemThe URI to identify UNII codes is http://fdasis.nlm.nih.gov
VersionIf it is desired to exchange the version, use the date of the file in the UNII list download in the format YYYYMMDD
CodeUse the value of the UNII column in the UNII List
DisplayUse the value of the PT (Preferred Term) column in the UNII List
InactiveTodo: Describe how it is determined which concepts are inactive
SubsumptionNo Subsumption relationships are defined by UNII
Filter PropertiesNone are described yet

Version Issues

To be investigated

Copyright/License Issues

UNII has no copyright acknowledgement needed, nor are there any license terms to adhere to.

NDF-RT Filter Properties

No need for filters identified yet.

Implicit Value Sets

No need for implicit value sets identified yet.


updates.html

Variations between Submitted data and Retrieved data

Maturity Level: 5Standards Status:Trial Use

Some of the time when using a FHIR interface, requests to create or update resource instances will behave exactly as the initiator requested. The desired record(s) will be created or revised within the target system and a subsequent query of the data would show the exact same information as was submitted. However, FHIR systems are not guaranteed to behave this way. Without any other agreement between exchange partners, FHIR systems are not obligated to store and return data as it was received. In fact, for some interoperability paradigms, they're not obligated to store any data at all. This page discusses some of the considerations around system behavior, including differences in expectations for systems interoperating using REST, messaging, documents and services.

Data element support

The most common reason for differences between what data is submitted to a system and what data can be extracted from it is that the system doesn't support all of the data elements present in the instance received. In the base resource, no systems are required to support any particular set of extensions or even any particular subset of core elements.

For elements that are part of the resource, the expectation is that "most" systems will support the element. i.e. most systems will support capturing a patient's name, gender and date of birth. But "most" does not mean "all". It is possible to be fully FHIR conformant and claim to support the Patient resource, but be incapable of storing any of those data elements. There are use-cases where names may be unnecessary (e.g. agricultural veterinary systems, anonymized reporting, etc.) and similar use-cases for almost every data element. No FHIR resource elements start off as mustSupport and very few resource elements start off with a minimum cardinality other than "0".

In the case of extensions, the very nature of a concept being an extension means that the designers of the specification expected that fewer than "most" systems would support the element, though support might vary widely by context. An extension might be used by 100% of systems in some country, discipline, etc. and not used by any systems in another context. In any event, there is no guarantee that an arbitrary receiver will recognize and be able to persist any given extension.

In order to know whether a particular data element is likely to be stored by a given server, a client should check the Capability statement of that server. If, for a given resource, the StructureDefinition pointed to indicates that the element or extension is "mustSupport=true", and the server is capable of storing and returning data in general, then it would be expected that the system will be capable of storing and returning that data element. (Some servers such as decision support systems might not be capable of storing or returning any received data.)

All of these concerns around possibly not storing resource elements or extensions can hold whether the data is sent using REST, Messaging or Services. However, with documents, a consuming system is expected to accept the entire contents of the document without losing any information or altering it in any way.

System behavior

Even if a system supports all of the data elements provided, not all systems will actually persist the data received or be capable of returning it in response to a query. mustSupport indicates that a system supports an element but does not prescribe exactly what the system must do with supported elements. Data might be persisted, displayed, relayed, analyzed, tabulated or used in a variety of other fashions. The behavior of a given system should be unsurprising given its context, but it is still important to recognize that not all systems will persist the data they receive.

Access permissions

Even if a system stores a given data element, that does not mean it will always include that element when responding to queries. Systems will have access permission rules that restrict who can see a given resource instance and, occasionally, who can see a particular data element within a resource. Systems responding to queries might suppress records or may adjust the content of resource instances to exclude data elements the querying system is not permitted to see.

Generated and inferred data

Some servers may add additional data elements (or more commonly, extensions) based on information they have generated or inferred from data in the resource, from other resources or other information of which the server is aware. i.e. an instance queried after being created or updated might have more information present than was included on the originally submitted record

Data integration

Servers receiving updates from multiple sources may choose to be selective about what sources they choose to trust for updates to certain information. For example, a patient registry system might choose to only allow updates to name, gender and date of birth from administrative systems but not clinical systems. If a system filters out patient address information from being disclosed to a system when it queries, it will likely choose to not replace or eliminate the addresses it has on file when it receives an update from that system.

One approach commonly followed by HL7 version 2 messaging interfaces is to not update any elements not included in an instance. For example, if a Patient instance were received with no telecom or contact information, all existing telecom and contact information would be retained and only those elements included in the instance would be updated. This same approach can be followed with FHIR, however, unlike HL7 V2, FHIR does not have a defined construct to use to indicate that a particular data element should explicitly be set to empty. Systems wishing to fully emulate HL7 v2 behavior will need to use an extension to mirror this behavior.

Ramifications of storage/retrieval variations

While changing data prior to storage or prior to returning query results is possible, it should not be considered "normal" behavior. It creates several challenges:

Mitigation approaches for storage/retrieval variations

FHIR does provide a couple of mechanisms that can help with the issue of a system that has received only partial data overwriting data that was filtered from its record:

Note that using either of the above mechanisms may constitute a security breach in circumstances where the individual or system accessing the data should not know that the record being reviewed has been modified in any way. (The presence of digital signatures would present a similar issue.)