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

Lesson 8: FHIR RESTful API - CRUD, Search, History and Versioning

Details of REST interactions: create (POST), read (GET), update (PUT), patch (PATCH), delete (DELETE), vread, history. Content negotiation (JSON/XML), ETag, If-Match, Conditional operations, CapabilityStatement.

🏗️ Architecture — Lesson 8 Lesson 8: FHIR RESTful API - CRUD, Search, History and Versioning

HL7 FHIR - Basic to Advanced Healthcare Data Standard

Part 3: FHIR RESTful API and Data Exchange

xdev.asia

1. FHIR RESTful API Overview

FHIR uses RESTful APIs as the main interaction paradigm. Each Resource type has its own endpoint, following the HTTP standard.

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

Overview of Interactions

InteractionHTTP MethodURLDescription
readGET[base]/[type]/[id]Read resources
vreadGET[base]/[type]/[id]/_history/[vid]Read specific version
update. updatePUT[base]/[type]/[id]Update resources
patch. patchPATCH[base]/[type]/[id]Partial update
deleteDELETE[base]/[type]/[id]Delete resources
createPOST[base]/[type]Create new resources
search. searchGET/POST[base]/[type]?paramsSearch
history. historyGET[base]/[type]/[id]/_historyHistory changes
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

{...}

Only create if there is no Patient that matches the condition → avoid duplicates.

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 (recommended)
application/fhir+xmlXML
Or use _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. Only update if the current version is 3. If it has changed → 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 often implement logical delete (mark deleted, retained in history) instead of physically deleting.

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 returns Bundle type history. history contains all versions.

8. Basic search

# 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 (old name: Conformance) describes the capabilities of the FHIR server: which resource types, interactions, and search parameters it supports.

10. HTTP Status Codes in FHIR

StatusMeaning
200 OKRead/Search/Update successfully
201 CreatedCreate successfully
204 No ContentDeleted successfully
304 Not ModifiedConditional read, not changed
400 Bad RequestsRequest is invalid
401 UnauthorizedNot authenticated
403 ForbiddenNo rights
404 Not FoundResource does not exist
409 ConflictVersion conflict (If-Match)
410 GoneResource has been deleted
412 Preconditions FailedConditional operation failed
422 Unprocessable EntityValidation failed (OperationOutcome)

11. Summary

  • FHIR RESTful API complies with HTTP standard: POST/GET/PUT/PATCH/DELETE

  • Versioning — Each update creates a new version, accessed via vread and history

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

  • Search — High flexibility with many parameter types, AND/OR combinations

  • CapabilityStatement — Self-describing server capabilities (machine-readable)