สถาบันข้อมูลขนาดใหญ่ (องค์การมหาชน)

Logo BDI For web

FHIR Server in action

Nov 4, 2021

*บทความนี้เหมาะสำหรับผู้มีพื้นฐานด้านการพัฒนา software มาบ้าง เช่นรู้จัก Git รู้จัก Docker เป็นต้น

Photo by Max Kukurudziak on Unsplash

เกริ่นนำ

ในระยะหลังมานี้สถานพยาบาลในเมืองไทยจัดการกับข้อมูลผู้ป่วยในรูปแบบ electronic มากขึ้น ไม่ว่าจะเป็นสถานพยาบาลของรัฐหรือเอกชน สังเกตจากเวลาเราไปหาหมอตามโรงพยาบาลใหญ่ ๆ ฝ่ายลงทะเบียนจะไม่ถามหาบัตรผู้ป่วยของสถานพยาบาลอีกต่อไปแล้ว แต่จะเป็นการถามหาบัตรประชาชนกับบัตรประกัน(ถ้ามี)แทน หลังจากนั้นพนักงานของแผนกลงทะเบียนก็จะป้อนข้อมูลเข้าสู่ระบบ HIS (Health Information System) เพื่อเพิ่มระเบียนการเข้าพบแพทย์ (Visit) และต่อจากนั้นระบบของสถานพยาบาลอาจจะออกบัตรคิวให้เราถือเพียงแผ่นเดียวเพื่อเดินไปยังแผนกต่าง ๆ

ถึงตรงนี้อาจมีคำถามในใจหลายท่านว่า “แล้ว FHIR คืออะไร? ทำไมเราถึงต้องใช้ FHIR?” ก่อนจะตอบคำถามนี้ผู้เขียนต้องขอวาดผังนักแสดงในละครเรื่องนี้กันสักเล็กน้อย

สถานพยาบาลที่ใช้ HIS
สถานพยาบาลที่ใช้ HIS

นี่คือภาพรวมของระบบ HIS ที่โรงพยาบาลหนึ่งตั้งขึ้นใช้งาน ผู้ป่วยเข้าพบบุคลากรทางการแพทย์ในแผนกต่าง ๆ และบุคลากรทางการแพทย์จึงเข้าใช้ระบบ HIS ผ่านเครื่อง HIS Client เพื่อจัดการข้อมูลผู้ป่วย จากนั้น HIS Sever จึงดำเนินการตามคำขอของ HIS Client

ถึงตรงนี้จะเกิดอะไรขึ้นถ้า สถานพยาบาลที่ใช้ HIS ยี่ห้อหนึ่งต้องการแลกเปลี่ยนข้อมูลกับสถานพยาบาลที่ใช้ HIS อีกยี่ห้อหนึ่ง ทุกอย่างจะไม่มีปัญหาถ้า โครงสร้างของฐานข้อมูลของ HIS สองยี่ห้อนั้นหน้าตาเหมือนกันชื่อผู้ป่วยถูกจัดเก็บไว้ในตาราง ชื่อเดียวกัน columns ต่าง ๆ ใช้ชื่อเดียวกัน ซึ่งในโลกแห่งความเป็นจริงนั้น HIS เกิดขึ้นมานานก่อนที่มาตรฐานข้อมูลสุขภาพในประเทศไทยจะเป็นรูปเป็นร่าง และ HIS แต่ละเจ้าก็ต่างคนต่างทำเป็นสูตรของใครก็ของเจ้านั้น ดังนั้นถ้าหากเราต้องการแลกเปลี่ยนกันได้ก็จำเป็นต้องมี “คนกลาง” ในการพูดคุยกันระหว่าง HIS ยี่ห้อ ก. และ HIS ยี่ห้อ ข.

