# FHIR REST API - CRUD Operations ###### tags: `fhir-project-ig` `CRUD` ## Table of Contents [TOC] ## Introduction This tutorial is designed for FHIR beginners wishing to become familiarised with the **basics of FHIR**. In this tutorial, we will walk through the basic **CRUD operations**. CRUD stands for **Create, Read, Update & Delete**. These are the four main actions that you will use to interact with the FHIR resources within a FHIR server. Understanding these four operations is essential to working with any **RESTful** service which a FHIR server implementat of. REST stands for 'Representational state transfer' and RESTFul just means a system that conforms to the constraints of REST. You don't need to know any more about REST at this stage for the tutorial but if you want more on REST take a look at this page, [Representational state transfer (REST)](https://en.wikipedia.org/wiki/Representational_state_transfer). I’m going to share a step-by-step how to use the FHIR Endpoint to perform basic CRUD (Create/Read/Update/Delete) Operations: * User: How to use the FHIR CRUD system ([Demo](https://victoriatjia.github.io/FHIR_CRUD/simple/html/Read.html)) * Developer: How to use Javascript XMLHttpRequest to perform FHIR CRUD Operations ([Source Code](https://github.com/victoriatjia/FHIR_CRUD)) ----------------------------- ## :pencil: Exercise by Scenario <!--, or directly submit an issue in GitHub,--> :::danger **!!!Attention!!!** All the cases mentioned in this scenario exercise, which are not marked or expressly stated as "real cases", are the data generated by the author based on the needs of the question, and all the fields in it are fictitious. If you find some similar information, please notify immediately via Email: victoriatjiaa@gmail.com and I will immediately correct the similar information. All the materials marked "real cases" are sourced from the author's own medical records, voluntarily provide own personal data to be use as an example for the teaching purposes. All the materials mentioned above, whether they are real cases or not, are only for academic research and teaching purposes, and any commercial or profit-making applications are strictly prohibited. The copyright and intellectual property rights of the materials still belong to the author. If there is any use in violation of these regulations, the author has the right to cancel the reader's authorization to use the materials. ::: ### Exercise 1: Create a Patient resource This question is to assess your knowledge of converting basic data into Resource in FHIR. The following is a patient's profile: :::info * Patient's name: Hannah Vlixie * Passport number: B0912988 * Contact number: (home) 021-21421 (public) 021-31976 (mobile) 628-523222111 * Residential address: Jl. Pluit Karang No.444, Penjaringan District, North Jakarta City, Jakarta Province 14450 * Emergency Contact Name: Felix Vlixon * Emergency Contact Phone: (mobile) 628-14433114 * Emergency Contact Relationship: Father * Patient's frequent used communication language is English (en-US) ::: Please use what you have learned about FHIR to design an FHIR Resource using the patient information above and save the results in either XML or JSON format. **Reference answer** :::spoiler Click me to see the reference answer ```gherkin= { "resourceType": "Patient", "identifier": [ { "use": "official", "type": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "PPN", "display": "Passport Number" } ] }, "value": "B0912988" } ], "name": [ { "use": "official", "text": "Hannah Vlixie", "family": "Hannah", "given": [ "Vlixie" ] } ], "telecom": [ { "system": "phone", "value": "021-21421", "use": "home" }, { "system": "phone", "value": "021-31976", "use": "work" }, { "system": "phone", "value": "628-523222111", "use": "mobile" } ], "gender": "female", "address": [ { "use": "home", "type": "both", "text": "Jl. Pluit Karang No.444, Penjaringan District , North Jakarta City, Jakarta Province 14450", "line": [ "Jl. Pluit Karang No.444" ], "city": "North Jakarta", "district": "Penjaringan", "postalCode": "14450", "country": "ID" } ], "contact": [ { "relationship": [ { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v2-0131", "code": "C", "display": "Emergency Contact" } ], "text": "Father" } ], "name": { "use": "official", "text": "Felix Vlixon" }, "telecom": [ { "system": "phone", "value": "628-14433114", "use": "mobile" } ] } ], "communication": [ { "language": { "coding": [ { "system": "urn:ietf:bcp:47", "code": "en-US" } ], "text": "English (US)" } } ] } ``` ::: ### Exercise 2: Upload a Patient resource This question is to assess your knowledge of interaction between REST API with FHIR Server Use what you have learned about the REST API to upload the FHIR data you completed in Exercise 1 into FHIR Server. **Hint** :::spoiler You could use Postman as a tool ::: ## User Version ### FHIR Create Scenario 1. Create a FHIR Person ![](https://i.imgur.com/yh8c3kJ.gif) ### FHIR READ Scenario 1. Read FHIR Person based on ID ![](https://i.imgur.com/2LHXbM3.gif) Scenario 2. Read FHIR Person based on query param <table> <tr> <td><b>Method</b></td> <td>POST</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>/<font color="green">[Resource ID]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Person/38361"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Person</font>/<font color="green">38361</font></a></td> </tr> </table> ### FHIR UPDATE E.g. Update FHIR Person we create on the previous step by changing its name from "Victoria" into "Victoria Tjia" ![](https://i.imgur.com/BKr0CWb.gif) ### FHIR DELETE E.g. Delete FHIR Person we create on the previous step ![](https://i.imgur.com/3qupdW0.gif) ----------------------------- ## Developer Version ### FHIR CRUD --- #### FHIR CREATE Scenario 1. Create a Patient data <table> <tr> <td><b>Method</b></td> <td>POST</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font></a></td> </tr> </table> --- #### FHIR READ Scenario 1. Read Patient data based on Resource ID <table> <tr> <td><b>Method</b></td> <td>GET</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>/<font color="green">[Resource ID]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient/TH00001"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font>/<font color="green">TH00001</font></a></td> </tr> </table> Scenario 2. Read Patient data based on Patient Medical Record Number (MRN) <table> <tr> <td><b>Method</b></td> <td>GET</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>?<font color="#009933">[query key]</font>=<font color="#cc00cc">[query value]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient?identifier=TH00001"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font>?<font color="#009933">identifier</font>=<font color="#cc00cc">TH00001</font></a></td> </tr> </table> Scenario 3. Read Patient data based on Patient Name <table> <tr> <td><b>Method</b></td> <td>GET</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>?<font color="#009933">[query key]</font>=<font color="#cc00cc">[query value]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient?name=victoria"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font>?<font color="#009933">name</font>=<font color="#cc00cc">victoria</font></a></td> </tr> </table> --- #### FHIR UPDATE Scenario 1. Update Patient data <table> <tr> <td><b>Method</b></td> <td>PUT</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>/<font color="green">[Resource ID]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient/TH00001"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font>/<font color="green">TH00001</font></a></td> </tr> </table> --- #### FHIR DELETE Scenario 1. Delete Patient data <table> <tr> <td><b>Method</b></td> <td>DELETE</td> </tr> <tr> <td><b>Format</b></td> <td><font color="red">[FHIR Endpoint]</font>/<font color="blue">[Resource Type]</font>/<font color="green">[Resource ID]</font></td> </tr> <tr> <td><b>URL Example</b></td> <td><a href="https://hapi.fhir.org/baseR4/Patient/TH00001"><font color="red">https://hapi.fhir.org/baseR4</font>/<font color="blue">Patient</font>/<font color="green">TH00001</font></a></td> </tr> </table> --- <!-- //CRUD IN JS ### The XMLHttpRequest Object The XMLHttpRequest object can be used to exchange data with a server behind the scenes. This means that it is possible to update parts of a web page, without reloading the whole page. ### XMLHttpRequest Object Methods | Method | Description | | --- | --- | | new XMLHttpRequest() | Creates a new XMLHttpRequest object | | open(_method,url,async,user,psw_) | Specifies the request <br> _method_: the request type (**GET/POST/PUT/DELETE**) <br>_url_: the file location <br>_async_: true (asynchronous) or false (synchronous) <br>_user_: optional user name <br>_psw_: optional password | send() | Sends the request to the server (Used for **GET** and **DELETE** requests) | | send(_string_) | Sends the request to the server (Used for **POST** and **PUT** requests) | setRequestHeader(header, value) | Adds HTTP headers to the request <br>_header_: specifies the header name <br>_value_: specifies the header value| ### XMLHttpRequest Object Properties | Property | Description | | --- | --- | | onreadystatechange | Defines a function to be called when the readyState property changes | | readyState | Holds the status of the XMLHttpRequest. <br> 0: request not initialized <br> 1: server connection established <br> 2: request received <br> 3: processing request <br> 4: request finished and response is ready | | responseText | Returns the response data as a string | | responseXML | Returns the response data as XML data | | status | Returns the status-number of a request <br> 200: "OK" <br> 403: "Forbidden" <br> 404: "Not Found" <br> For a complete list go to the [Http Messages Reference](https://www.w3schools.com/tags/ref_httpmessages.asp) | | statusText | Returns the status-text (e.g. "OK" or "Not Found") | In addition, **POST/PUT/GET/DELETE** are using the same xmlhttp function to perform the operations. The main different is in: | Method | Open request function | Send request function | | -------- | -------- | -------- | | POST | xhttp.open("POST", url, true) | xhttp.send(string)| | PUT | xhttp.open("PUT", url, true) | xhttp.send(string);| | GET | xhttp.open("GET", url, true) | xhttp.send()| | DELETE | xhttp.open("DELETE", url, true) | xhttp.send()| When we want to open request, we need to specifies what kind of action we want to request (POST/PUT/GET/DELETE). As for sending request, since POST (upload data) and PUT (update data) both need to write or perform change to the data, thus it need to include the string of data we want to write/change. In GET and DELETE, we only want to read or delete the data, so no need to send the string of data since we can obtain it by using the resource ID.** Now we can see the function content and I'll explain line by line for the HTTP POST only, since the other function method is just having exact same function format. #### HTTP POST **Function name**: postResource **Description**: Send data to server **Parameter**: * URL: Server path * ResourceName: FHIR Resource type * Parameter: Filter parameter to search * ReponseType: Requested data type returned by the server (json or xml) * AfterFun: The function to be executed after the data is obtained * RequestData: Parameter to be send to server ``` gherkin= function postResource(URL, ResourceName, Parameter, ResponseType, AfterFun, RequestData){ var url = URL + ResourceName + Parameter; var xhttp = new XMLHttpRequest(); xhttp.open("POST", url, true); xhttp.setRequestHeader("Content-type", 'text/' + ResponseType); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var str = this.response; eval(AfterFun)(str); return str; } else if(this.readyState == 4 && this.status != 200){ retValue(this.response); } }; xhttp.send(RequestData); } ``` Description: > **Line 2**: Define a FHIR URL with format: [FHIR Server URL]/[ResourceType]/[Parameter] > **Line 5**: For the header request, we usually only define the "ContentType" header, the value is requested as we need, it can be "json" or "xml" > **Line 7-8**: When request complete (readyState=4 and status=200), then we obtain the response value (this.response) and store it into variable "str" > **Line 9**: Next we will continue by performing the next function we want using the "eval" function along by passing the "str" variable. Eval function is used to convert string to JavaScript function code for execution. It will take the string variable called "AfterFun" and see it as a function instead of a string. > **Line 12-13**: If the request is failed (readyState=4 and status!=200), we will continue perform "retValue" function for alert some message box so the user aware of current situation. #### HTTP PUT **Function name**: putResource **Description**: Update data to server **Parameter**: * URL: Server path * ResourceName: FHIR Resource type * Parameter: Filter parameter to search * ReponseType: Requested data type returned by the server (json or xml) * AfterFun: The function to be executed after the data is obtained * RequestData: Parameter to be send to server ``` gherkin= function deleteResource(URL, ResourceName, Parameter, AfterFun){ var url = URL + ResourceName + Parameter; var xhttp = new XMLHttpRequest(); xhttp.open("PUT", url, true); xhttp.setRequestHeader("Content-type", 'text/' + ResponseType); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var str = this.response; eval(AfterFun)(str); return str; } else if(this.readyState == 4 && this.status != 200){ retValue(this.response); } }; xhttp.send(RequestData); } ``` #### HTTP GET **Function name**: getResource **Description**: Query data from server **Parameter**: * URL: Server path * ResourceName: FHIR Resource type * Parameter: Filter parameter to search * ReponseType: Requested data type returned by the server (json or xml) * AfterFun: The function to be executed after the data is obtained ``` gherkin= function getResource(URL, ResourceName, Parameter, ResponseType, AfterFun, RequestData){ var url = URL + ResourceName + Parameter; var xhttp = new XMLHttpRequest(); xhttp.open("GET", url, true); xhttp.setRequestHeader("Content-type", 'text/' + ResponseType); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var str = this.response; eval(AfterFun)(str); return str; } else if(this.readyState == 4 && this.status != 200){ retValue(this.response); } }; xhttp.send(); } ``` #### HTTP DELETE **Function name**: deleteResource **Description**: Delete data in server **Parameter**: * URL: Server path * ResourceName: FHIR Resource type * Parameter: Filter parameter to search * AfterFun: The function to be executed after the data is obtained ``` gherkin= function deleteResource(URL, ResourceName, Parameter, AfterFun){ var url = URL + ResourceName + Parameter; var xhttp = new XMLHttpRequest(); xhttp.open("DELETE", url, true); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var str = this.response; eval(AfterFun)(str); return str; } else if(this.readyState == 4 && this.status != 200){ retValue(this.response); } }; xhttp.send(RequestData); } -->