FHIR for Developers tutorial as given during the HL7 WGM meetings. Good introductory text for developers getting started with FHIR, HL7's new messaging standard for healthcare.
You’re a message broker routingandtranslating between v2, v3 and FHIRYourapp interfaces with a PHR using FHIR nativelyYourapplicationcancommunicateusing FHIR, but your software uses a proprietary RDBMSUse FHIR as the common languagefor a Vendor Neutral Archive…Or a combination….Youmightsee data as a nestedstructure of XML, a series of tableswithkeys, class-diagrams….
No context conduction: if a Condition resource references a particular Patient as it's subject, and it links to a Procedure resource as it's cause, there is no automatic rule or implication that the procedure has the same patient as it's subject.
Referencesbetween resources are notby “business key”, it is *not* the patientid, it’s a REST URI!
Very important concept, comparable with the “Aggregate” notion of Domain Driven Design, for which many useful implementation strategies have been documented on the internet.
Resource Id’s (=URLs) are infrastructural id’s, they differ from “business” identifier.Many Resources also have business identifiers, they are explicitly modeled, like Patient.identifier (even more than one identifier possible!)Business identifiers are completely separate from technical resource id’s
This is not only the URL you use to retrieve the resource, it’s also its id.All URL’s in FHIR are case-sensitive (and so is the id)It is *metadata*, you won’t find this in the Resource’s definition
Only the Resources are user-definable, other types are “built-in”Derived primitives are patterns -> validation consists of regexp matchingConstrained types are defined using invariants (OCL, Xpath, prose) -> validation using schematron, codeNarrative and Extension are both ONLY used in ResourcesResources can use both (derived) primitives and composite datatypes in its definitionsInfrastructural types need special handling, not general-purpose types
The lexical rendering for these primitives in Xml is the same as in JSON (maybe this should go in the serialization section)Expressing the allowed values in terms of XSD primitives brings in more variability then we would like. E.g. “0” and “1” are valid for xs:boolean. “+000004”, “4”, “+4” are all valid decimals.
TODO: Search on timestamps (lower, upper bound), converting to zulu, user-timezone, sorting of times etc.* Old-school types built on classis logic and math from the Greek, we have new stuff too…types based on urls!
TODO: Search on timestamps (lower, upper bound), converting to zulu, user-timezone, sorting of times etc.* Old-school types built on classis logic and math from the Greek, we have new stuff too…types based on urls!
The content of a primitive is not rendered in XML as a text node but as an attributeNotice how the “system” of a code is now a uri, so unlike in v3 an OID is now ALSO a uri (urn:oid:1.2.3.4.5)
Datatypes use both primitives and other datatypes.Some datatypes use attributes with cardinality > 1CodeableConcept.primary is actually refers to a coding in CodeableConcept.coding, so is not a code itself.Quantity has constrained variations Distance, Count, Duration, Money. They introduce constraints on useable units, but do not add attributes, so not shown here.Note that HumanId.assigner refers to a Resource
* value[x] means: An element with a name that starts with “value”. The [x] is replaced by the (capitalized) name of the actual datatype
Examples is from http://www.hl7.org/implement/standards/fhir/observation.htmBindings can be Incomplete (HL7 defines some codes for it, but you can add your own), Fixed (HL7 defines the only codes that are allowed) and Example (no specific codes defined, but some are given to give you an idea of what goes in the element)
Examples is from http://www.hl7.org/implement/standards/fhir/observation.htmBindings can be Incomplete (HL7 defines some codes for it, but you can add your own), Fixed (HL7 defines the only codes that are allowed) and Example (no specific codes defined, but some are given to give you an idea of what goes in the element)
Bindings use ValueSets to define what codes are allowed.Patient.administrativeGender has a binding using the valueset “http://hl7.org/fhir/vs/administrative-gender”This valueset includes codes from two code systems http://hl7.org/fhir/v3/AdministrativeGender and http://hl7.org/fhir/v3/NullFlavorSo yes, FHIR reused code systems from v3 (and v2), and has some defined specifically for FHIR.
The element “identifier” is of type IdentifierThe element “telecom” repeats, there is no notion of a “list” in Xml.Empty elements are left out
There are references that can reference more than one type of resource! In this example: Resource(Patient | Group | Device)The reference has a “type” element to indicate the kind of Resource that is referencedReferences may be: Relative (on the same server), absolute URL (somewhere external) and internal (not treated here)
* “Every resource SHALL include a human readable narrative”
Only the Resources are user-definable, other types are “built-in”Derived primitives are patterns -> validation consists of regexp matchingConstrained types are defined using invariants (OCL, Xpath, prose) -> validation using schematron, codeNarrative and Extension are both ONLY used in ResourcesResources can use both (derived) primitives and composite datatypes in its definitionsInfrastructural types need special handling, not general-purpose types
* Content is the same
You can retrieve any person using a GET on the person’s id, which is just an url on the server: /fhir/person/@<id>We have our own MIME-type: “text/xml+fhir”Note that FHIR always uses UTF-8. Since this is not the default for HTTP, the server explicitly mentions thisBut should mean the xml encoding mentions “utf-8” and that the payload is really encoded in utf-8There can be a Byte Order Mark, but hopefully your framework handles all that ;-)The response returns a Content-Location header with a version-specific location….see next slide
This is not only the URL you use to retrieve the resource, it’s also its id.All URL’s in FHIR are case-sensitive (and so is the id)
You can retrieve any person using a GET on the person’s id, which is just an url on the server: /fhir/person/@<id>We have our own MIME-type: “text/xml+fhir”Note that FHIR always uses UTF-8. Since this is not the default for HTTP, the server explicitly mentions thisBut should mean the xml encoding mentions “utf-8” and that the payload is really encoded in utf-8There can be a Byte Order Mark, but hopefully your framework handles all that ;-)The response returns a Content-Location header with a version-specific location….see next slide
Both the Resource id URL and the version-specific URL are used on many places of the REST spec and resource content (References!). They are always used consistently in this form.
* Note that you have two ways (at one moment) to reach version 15
You can retrieve any person using a GET on the person’s id, which is just an url on the server: /fhir/person/@<id>We have our own MIME-type: “text/xml+fhir”Note that FHIR always uses UTF-8. Since this is not the default for HTTP, the server explicitly mentions thisBut should mean the xml encoding mentions “utf-8” and that the payload is really encoded in utf-8There can be a Byte Order Mark, but hopefully your framework handles all that ;-)The response returns a Content-Location header with a version-specific location….see next slide
You can retrieve any person using a GET on the person’s id, which is just an url on the server: /fhir/person/@<id>We have our own MIME-type: “text/xml+fhir”Note that FHIR always uses UTF-8. Since this is not the default for HTTP, the server explicitly mentions thisBut should mean the xml encoding mentions “utf-8” and that the payload is really encoded in utf-8There can be a Byte Order Mark, but hopefully your framework handles all that ;-)The response returns a Content-Location header with a version-specific location….see next slide
- Whether server allows this depends on the level of trust between server and client: e.g. in-house scenario versus nation-wide network.
Behaviour depends on server configuration (and made public in a conformance statement).
* the deletion operation should be understood as deleting the record of the resource, with nothing about the state of the real-world corresponding resource implied.
* Now, if you query for /server.org/fhir/patient/@33, you get a 410
* The resource returns back to life!
- In our previous section on REST we saw how we mapped this metadata to HTTP headers, but in a query result, we need to find a way to map this to a list, Atom
Note that the id of the resource is kept outside the resource itselfAtom has many other meta-data items (not shown), which we will discuss later.
See how the resource meta-data is mapped to equivalent Atom membersAuthor is required by atom, so you’ll have to keep track who authored the resource, which might well be the user that POSTed it to your RESTful endpointSummary is optional, but it is easy to fill it with the Resource’s <text> (human readable narrative), so Feed readers have a way to display the contents of a resource. Yes, this means the summary is present twice in the entry.All elements you see here are Atom spec, not FHIR
Multiple versions are supported explicitly by Atom by having mutliple entries with the same id, but a different ‘updated’ dateNotice how the ‘self’ links do differ, these are different versions after all.
Brand-new RFC, probably not much support in frameworks (yet).Note the additional namespaceUpdated Timestamp is still present, but is now called “when”.
Just take a quick glimpse, it’s a pretty trivial translation of the Xml form to JSON, removing all needs for namespaces and attributes, and turning repeating elements into JSON arrays.
* You might wonder: Since bundles are Atom, why not use standard Atom parsers?
A server might defer validation to another server (because it doesn’t know the profile)A server may fetch the “unknown” profile and validate it itselfThere may be several servers sharing the work
A Document, no matter how nested, is flattened to a list of entries, the Document’s header being the first.The document header (and any other the other resources) refer to each other using normal references to reflect the document’s nesting.Of course, there may be a digital signature (on the whole Bundle) to attest to the content of the document.
* But ofcourse, some server could have functions to disassemble documents on reception. Since the contents of documents are resources, each of the contained resources (including the Document header) can be stored using the normal REST interface. But they are no longer the document anymore!
* But ofcourse, some server could have functions to disassemble documents on reception. Since the contents of documents are resources, each of the contained resources (including the Document header) can be stored using the normal REST interface. But they are no longer the document anymore!
A Message is similar, refers (amongst others) to its author, and contains information about the source, destination and the event that triggered it.A message contains 1 “data” resource, which is the root of the payload of the message. This is just a normal resource, which in its turn can refer to other related resources.
* It’s the same drop-off point as for documents!
You’re a message broker routingandtranslating between v2, v3 and FHIRYourapp interfaces with a PHR using FHIR nativelyYourapplicationcancommunicateusing FHIR, but your software uses a proprietary RDBMSUse FHIR as the common languagefor a Vendor Neutral Archive…Or a combination….Youmightsee data as a nestedstructure of XML, a series of tableswithkeys, class-diagrams….
First scenario is most common forexisting databases: you have to map the FHIR structure (as POCO’s) ontoyour (existing) databases tables. Lot’s of mappingsto support our nesting, cardinalities, datatypesSecond scenario uses the parserstocreatePOCO’sthenuse a NoSqldriver’sserializationpossibilitiesto store the structure-as-is in NoSqlThird scenario usesPOCO’sand DBMS, but instead of mapping FHIR totables, stores the data as-is intoblob storage in a DBMS
We tried to find a way to automatically convert between the two so you could round-trip…But discovered you’d need additional metadata to do that, or vendor-specific solutionsDecided not to introduce these: result was weird Xml or weird JsonReference platform contains tools to interconvert (these use model definition metadata)
You might have to keep the original (as you received it on the service interface), for attestation/digital signaturesStore the binaries in another location (e.g. Amazon S3, filesystem) and just keep the URL here.