ผู้เขียนขอกลับมาตอบคำถามว่า “FHIR คืออะไร?” FHIR (Fast Health Interoperability Resources) คือมาตรฐานข้อมูลสุขภาพซึ่งเขียนขึ้นโดยองค์กร HL7 คิดง่าย ๆ ก็คือ FHIR เป็น “คนกลาง”, “ล่าม” หรือ “วุ้นแปลภาษา” ให้ระบบที่มีโครงสร้างข้อมูลที่ต่างกันให้สื่อสารกันกายใต้โครงสร้างข้อมูล FHIR นั่นเอง ทีนี้สำหรับคำถาม “ทำไมเราต้องใช้ FHIR?” ในประเทศไทยเริ่มมีการใช้มาตรฐานข้อมูลสุขภาพขึ้นมาบ้างแล้ว เริ่มมีบทความเกี่ยวกับเรื่องนี้ขึ้น เช่น บทความ “4 ขั้นตอนสู่การแลกเปลี่ยนข้อมูลสุขภาพ ด้วย HL7 FHIR” โดยอาจารย์รัฐ ปัญโญวัฒน์ หรือแม้แต่โครงการระดับประเทศอย่างโครงการ Health Link เองก็มีการนำเอา มาตรฐาน FHIR มาใช้เช่นกัน ดังนั้นอาจจะตอบแบบกำปั้นทุบดินก็ว่าได้ว่า “ผู้บุกเบิกในด้านนี้ในเมืองไทยลองใช้มาก่อน”

ลองเล่นกับไฟ (FHIR Server)

ได้อ่านสิ่งที่ผู้เขียนเกริ่นมาพอจะทำให้ผู้อ่านมีไฟในการลองเล่นกับ “ไฟ” หรือไม่ครับ? หากเป็นเช่นนั้นแล้วเรามาทดลองดูกันครับว่าเราทำอะไรกับมันได้บ้าง ก่อนที่ผู้อ่านจะสามารถลองเล่นกับไฟได้ ก็ต้องจุดไฟก่อนครับ (ไม่ใช่ไฟนั้นน ><) ขอโทษครับไม่ใช่ไฟแบบนั้น (แต่ FHIR ออกเสียงว่า “ไฟร์” จริง ๆ นะเออ) เอาเป็นว่าขอให้ติดตั้งเครื่องมือดังต่อไปนี้บนเครื่องคอมพิวเตอร์ของผู้อ่านให้เรียบร้อยครับ

ไปเอา Source Code จากนักพัฒนา

หลังจากติดตั้งเครื่องมือด้านบนเรียบร้อยแล้ว ก็ถึงเวลาที่เราจะไปเอา Source Code จากนักพัฒนามาลองรันกัน โดยเจ้าที่ผู้อ่านจะแนะนำนั้นเป็นหนึ่งใน Software ที่พัฒนาให้เป็นไปตามมาตรฐาน FHIR มาอย่างยาวนานคือ HAPI FHIR เบื้องหลังของ Software ตัวนี้พัฒนาโดยใช้ Java (Springboot Framework) นะครับ ผู้อ่านสามารถเข้าไป clone ได้จาก repository นี้ ถ้าใช้ Command Line ก็คือ

> cd your/workspace/folder
> git clone https://github.com/hapifhir/hapi-fhir-jpaserver-starter

หลังจาก clone มาแล้ว เปิดเข้าไปใน folder ควรมีหน้าตาแบบนี้ครับ

Folder ที่ได้จากการ Clone
Folder ที่ได้จากการ Clone

สร้าง DevContainer

หลังจากที่เราเปิด Folder hapi-fhir-jpaserver-starter ขึ้นมาแล้วให้ Click ขวาเลือก Open with Code จาก Context Menu ครับ

Open with Code
Open with Code

จากนั้นใน VSCode สร้าง Folder ชื่อ .devcontainer ขึ้นมา

สร้าง Folder ชื่อ .devcontainer
สร้าง Folder ชื่อ .devcontainer

