{{announcement.body}}
{{announcement.title}}

Implement FHIR REST Server With Couchbase

DZone 's Guide to

Implement FHIR REST Server With Couchbase

The article provides a sample FHIR Rest server that was implemented on Couchbase and walk you through the different search patterns as specified by FHIR specifications

· Database Zone ·
Free Resource

This is a follow up to my previous post that covered the topic of FHIR Data Model with Couchbase N1QL. In this blog, I will discuss the topic of how to implement the FHIR Search REST API Server over the Couchbase services, including actual examples of how the different FHIR search patterns work with Couchbase using the synthetic FHIR data provided by Synthea.

Please refer to my previous blog for background information on FHIR Data Model with Couchbase N1QL

Why Is This of Interest to You

  1. Your organization is planning to develop an FHIR compliant Electronic Health Record system (EHR), and you are looking into taking advantage of the many benefits of NoSQL databases, such as Distributed, High Availability, XDCR, and Multi-Dimensional Scalability.
  2. You are an architect, or developer and like understand how Couchbase JSON database can greatly reduce the complexity of your applications, by relegating the complex FHIR Search  [https://www.hl7.org/fhir/searchparameter-registry.html] processing to the database server layer.
  3. You are a data analyst and like to understand how you can leverage your SQL knowledge to query FHIR data directly with Couchbase N1QL and Full-Text-Search.
  4. You are interested in finding out how Couchbase N1QL, a SQL for JSON, can provide an efficient way to query a JSON data modelt hat is both hierarchical and relational in nature, as defined by HL7 Administrative Module.

FHIR Application With Couchbase


The diagram above shows the processes involved in a typical FHIR application. The FHIR Rest server is central to this application, and needs to support the search specifications  as specified by FHIR https://www.hl7.org/fhir/search.html. The REST server manages all the interactions between the client applications and translates the FHIR search requests into N1QL statements, and submits the requests to the Couchbase Query Service.

How to Set up the FHIR Server With Couchbase

You can set up the Couchbase FHIR API server, and the Couchbase NoSQL database on a single server, or even your laptop. The setup process requires:

  1. Installing Couchbase Server 6.5.
  2. Load the Synthea Data into the Couchbase Server.
  3. Deploy the dotnet FHIR Server code.

Please follow the instructions https://github.com/AV25242/dotnet-fhir-server-couchbase.git

FHIR Sample Data Set

Before you start using FHIR search to query, let’s familiarize ourselves with the FHIR sample data set.

  1. The Synthea data set consists of 1K synthetic patient records. 
  2. The patient record is in a FHIR resource bundle format, meaning a patient record includes all of the patient’s related FHIR objects.
Java
 




x
18


 
1
"Practitioner"
2
"ImagingStudy"
3
"MedicationRequest"
4
"Condition"
5
"Device"
6
"DiagnosticReport"
7
"CarePlan"
8
"Encounter"
9
"CareTeam"
10
"Claim"
11
"Procedure"
12
"Immunization"
13
"Observation"
14
"MedicationAdministration"
15
"Organization"
16
"Goal"
17
"ExplanationOfBenefit"
18
"AllergyIntolerance"



For the purpose of this demonstration, the patient resource bundle have been normalized into individual resource type documents, to allow us to query the FHIR object directly or indirectly via the reference id of the object.

FHIR Searches

You can test out the REST API server using any REST clients of your choice. For simplicity, I use CURL on my terminal.

1. Search Any FHIR Resource

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient
2
>curl -X GET http://<fhir-server-ip>/Fhir/api/Practitioner



2. Search Resource by ID

All FHIR resources have an id identifier, and can be searched directly.

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient/8850c4aa-cb77-4659-8373-980882405846
2
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient?id=8850c4aa-cb77-4659-8373-980882405846
3
>curl -X GET http://<fhir-server-ip>/Fhir/api/Organization?id=d32bd7bd-4211-34d8-a08b-b1b2b810d41b



3. Search Resource by Any Top Level Field

Search all insurance Claims that were provided by “BAYSIDE MEDICAL CENTER”

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Claim?provider.display=BAYSTATE MEDICAL CENTER



4. Search Resource by Name  -  N1QL ARRAY Search

The FHIR resource has a comprehensive structure to capture the resource name.

Java
 




xxxxxxxxxx
1
18


 
1
"name": [
2
        {
3
            "family": "Wizard",
4
            "given": [
5
                     "Buffy"
6
            ],
7
            "prefix": [1 item],
8
            "use": "official"
9
        },
10
        {
11
            "family": "Schneider",
12
            "given": [
13
                     "Tootsie"
14
            ],
15
            "prefix": [1 item],
16
            "use": "maiden"
17
        }
18
]



The FHIR search on name has to take into account all the different names the patient may use.

Java
 




xxxxxxxxxx
1


1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient/?name=Buffy



5. Search Patients by Medical Identifier - N1QL ARRAY Search

FHIR also supports identifiers for the resource. In the example below, a patient can be identified with his/her SSN and OID (for non-FHIR systems). 

Java
 




xxxxxxxxxx
1
30


 
1
"patient": {
2
           "address": [1 item],
3
           "birthDate": "1946-11-29",
4
           "communication": [1 item],
5
           "deceasedDateTime": "1983-03-25T09:56:18-08:00",
6
           "extension": [7 items],
7
           "gender": "female",
8
           "id": "b495844d-22d0-4045-a00d-99f6799df265",
9
           "identifier": [
10
                   {2 items},
11
                   {3 items},
12
                   {
13
                       "system": "http://hl7.org/fhir/sid/us-ssn",
14
                       "type": {2 items},
15
                       "value": "999-46-2135"
16
                   },
17
                   {
18
                       "system": "urn:oid:2.16.840.1.113883.4.3.25",
19
                       "type": {2 items},
20
                       "value": "S99963447"
21
                   },
22
                   {3 items}
23
            ],
24
            "maritalStatus": {2 items},
25
            "multipleBirthBoolean": false,
26
            "name": [1 item],
27
            "resourceType": "Patient",
28
            "telecom": [1 item],
29
            "text": {2 items}
30
}



Search Patient by the SSN identifier

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient?identifier=http://hl7.org/fhir/sid/us-ssn|999-46-2135



6. Search Resource by Medical System/Code - N1QL ARRAY Search

FHIR uses the medical code to identify Procedure, Observation, or DiagnosticReport.

Java
 




xxxxxxxxxx
1
21


 
1
"diagnosticreport": {
2
    "category": [1 item],
3
    "code": {
4
        "coding": [
5
            {
6
                "code": "57698-3",
7
                "display": "Lipid Panel",
8
                "system": "http://loinc.org"
9
            }
10
        ],
11
        "text": "Lipid Panel"
12
    },
13
    "effectiveDateTime": "2016-08-17T14:24:27-07:00",
14
    "encounter": {1 item},
15
    "id": "38f2e919-894c-4277-ba00-372c18c9cced",
16
    "issued": "2016-08-17T14:24:27.878-07:00",
17
    "resourceType": "DiagnosticReport",
18
    "result": [4 items],
19
    "status": "final",
20
    "subject": {1 item}
21
}



Search DiagnosticReport by its system & code

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/DiagnisticReport?code=http://loinc.org|57698-3



7. Search Resource by Phone/Email - N1QL ARRAY Search

FHIR record encapsulates all the communication channels to the resource in the “telecom” field.

Java
 




xxxxxxxxxx
1
12


 
1
"telecom": [
2
    {
3
        "system": "email",
4
        "use": "work",
5
        "value": "Quinton758.Hammes673@example.com"
6
    },
7
    {  
8
         "system": "phone",
9
         "use": "home",
10
         "value": "555-270-6484"
11
    }
12
]



The FHIR search on telecom  has to take into account all the different telecom types the resource may use.

Search Patients by phone

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient/?phone=555-270-6484



Search Practitioners by email

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Practioner/?email=Quinton758.Hammes673@example.com



Search Hospitals by phone

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Organization/?phone=978-524-7933



8. Search Resource Using Date Range - N1QL Range Scan

FHIR objects may include a datetime range to indicate the period in which an event occurred.

Java
 




xxxxxxxxxx
1


 
1
"performedPeriod": {
2
    "end": "2011-07-06T23:03:22-07:00",
3
    "start": "2011-07-06T22:48:22-07:00"
4
},



Search Procedures that took place between two dates

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Procedure?performedPeriod.start=ge2011-07-07&performedPeriod.end=le2011-07-08



9. Search Resource With _text Search - Couchbase FTS

FHIR search specification also includes a text search capability. A search request using the ‘_text’ field will instruct the server to perform a text search on all the text field.

Java
 




xxxxxxxxxx
1
15


 
1
"condition": {
2
    "abatementDateTime": "2019-06-01T20:53:48-07:00",
3
    "clinicalStatus": {1 item},
4
    "code": {
5
        "coding": [1 item],
6
        "text": "Fracture of ankle"
7
    },
8
    "encounter": {1 item},
9
    "id": "f2e4b232-513c-4222-9568-6fbe096bb6ba",
10
    "onsetDateTime": "2019-04-02T20:53:48-07:00",
11
    "recordedDate": "2019-04-02T20:53:48-07:00",
12
    "resourceType": "Condition",
13
    "subject": {1 item},
14
    "verificationStatus": {1 item}
15
}



Search Conditions with the term ‘Fracture’ in text description.

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Condition?_text=Fracture



Search Observations with the term ‘Cholesterol’’ in text description.

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Observation?_text=Cholesterol



10. Search Patients by Their Related Objects - N1QL JOIN

The FHIR data model is relational in nature. The objects that are associated with a Patient contain the Patient’s id in the subject field.  A patient has multiple CarePlans, and the plan has a subject field with the patient’s id to denote that the care plan belongs to the patient.

Java
 




xxxxxxxxxx
1
18


 
1
"careplan": {
2
    "activity": [2 items],
3
    "addresses": [1 item],
4
    "careTeam": [1 item],
5
    "category": [1 item],
6
    "encounter": {1 item},
7
    "id": "49b2239e-da9c-447a-9e7c-f5721339cf2f",
8
    "intent": "order",
9
    "period": {
10
        "start": "2015-03-22T21:38:17-07:00"
11
    },
12
    "resourceType": "CarePlan",
13
    "status": "active",
14
    "subject": {
15
        "reference": "urn:uuid:9aa55fa8-0422-4ca4-bc22-099aea53a590"
16
    },
17
    "text": {2 items}
18
}



Using the subject.reference field, the FHIR REST server can implement a database JOIN between the Patient and the related objects, to allow a search for the patient record who owns a specific plan with a plan id value

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient?CarePlan.id=49b2239e-da9c-447a-9e7c-f5721339cf2f



Search all Patients who had a particular Observation code

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient?Observation.code=http://loinc.org|29463-7



Search all Patients who have had a s particular medical Condition

Java
 




xxxxxxxxxx
1


 
1
>curl -X GET http://<fhir-server-ip>/Fhir/api/Patient?Condition.code.text=Hypertension



Why Couchbase Is a Better Platform to Build Your FHIR Server

A quick Google on FHIR Server returns 300K results, and the HL7 FHIR implementation page lists over dozen open source projects. Ready built FHIR server implementations are also available from many vendors. These implementations rely on different database technologies from traditional RDBMS, as well as many of the current NoSQL databases in the market today.

The dotnot-fhir-server-couchbase provides several examples on how you can build the FHIR search specifications using the Couchbase platform. The code translates the FHIR API search APIs and parameters into Couchbase N1QL statements, then submits them to the Couchbase Query service, where all the processing are processed. This approach can reduce the complexity in the REST server or the REST client application. A few key points to note:

  1. The FHIR JSON data are stored as-is in the Couchbase Data Service. There are no pre/post processing required when you need to POST/GET the FHIR document to/from the server.
  2. Couchbase N1QL query language has a rich set of operations that support the processing of the hierarchical data of the FHIR JSON format. Allowing seamless access to nested array of data, such as those used to describe patient's conditions, observations,  treatments, and diagnostic reports.
  3. FHIR requirements for Text Search can be met directly in Couchbase Full Text Search with N1QL Search() function, there is no need to map your searches to Lucene, ElasticSearch or Solr.
  4. FHIR chained search specifications could require a FHIR server implementation to submit multiple searches to the database in order process the request. However with N1QL, an ANSI join query can result in a much simpler code to meet the requirement.

Summary

FHIR Rest Servers are being implemented in all types of platforms.  The search protocols are relatively simple, focusing on the support of retrieving Patient, and other FHIR related objects by their specific attributes. The search APIs insulate the REST client from complex hierarchical and array processing, which are fundamentals to JSON data format.  However, the specification, as it is currently stands:

  1. Does not define a clear way to allow querying of patients (or other FHIR objects) by its related information, other than a simple parent and child capability. 
  2. There is no support for aggregation, thus limiting any analysis capabilities.

Because of these limitations, the REST client applications can become more complex as it needs to embed the logic of traversing the FHIR data model.  The example REST Server API code in this blog article, seeks to illustrate three key points

  1. The simplicity of implementing searching on array elements using Couchbase N1QL array construct.
  2. The ability to push down to the Couchbase Query service, to perform the search on Patient objects using related objects. In effect, leveraging N1QL ANSI JOIN supports.
  3. The ability to seamlessly integrate Full Text Search capabilities with Couchbase N1QL, without the need to use dedicated FTS platforms such ElasticSearch or Solr, as suggested in the FHIR Search specification.

I would also like to thank Arun Vijayraghavan, our Principal Product Manager for all Couchbase SDKs, who developed the dotnet-fhir-server-couchbase code, and the feedback from customers who have helped reviewing our FHIR search implementation. Please let us know if you have any questions, feedback or running into any issue setting the environment.

Resources

  1. Fast Healthcare Interoperability Resource: https://www.hl7.org/fhir/index.html
  2. SyntheticMass: “Jason Walonoski, Mark Kramer, Joseph Nichols, Andre Quina, Chris Moesel, Dylan Hall, Carlton Duffett, Kudakwashe Dube, Thomas Gallagher, Scott McLachlan, Synthea: An approach, method, and software mechanism for generating synthetic patients and the synthetic electronic health care record, Journal of the American Medical Informatics Association, Volume 25, Issue 3, March 2018, Pages 230–238, https://doi.org/10.1093/jamia/ocx079
  3. N1QL Tutorial: https://query-tutorial.couchbase.com/tutorial/#1
  4. FHIR Search Parameters: https://www.hl7.org/fhir/searchparameter-registry.html
Topics:
couchbase, database, fhir, n1ql, tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}