Chuyển đến nội dung chính

Bài 8: FHIR RESTful API - CRUD, Search, History và Versioning

Chi tiết các tương tác REST: create (POST), read (GET), update (PUT), patch (PATCH), delete (DELETE), vread, history. Content negotiation (JSON/XML), ETag, If-Match, Conditional operations, CapabilityStatement.

🏗️ Kiến trúc — Bài 8 Bài 8: FHIR RESTful API - CRUD, Search, History và Versioning

HL7 FHIR - Chuẩn Dữ liệu Y tế từ Cơ bản đến Nâng cao

Phần 3: FHIR RESTful API và Data Exchange

xdev.asia

Xem bản video

1. FHIR RESTful API Overview

FHIR sử dụng RESTful API làm paradigm tương tác chính. Mỗi Resource type có một endpoint riêng, tuân theo chuẩn HTTP.

Base URL

https://fhir-server.example.com/fhir/r5
        ├── base URL ──────────────────┘
        
GET [base]/Patient/123           → Đọc Patient có id=123
POST [base]/Patient              → Tạo Patient mới
PUT [base]/Patient/123           → Cập nhật Patient 123
DELETE [base]/Patient/123        → Xóa Patient 123

Tổng quan các Interactions

InteractionHTTP MethodURLMô tả
readGET[base]/[type]/[id]Đọc resource
vreadGET[base]/[type]/[id]/_history/[vid]Đọc version cụ thể
updatePUT[base]/[type]/[id]Cập nhật resource
patchPATCH[base]/[type]/[id]Cập nhật một phần
deleteDELETE[base]/[type]/[id]Xóa resource
createPOST[base]/[type]Tạo resource mới
searchGET / POST[base]/[type]?paramsTìm kiếm
historyGET[base]/[type]/[id]/_historyLịch sử thay đổi
capabilitiesGET[base]/metadataCapabilityStatement

2. Create (POST)

POST /fhir/r5/Patient HTTP/1.1
Host: fhir-server.example.com
Content-Type: application/fhir+json
Prefer: return=representation

{
  "resourceType": "Patient",
  "name": [
    {
      "family": "Nguyễn",
      "given": ["Văn", "A"],
      "text": "Nguyễn Văn A"
    }
  ],
  "gender": "male",
  "birthDate": "1990-05-15"
}
HTTP/1.1 201 Created
Location: /fhir/r5/Patient/newly-assigned-id/_history/1
ETag: W/"1"
Last-Modified: 2025-01-15T10:00:00Z
Content-Type: application/fhir+json

{
  "resourceType": "Patient",
  "id": "newly-assigned-id",
  "meta": {
    "versionId": "1",
    "lastUpdated": "2025-01-15T10:00:00.000Z"
  },
  ...
}

Conditional Create

POST /fhir/r5/Patient HTTP/1.1
If-None-Exist: identifier=http://hospital.vn/mrn|MRN12345

{...}

Chỉ tạo nếu chưa tồn tại Patient nào match điều kiện → tránh duplicate.

3. Read (GET)

GET /fhir/r5/Patient/patient-001 HTTP/1.1
Accept: application/fhir+json

Version Read (vread)

GET /fhir/r5/Patient/patient-001/_history/3 HTTP/1.1

Content Negotiation

Accept HeaderFormat
application/fhir+jsonJSON (khuyên dùng)
application/fhir+xmlXML
Hoặc dùng _format param?_format=json

4. Update (PUT)

PUT /fhir/r5/Patient/patient-001 HTTP/1.1
Content-Type: application/fhir+json
If-Match: W/"3"

{
  "resourceType": "Patient",
  "id": "patient-001",
  "name": [
    {
      "family": "Nguyễn",
      "given": ["Văn", "A"],
      "text": "Nguyễn Văn A"
    }
  ],
  "gender": "male",
  "birthDate": "1990-05-15",
  "telecom": [
    {
      "system": "phone",
      "value": "0901234567",
      "use": "mobile"
    }
  ]
}

If-Match: W/"3" → Optimistic locking. Chỉ update nếu version hiện tại là 3. Nếu đã thay đổi → 409 Conflict.

Conditional Update