ใน Folder .devcontainer ให้สร้างไฟล์ขึ้นมาสามไฟล์ดังต่อไปนี้

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.1/containers/java
{
    "name": "Java",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspace",

    // Set *default* container specific settings.json values on container create.
    "settings": { 
        "java.home": "/docker-java-home",
        "sqltools.connections": [{
            "name": "Container database",
            "driver": "PostgreSQL",
            "previewLimit": 50,
            "server": "hapi-fhir-postgres",
            "port": 5432,
            "database": "hapi",
            "username": "admin",
            "password": "admin"
        }],
    },
    
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
        "gabrielbb.vscode-lombok",
        "mtxr.sqltools",
        "vscjava.vscode-java-pack",
        "mtxr.sqltools-driver-pg"
    ],

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Use 'postCreateCommand' to run commands after the container is created.
    "postCreateCommand": "sudo chown vscode:vscode /home/vscode/.m2 & sudo chown vscode:vscode /workspace/target-volume & java -version",

    // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
    "remoteUser": "vscode"
}
version: '3'

services:
  app:
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
      args:
        VARIANT: 11
        INSTALL_MAVEN: "true"
        INSTALL_GRADLE: "false"
        NODE_VERSION: "none"

    volumes:
      - ..:/workspace:cached
      - target-volume:/workspace/target-volume
      - m2-volume:/home/vscode/.m2

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
    # network_mode: hapi-fhir-postgres

    # Uncomment the next line to use a non-root user for all processes.
    # user: vscode

    # Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)

  hapi-fhir-postgres:
    image: postgres:12
    restart: unless-stopped

    # volumes:
    #   - hapi-fhir-postgres:/var/lib/postgresql/data

    environment:
      POSTGRES_DB: hapi
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin

    # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)
  
volumes:
  target-volume:
  m2-volume:
#   hapi-fhir-postgres:
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.202.1/containers/java/.devcontainer/base.Dockerfile

# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 11, 16, 11-bullseye, 16-bullseye, 11-buster, 16-buster
ARG VARIANT="16-jdk-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}

# [Option] Install Maven
ARG INSTALL_MAVEN="false"
ARG MAVEN_VERSION=""
# [Option] Install Gradle
ARG INSTALL_GRADLE="false"
ARG GRADLE_VERSION=""
RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven "${MAVEN_VERSION}""; fi 
    && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle "${GRADLE_VERSION}""; fi

# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive 
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

และภายใน Folder .vscode (ซึ่งมีอยู่แล้ว) ให้สร้างไฟล์เพิ่มอีกสองไฟล์ดังนี้

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Debug (Attach)",
            "request": "attach",
            "preLaunchTask": "mvnDebug",
            "hostName": "localhost",
            "port": 8000,
        },
    ]
}
{
    // See https://go.microsoft.com/fwlink/?LinkId":"733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "mvnDebug",
            "type": "shell",
            "options": {
                "env": {    
                    "DATASOURCE_URL":"jdbc:postgresql://hapi-fhir-postgres:5432/hapi",
                    "DATASOURCE_USERNAME":"admin",
                    "DATASOURCE_PASSWORD":"admin",
                },
            },

            "command": "mvnDebug jetty:run",
            "isBackground": true,
            "problemMatcher": [{
                "pattern": [{
                    "regexp": "\b\B",
                    "file": 1,
                    "location": 2,
                    "message": 3
                }],
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "^.*Preparing to execute Maven in debug mode.*",
                    "endsPattern": "^.*Listening for transport dt_socket at address.*"
                }
            }]
        }
    ]
}

สรุปไฟล์ที่เกิดขึ้นใหม่ใน Project จะมีตามนี้ครับ

ไฟล์ที่เกิดขึ้นใหม่
ไฟล์ที่เกิดขึ้นใหม่

สุดท้ายภายในไฟล์ pom.xml ให้เพิ่ม Code ต่อไปนี้

<directory>${project.basedir}/target-volume/target</directory>

ลงไปใน project/build โดยผลลัพธ์หลังแก้ไขจะเป็นแบบนี้

แก้ไขไฟล์ pom.xml
แก้ไขไฟล์ pom.xml

เมื่อถึงตอนนี้เราสามารถเปิด VSCode เข้าสู่ DevContainer ได้แล้วโดยการ Click ปุ่ม Open a Remote Window มุมล่างซ้ายของ VSCode และเลือก Reopen in Container

ปุ่ม Open a Remote Window
ปุ่ม Open a Remote Window
เมนู Reopen in Container

ทดลองรัน FHIR Server บนเครื่อง localhost

ภายหลังจากเปิด DevContainer ภายใน VSCode ขึ้นมาได้สำเร็จแล้ว ให้ผู้อ่านดูใน Tab Java Project และ หน้าต่าง VSCode Terminal ว่าเสร็จกระบวนการหรือยัง ขอ Internet Speed แรง ๆ ด้วยก็จะทำให้เสร็จเร็วขึ้นได้มากครับ

รอเปิด Project บน DevContainer
รอเปิด Project บน DevContainer

ตรงนี้ใช้เวลานานนิดนึงนะครับ สำหรับการเปิดครั้งแรก เพราะ VSCode จะไปควานหา Maven Dependency มาให้เรา หากใครพบปัญหาว่ามันทำงานไม่เสร็จสักทีหลังจากที่รอนานมาก ๆ (ตัวเลข xxx/1000 ไม่ขยับ อาจจะด้วยเพราะการเปิดครั้งก่อนหน้า crash หรือ Internet ถูกตัดกลางคัน) ผู้อ่านแนะนำให้ปิด VSCode แล้วเปิดบน DevContainer ใหม่อีกครั้ง หลังจากผ่านกระบวนการ Importing Maven Project ไปแล้ว การเปิดครั้งต่อ ๆ ไปควรจะเร็วขึ้นอย่างเห็นได้ชัด เพราะใน .devcontainer ผู้เขียนได้ mount .m2 repository เป็น volume ไว้ให้แล้ว เว้นเสียแต่ผู้อ่านใช้คำสั่ง docker volume prune หลังจากปิด VSCode ไป (ทำให้ .m2 volume หายไปด้วย ต้องโหลดกันใหม่นาจา ><)

หลังจาก VSCode ทำงานเสร็จเราจะพบว่าหน้าต่าง VSCode มีรายการ Java Projects ปรากฏขึ้นมาดังนี้

รายการ Java Projects
รายการ Java Projects

ทีนี้เราก็พร้อมรันแล้วครับ ไปที่เมนู Debug and Run จากนั้นกดปุ่มรันได้เลยครับ

เมนู Debug and Run
เมนู Debug and Run

จากนั้นรอ VSCode รัน Debug

รอ VSCode รัน Debug
รอ VSCode รัน Debug

พอรันสำเร็จจะได้หน้าตาแบบนี้ครับ

หลังจากรันสำเร็จ
หลังจากรันสำเร็จ

พอกดปุ่ม Open in Browser ก็จะได้พบกับ FHIR Tester Page หน้านี้ครับ (สำหรับใครที่กดไม่ทันก็เปิด browser แล้วใส่ URL localhost:8080 แทนได้นะครับ)

FHIR Test Page

การรับส่งข้อมูลกับ FHIR Server

ถึงเวลาของผู้ช่วยของเราอีกหนึ่งตัวที่จะมาแสดงบทบาทเพื่อคุยกับ FHIR Server กันแล้วครับนั้นก็คือ VSCode Plugin REST Client ก่อนอื่นขอให้ผู้อ่านเปิด VSCode หน้าต่างใหม่ขึ้นมา (โดยแยกจาก DevContainer) นะครับ

เปิด VSCode หน้าต่างใหม่
เปิด VSCode หน้าต่างใหม่

หลังจากนั้นให้สร้างไฟล์ใหม่ขึ้นมาโดยเลือกประเภทเป็น HTTP ครับ อาจจะตั้งชื่อว่า sample_request.http ก็ได้ครับ จากนั้นให้ใส่ Code ต่อไปนี้ลงในไฟล์ ในที่นี้คือข้อมูลใบรับรองแพทย์จำลองเพื่อนำไปขอสอบใบขับขี่

### CREATE
POST http://localhost:8080/fhir HTTP/1.1
Content-Type: application/json