PUT /fhir/r5/Patient?identifier=http://hospital.vn/mrn|MRN12345 HTTP/1.1

{...resource body...}

5. Patch (PATCH)

JSON Patch (RFC 6902)

PATCH /fhir/r5/Patient/patient-001 HTTP/1.1
Content-Type: application/json-patch+json

[
  {
    "op": "add",
    "path": "/telecom/-",
    "value": {
      "system": "email",
      "value": "[email protected]",
      "use": "home"
    }
  },
  {
    "op": "replace",
    "path": "/active",
    "value": true
  }
]

FHIRPath Patch

PATCH /fhir/r5/Patient/patient-001 HTTP/1.1
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "operation",
      "part": [
        {"name": "type", "valueCode": "add"},
        {"name": "path", "valueString": "Patient"},
        {"name": "name", "valueString": "active"},
        {"name": "value", "valueBoolean": true}
      ]
    }
  ]
}

6. Delete (DELETE)

DELETE /fhir/r5/Patient/patient-001 HTTP/1.1

FHIR servers thường implement logical delete (đánh dấu đã xóa, giữ lại trong history) thay vì xóa vật lý.

7. History

# History của một resource
GET /fhir/r5/Patient/patient-001/_history

# History của tất cả Patient
GET /fhir/r5/Patient/_history

# History toàn server
GET /fhir/r5/_history

# Với parameters
GET /fhir/r5/Patient/patient-001/_history?_count=10&_since=2025-01-01T00:00:00Z

Response trả về Bundle type history chứa tất cả versions.

8. Search cơ bản

# Tìm Patient theo tên
GET /fhir/r5/Patient?name=Nguyen

# Tìm theo giới tính
GET /fhir/r5/Patient?gender=male

# Kết hợp nhiều tham số (AND)
GET /fhir/r5/Patient?name=Nguyen&gender=male&birthdate=1990-05-15

# Tìm Observation theo Patient
GET /fhir/r5/Observation?subject=Patient/patient-001&category=vital-signs

# POST search (khi query string quá dài)
POST /fhir/r5/Patient/_search
Content-Type: application/x-www-form-urlencoded

name=Nguyen&gender=male

Search Response (Bundle searchset)

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 42,
  "link": [
    {
      "relation": "self",
      "url": "https://fhir-server.example.com/fhir/r5/Patient?name=Nguyen&_count=10"
    },
    {
      "relation": "next",
      "url": "https://fhir-server.example.com/fhir/r5?_getpages=uuid-abc&_pageId=2"
    }
  ],
  "entry": [
    {
      "fullUrl": "https://fhir-server.example.com/fhir/r5/Patient/patient-001",
      "resource": {
        "resourceType": "Patient",
        "id": "patient-001"
      },
      "search": {
        "mode": "match",
        "score": 1
      }
    }
  ]
}

9. CapabilityStatement

GET /fhir/r5/metadata HTTP/1.1
Accept: application/fhir+json

CapabilityStatement (tên cũ: Conformance) mô tả khả năng của FHIR server: hỗ trợ resource types nào, interactions nào, search parameters nào.

10. HTTP Status Codes trong FHIR

StatusÝ nghĩa
200 OKRead/Search/Update thành công
201 CreatedCreate thành công
204 No ContentDelete thành công
304 Not ModifiedConditional read, chưa thay đổi
400 Bad RequestRequest không hợp lệ
401 UnauthorizedChưa xác thực
403 ForbiddenKhông có quyền
404 Not FoundResource không tồn tại
409 ConflictVersion conflict (If-Match)
410 GoneResource đã bị xóa
412 Precondition FailedConditional operation failed
422 Unprocessable EntityValidation failed (OperationOutcome)

11. Tổng kết

  • FHIR RESTful API tuân thủ chuẩn HTTP: POST/GET/PUT/PATCH/DELETE

  • Versioning — Mỗi update tạo version mới, truy xuất qua vread và history

  • Conditional operations — If-Match, If-None-Exist cho concurrency control

  • Search — Flexibility cao với nhiều parameter types, kết hợp AND/OR

  • CapabilityStatement — Tự mô tả khả năng server (machine-readable)