{
    "resourceType": "Bundle",
    "type": "transaction",
    "entry": [
        {
            "request": {
                "method": "POST",
                "url": "Composition"
            },
            "resource": {
                "resourceType": "Composition",
                "identifier": {
                    "use": "official",
                    "value": "MJFPXKW"
                },
                "status": "final",
                "type": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "11503-0"
                        }
                    ]
                },
                "date": "2000-01-01",
                "title": "Medical Certification Composition",
                "author": [
                    {
                        "type": "Patient",
                        "reference": "urn:uuid:b1bb49cf-86c9-470b-915e-fbc2989d1205"
                    },
                    {
                        "type": "Practitioner",
                        "reference": "urn:uuid:ce6f92f1-e7fe-489b-b118-04d33f250ee7"
                    }
                ],
                "section": [
                    {
                        "title": "patient",
                        "entry": [
                            {
                                "type": "Patient",
                                "reference": "urn:uuid:b1bb49cf-86c9-470b-915e-fbc2989d1205"
                            }
                        ]
                    },
                    {
                        "title": "patient_section_item_1",
                        "entry": [
                            {
                                "type": "Condition",
                                "reference": "urn:uuid:77286c28-32b2-468f-a30e-b437b8f2b355"
                            },
                            {
                                "type": "Condition",
                                "reference": "urn:uuid:ea24a921-f516-45cb-b6e0-c5f2a6991601"
                            },
                            {
                                "type": "Condition",
                                "reference": "urn:uuid:79217720-3a23-4c2a-9719-1960c8d107cb"
                            }
                        ]
                    },
                    {
                        "title": "item_2",
                        "entry": []
                    },
                    {
                        "title": "item_3",
                        "entry": []
                    },
                    {
                        "title": "item_4",
                        "entry": []
                    },
                    {
                        "title": "vital_signs",
                        "entry": [
                            {
                                "type": "Condition",
                                "reference": "urn:uuid:77286c28-32b2-468f-a30e-b437b8f2b355"
                            },
                            {
                                "type": "Condition",
                                "reference": "urn:uuid:77286c28-32b2-468f-a30e-b437b8f2b355"
                            }
                        ]
                    },
                    {
                        "title": "clinical_findings",
                        "entry": [
                            {
                                "type": "Observation",
                                "reference": "urn:uuid:aae58675-37a0-43fb-966c-79f9053230ad"
                            },
                            {
                                "type": "Observation",
                                "reference": "urn:uuid:c71fa639-aeb9-44a1-aa87-21e91dcc5105"
                            },
                            {
                                "type": "Observation",
                                "reference": "urn:uuid:2332ce65-35f8-4277-86bd-02e57bed14cc"
                            }
                        ]
                    },
                    {
                        "title": "doctor_opinion",
                        "entry": [
                            {
                                "type": "Observation",
                                "reference": "urn:uuid:8c0a6dd3-de36-4314-a66a-9338f62ce6de"
                            },
                            {
                                "type": "DiagnosticReport",
                                "reference": "urn:uuid:fee34d6e-4a78-4c73-8038-cefc0d0b9361"
                            }
                        ]
                    }
                ]
            }
        },
        {
            "fullUrl": "urn:uuid:b1bb49cf-86c9-470b-915e-fbc2989d1205",
            "request": {
                "method": "POST",
                "url": "Patient"
            },
            "resource": {
                "resourceType": "Patient",
                "identifier": [
                    {
                        "use": "official",
                        "value": "0123456789123"
                    }
                ],
                "name": [
                    {
                        "prefix": "นางสาว",
                        "given": [
                            "รติกาล"
                        ],
                        "family": "ทศรัตนธรรมกูล"
                    }
                ],
                "gender": "female",
                "address": {
                    "line": "123 หมู่ 4 ถนนสู่สรวงสวรรค์",
                    "district": "เมือง",
                    "state": "นครสวรรค์",
                    "postalCode": "12345",
                    "country": "ไทย"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:ce6f92f1-e7fe-489b-b118-04d33f250ee7",
            "request": {
                "method": "POST",
                "url": "Practitioner"
            },
            "resource": {
                "resourceType": "Practitioner",
                "identifier": [
                    {
                        "use": "official",
                        "value": "012345"
                    }
                ],
                "name": [
                    {
                        "prefix": "นพ.",
                        "given": [
                            "ปราการ"
                        ],
                        "family": "ป้องบัลลังค์"
                    }
                ],
                "gender": "male"
            }
        },
        {
            "fullUrl": "urn:uuid:77286c28-32b2-468f-a30e-b437b8f2b355",
            "request": {
                "method": "POST",
                "url": "Condition"
            },
            "resource": {
                "resourceType": "Condition",
                "code": {
                    "text": "หอบหืด"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:ea24a921-f516-45cb-b6e0-c5f2a6991601",
            "request": {
                "method": "POST",
                "url": "Condition"
            },
            "resource": {
                "resourceType": "Condition",
                "code": {
                    "text": "เบาหวาน"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:79217720-3a23-4c2a-9719-1960c8d107cb",
            "request": {
                "method": "POST",
                "url": "Condition"
            },
            "resource": {
                "resourceType": "Condition",
                "code": {
                    "text": "ความดันสูง"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:9f605cc1-d39f-4000-89bf-8a5399b51f4c",
            "request": {
                "method": "POST",
                "url": "Organization"
            },
            "resource": {
                "resourceType": "Organization",
                "identifier": [
                    {
                        "use": "official",
                        "value": "001234567"
                    }
                ],
                "name": "โรงพยาบาลศรีธัญญา"
            }
        },
        {
            "fullUrl": "urn:uuid:aae58675-37a0-43fb-966c-79f9053230ad",
            "request": {
                "method": "POST",
                "url": "Observation"
            },
            "resource": {
                "resourceType": "Observation",
                "code": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "29463-7"
                        }
                    ]
                },
                "valueQuantity": {
                    "value": 75.1,
                    "unit": "kg"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:c71fa639-aeb9-44a1-aa87-21e91dcc5105",
            "request": {
                "method": "POST",
                "url": "Observation"
            },
            "resource": {
                "resourceType": "Observation",
                "code": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "8302-2"
                        }
                    ]
                },
                "valueQuantity": {
                    "value": 167,
                    "unit": "cm"
                }
            }
        },
        {
            "fullUrl": "urn:uuid:2332ce65-35f8-4277-86bd-02e57bed14cc",
            "request": {
                "method": "POST",
                "url": "Observation"
            },
            "resource": {
                "resourceType": "Observation",
                "code": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "85354-9"
                        }
                    ]
                },
                "component": [
                    {
                        "code": {
                            "coding": [
                                {
                                    "system": "http://loinc.org",
                                    "code": "8480-6"
                                }
                            ]
                        },
                        "valueQuantity": {
                            "value": 107,
                            "unit": "mmHg"
                        }
                    },
                    {
                        "code": {
                            "coding": [
                                {
                                    "system": "http://loinc.org",
                                    "code": "8462-4"
                                }
                            ]
                        },
                        "valueQuantity": {
                            "value": 60,
                            "unit": "mmHg"
                        }
                    }
                ]
            }
        },

        {
            "fullUrl": "urn:uuid:8c0a6dd3-de36-4314-a66a-9338f62ce6de",
            "request": {
                "method": "POST",
                "url": "Observation"
            },
            "resource": {
                "resourceType": "Observation",
                "code": {
                    "coding": [
                        {
                            "system": "medcer-system",
                            "code": "clinical-finding"
                        }
                    ]
                },
                "effectiveDateTime": "2000-01-01",
                "valueString": "เจ็บท้อง"
            }
        },
        {
            "fullUrl": "urn:uuid:fee34d6e-4a78-4c73-8038-cefc0d0b9361",
            "request": {
                "method": "POST",
                "url": "DiagnosticReport"
            },
            "resource": {
                "resourceType": "DiagnosticReport",
                "conclusion": "ขับได้ =__= มือเดียว!"
            }
        }
    ]
}

จากนั้นกด Send Request

เมนู Send Request
เมนู Send Request

จะพบหน้าต่างผลลัพธ์ปรากฏขึ้นคู่กันดังนี้

ผลลัพธ์การทำ HTTP Response
ผลลัพธ์ HTTP Response

นั่นหมายความว่าข้อมูลที่เราส่งเข้าไป ถูก FHIR Server เก็บไว้ในฐานข้อมูลเรียบร้อยแล้วครับ หากกลับไปยัง Browser แล้วไปยัง Resource ที่เราส่งข้อมูลเข้าไปจะพบระเบียนอยู่อย่างแน่นอน

FHIR Sample Resource
FHIR Sample Resource

จากไฟล์ HTTP นั้นเราสามารถส่ง Request ประเภทอื่น เพื่อคุยกับ FHIR Server ได้เช่นกันครับ หากเราเพิ่ม Code ลงในไฟล์ sample_request.http ดังนี้

### READ
GET http://localhost:8080/fhir/Composition/1 HTTP/1.1

หรือ

### Generate Document
GET http://localhost:8080/fhir/Composition/1/$document HTTP/1.1

หรือ

### UPDATE
PUT http://localhost:8080/fhir/Patient/2 HTTP/1.1
Content-Type: application/json

{
    "resourceType": "Patient",
    "id": 2,
    "identifier": [
        {
            "use": "official",
            "value": "0123456789123"
        }
    ],
    "name": [
        {
            "prefix": "นางสาว",
            "given": [
                "เปลี่ยนชื่อ"
            ],
            "family": "เปลี่ยนนามสกุล"
        }
    ],
    "gender": "female",
    "address": {
        "line": "123 หมู่ 4 ถนนสู่แดนดิน",
        "district": "เมือง",
        "state": "นครสวรรค์",
        "postalCode": "12345",
        "country": "ไทย"
    }
}

ก็จะสามารถใช้เล่นกับไฟที่อยู่บน localhost ของเราได้ตามใจชอบ (และสบายใจ) ตัวอย่าง Request สามอันหลังผู้เขียนไม่ได้แสดงผลลัพธ์ไว้ให้ดูเพราะผู้เขียนหวังว่าผู้อ่านอาจจะลองไปทำต่อ แล้วพบผลลัพธ์ด้วยตัวเองครับ

ของแถม

ถ้าผู้อ่านต้องการให้ล้างข้อมูลใน Database ทั้งหมดทุกครั้งที่รัน เราสามารถสั่ง Config ได้ในไฟล์ src/main/resources/application.yaml ดังนี้ครับ

ปรับแต่ง hibernate
ปรับแต่ง hibernate

การกำหนดค่าเป็น hibernate.hbm2ddl.auto: create คือการสั่งให้ Hibernate (ORM ของ Java) ลบตารางเก่า(ถ้ามี)และสร้างตารางใหม่ขึ้นมาตาม model ที่อยู่ภายใน FHIR Library ครับ (ของเดิม hibernate.hbm2ddl.auto: update นั้นจะเป็น update schema ไม่ได้ลบตาราง)

สรุป

หากผู้อ่านเดินทางมาจนถึงจุดนี้ได้ ผู้เขียนเชื่อเหลือเกินว่าผู้อ่านคงจะมีความสนใจ FHIR Server เป็นแน่แท้ (หรือไม่ก็อาจจะด้วยภาระหน้าที่บางประการจึงทำให้ต้องอ่าน) ผู้เขียนคิดว่าผู้อ่านคงจะได้ประโยชน์บางอย่างจากบทความนี้ไปบ้างไม่มากก็น้อย ทั้งในแง่มุมของการใช้เครื่องมือ และในแง่มุมของการที่ได้ศึกษามาตรฐาน FHIR ขอขอบคุณแหล่งความรู้ต่าง ๆ ที่อยู่ตามลิงค์ในบทความนี้ และจะขาดไปไม่ได้คือขอขอบคุณผู้อ่านทุกท่านอย่างยิ่งที่อ่าน (และทำตาม) มาจนจบบทความนี้ครับ

เนื้อหาโดย ประณิธาน ธรรมเจริญพร
ตรวจทานและปรับปรุงโดย อิสระพงศ์ เอกสินชล

Pranithan Thamcharoenporn

Developer and Data Engineer Government Big Data Institute (GBDi)

© Big Data Institute | Privacy Notice