iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0

前言

在上一篇文章中,我們認識了FHIR Server的CapabilityStatement。

CapabilityStatement就像Server的能力說明書,可以告訴Client:

  • 使用哪一個FHIR版本
  • 支援哪些Resource
  • 支援哪些資料格式
  • 支援哪些搜尋參數
  • 允許哪些RESTful互動

如果一台FHIR Server在Patient的interaction中宣告支援:

{
  "code": "read"
}

就代表Client可以讀取一筆已知id的Patient Resource。

今天不進行實際API操作,而是透過Request與Response範例,認識FHIR的read互動,以及讀取Patient時需要注意的問題。

本文所有姓名、編號、網址及醫療資料均為虛構教學範例。


什麼是read互動?

read是FHIR RESTful API中的一種互動,用來讀取Server中某一筆已知id的Resource。

它的基本格式是:

GET [base]/[Resource Type]/[id]

可以拆成三個部分:

部分 用途
[base] FHIR Server的Base URL
[Resource Type] Resource類型
[id] Resource的邏輯id

假設FHIR Server的Base URL是:

https://hospital.example.org/fhir

要讀取id為patient-001的Patient,Request可以表示為:

GET https://hospital.example.org/fhir/Patient/patient-001
Accept: application/fhir+json

整句可以理解成:

請從這台FHIR Server讀取id為patient-001的Patient,並以FHIR JSON格式回傳。


為什麼使用GET?

在FHIR RESTful API中,GET主要用於讀取或搜尋資料。

GET具有Safe Method的概念,表示Client提出GET Request時,不應要求Server修改或刪除目標Resource。

因此:

GET /Patient/patient-001

只是在取得Patient資料,不是建立、更新或刪除Patient。

Server可能記錄存取Log、更新快取或產生稽核紀錄,但這些系統行為不等於修改Client要求讀取的Patient內容。


Read URL如何組成?

以下面這個URL為例:

https://hospital.example.org/fhir/Patient/patient-001

可以拆成:

URL部分 內容
通訊協定 https
網域 hospital.example.org
FHIR服務路徑 /fhir
Resource類型 /Patient
Resource id /patient-001

其中:

https://hospital.example.org/fhir

是FHIR Server的Base URL。

Patient

表示Resource類型。

patient-001

表示Patient Resource的邏輯id。

FHIR Resource名稱區分英文大小寫,因此應寫成:

Patient

而不是:

patient

讀取Patient前必須知道什麼?

FHIR read需要知道兩項關鍵資訊:

  1. Resource類型
  2. Resource的邏輯id

例如:

Patient/patient-001

如果只知道病人姓名或病歷號,通常不能直接組成read URL,而要先使用FHIR搜尋功能找出符合條件的Patient。

可以整理成:

已知資料 適合的互動
已知Patient id read
只知道姓名 search
只知道病歷號 search
只知道出生日期 search
需要符合多項條件 search

read和search都可能使用GET,但URL及Response結構不同。


id和病歷號不一定相同

這是讀取Patient時非常重要的概念。

一份Patient可能包含:

{
  "resourceType": "Patient",
  "id": "patient-001",
  "identifier": [
    {
      "system": "https://hospital.example.org/mrn",
      "value": "MRN0001"
    }
  ]
}

其中:

欄位 用途
id patient-001 FHIR Server中的Resource邏輯id
identifier.value MRN0001 醫院實務使用的病歷號
identifier.system https://hospital.example.org/mrn 病歷號所屬的識別系統

read使用的是Resource的id

GET /Patient/patient-001

不能直接假設病歷號就是FHIR Resource id。

如果只知道病歷號MRN0001,通常需要使用identifier搜尋,而不是直接寫成:

GET /Patient/MRN0001

除非該FHIR Server確實將兩者設計成相同值。


成功時會回傳什麼?

如果FHIR Server找到指定的Patient,也允許Client讀取,通常會回傳:

200 OK
Content-Type: application/fhir+json

Response Body則是一筆Patient Resource:

{
  "resourceType": "Patient",
  "id": "patient-001",
  "meta": {
    "versionId": "3",
    "lastUpdated": "2026-09-03T08:30:00Z"
  },
  "identifier": [
    {
      "system": "https://hospital.example.org/mrn",
      "value": "MRN0001"
    }
  ],
  "active": true,
  "name": [
    {
      "use": "official",
      "text": "王小明",
      "family": "王",
      "given": [
        "小明"
      ]
    }
  ],
  "gender": "male",
  "birthDate": "2000-01-01",
  "managingOrganization": {
    "reference": "Organization/hospital-001",
    "display": "範例醫院"
  }
}

最外層的:

"resourceType": "Patient"

表示這是一筆Patient,而不是搜尋結果Bundle。


如何確認回傳的是正確Patient?

可以先查看以下欄位:

resourceType

"resourceType": "Patient"

確認Resource類型為Patient。

id

"id": "patient-001"

確認回傳的id與Request URL中的id一致。

Request:

/Patient/patient-001

Response:

"id": "patient-001"

identifier

"identifier": [
  {
    "system": "https://hospital.example.org/mrn",
    "value": "MRN0001"
  }
]

查看病歷號等業務識別資料。

name及birthDate

"name": [
  {
    "text": "王小明"
  }
],
"birthDate": "2000-01-01"

這些欄位可以協助確認病人身分,但正式系統不能只依靠姓名判斷,因為可能有同名病人。


為什麼read回傳Patient,而不是Bundle?

read操作針對一筆已知id的Resource:

GET /Patient/patient-001

因此,成功Response通常直接回傳Patient。

搜尋則是依照條件尋找零筆、一筆或多筆Resource:

GET /Patient?name=王小明

所以搜尋結果使用Bundle包裝。

兩者可以比較如下:

比較項目 read search
是否需要已知id
Request範例 GET /Patient/123 GET /Patient?name=王小明
成功Response Patient Bundle
結果數量 一筆 可能是零筆、一筆或多筆
找不到時 404或其他狀態 通常回傳結果為0的Bundle

read成功後可能看到哪些Response Headers?

除了:

Content-Type: application/fhir+json

Server還可能回傳:

ETag: W/"3"
Last-Modified: Thu, 03 Sep 2026 08:30:00 GMT

ETag

ETag可以表示Resource的版本資訊。

ETag: W/"3"

可能對應:

"meta": {
  "versionId": "3"
}

Last-Modified

Last-Modified: Thu, 03 Sep 2026 08:30:00 GMT

表示Resource最後修改時間,可能對應:

"lastUpdated": "2026-09-03T08:30:00Z"

這些Header可以協助Client處理快取、版本及更新衝突。


meta.versionId代表什麼?

Patient中可能包含:

"meta": {
  "versionId": "3",
  "lastUpdated": "2026-09-03T08:30:00Z"
}

versionId表示這筆Resource目前的版本識別碼。

如果Patient資料曾經更新,例如新增電話或修改地址,Server可能產生新版本。

要注意:

  • versionId不是Patient id。
  • versionId不是病歷號。
  • versionId也不一定是單純從1開始累加的數字。

版本識別方式由FHIR Server管理,Client不應自行推測產生規則。


vread:讀取特定歷史版本

一般read取得的是Resource目前的版本:

GET /Patient/patient-001

FHIR還定義了vread,可以讀取特定歷史版本。

基本形式是:

GET /Patient/patient-001/_history/2

其中:

2

代表特定versionId

read和vread可以比較如下:

互動 用途
read 取得Resource目前版本
vread 取得Resource指定的歷史版本

不是所有FHIR Server都支援vread或保存完整歷史資料,因此需要查看CapabilityStatement。


找不到Patient時

如果Server中沒有:

Patient/patient-001

可能回傳:

404 Not Found

Response Body也可能提供OperationOutcome:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "not-found",
      "diagnostics": "Patient/patient-001 was not found."
    }
  ]
}

可能原因包括:

  • Patient id輸入錯誤
  • Resource不存在
  • Resource類型錯誤
  • Patient存在於另一台FHIR Server
  • Server基於安全政策不透露資料是否存在
  • URL使用了病歷號而不是Resource id

Resource已被刪除時

如果Patient以前存在,但目前已被刪除,Server可能回傳:

410 Gone

410和404的基本差異是:

狀態碼 基本概念
404 Not Found 找不到Resource
410 Gone Server知道Resource曾存在,但目前已刪除

實際Server是否區分404及410,取決於它的實作方式、歷史資料政策及CapabilityStatement。

刪除也不一定代表資料從所有儲存空間中完全消失。醫療系統可能因法規、稽核或資料治理需求保留歷史紀錄。


沒有登入或權限不足時

正式FHIR Server通常不會讓所有人任意讀取Patient。

如果Client沒有提供有效身分驗證資訊,可能收到:

401 Unauthorized

如果Client身分已被辨認,但沒有讀取這筆Patient的權限,可能收到:

403 Forbidden

可以簡化成:

狀態碼 基本意義
401 尚未通過有效身分驗證
403 已辨識身分,但權限不足

不過,為了避免洩露Resource是否存在,部分系統可能統一回傳其他狀態碼。因此,實際行為仍要查看Server的安全政策。


Patient存在,為什麼仍然不能讀取?

即使Patient確實存在,也可能因以下原因無法取得:

  • Client沒有登入
  • Access Token已過期
  • Token沒有Patient read權限
  • 使用者與病人沒有合法照護關係
  • 病人沒有同意特定資料用途
  • Resource受到安全標記限制
  • Server正在維護
  • Resource不符合目前API提供範圍
  • Request使用錯誤FHIR版本
  • 使用者只能讀取部分欄位

所以「知道Patient id」不代表具有讀取資料的合法權限。


read會自動回傳所有病歷嗎?

不會。

執行:

GET /Patient/patient-001

通常只會回傳Patient Resource。

Patient可能包含:

  • 姓名
  • 出生日期
  • 聯絡方式
  • 地址
  • 病歷號
  • 管理機構

它不會自動包含:

  • 所有Encounter
  • 所有Observation
  • 所有Condition
  • 所有MedicationRequest
  • 所有DiagnosticReport

這些資料會存在於其他Resource中,並透過Reference指向Patient。

例如,Observation可能包含:

"subject": {
  "reference": "Patient/patient-001"
}

如果要取得病人的Observation,需要另外搜尋相關Resource。


Response中的資料可能不完整嗎?

可能。

FHIR Patient中的許多欄位都是選填,Server也可能依照使用者權限只回傳部分內容。

例如:

{
  "resourceType": "Patient",
  "id": "patient-001",
  "name": [
    {
      "text": "王小明"
    }
  ]
}

這仍然可能是一筆符合FHIR基本結構的Patient,即使它沒有:

  • gender
  • birthDate
  • telecom
  • address

Resource是否符合實際業務需求,還要確認:

  • 使用的Profile
  • 欄位Cardinality
  • Server資料品質
  • Client存取權限
  • 原始資料是否完整

因此,read成功不代表所有希望取得的欄位一定存在。


read與資料最小化

醫療資料具有高度敏感性。

即使Client有權讀取Patient,系統也不一定需要回傳所有欄位。

例如,一個只負責顯示叫號資訊的系統,可能只需要:

  • 病人顯示名稱
  • 就醫序號
  • 診間資訊

它不一定需要病人的完整地址、電話或其他健康資訊。

這與資料最小化概念有關:

只提供完成特定目的所需要的資料,避免不必要的個人資料揭露。

FHIR定義資料交換結構,但哪些資料可以被誰讀取,仍需要透過權限、同意、組織政策及法規進行控制。


Client如何判斷一次read的結果?

概念上可以依照以下順序理解:

1. 查看Status Code

  • 200:成功
  • 401:身分驗證問題
  • 403:權限不足
  • 404:找不到Resource
  • 410:Resource已刪除
  • 500:Server內部錯誤

2. 查看Content-Type

Content-Type: application/fhir+json

確認Response Body是FHIR JSON。

3. 查看resourceType

成功時:

"resourceType": "Patient"

錯誤時可能是:

"resourceType": "OperationOutcome"

4. 確認id

"id": "patient-001"

確認與URL中的Resource id相符。

5. 查看meta

確認版本及最後更新時間。

6. 查看Patient欄位

例如identifier、name、gender及birthDate。

這是一種閱讀Response的思考順序,並非實際操作步驟。


一次read互動的完整概念

Request

GET https://hospital.example.org/fhir/Patient/patient-001
Accept: application/fhir+json

Server判斷

Server可能依序確認:

  • 是否支援Patient
  • 是否支援read
  • Client身分是否有效
  • Client是否具有權限
  • Patient是否存在
  • 可以回傳哪些欄位
  • 應使用哪一種資料格式

成功Response

200 OK
Content-Type: application/fhir+json
ETag: W/"3"
{
  "resourceType": "Patient",
  "id": "patient-001",
  "meta": {
    "versionId": "3"
  }
}

失敗Response

404 Not Found
Content-Type: application/fhir+json
{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "not-found"
    }
  ]
}

今日小結

今天認識了FHIR RESTful API中的read互動。

read的基本形式是:

GET [base]/[Resource Type]/[id]

如果要讀取Patient:

GET [base]/Patient/[id]

成功時通常會收到:

  • 200 OK
  • FHIR JSON Content-Type
  • 一筆Patient Resource
  • 版本及最後更新資訊

失敗時則可能收到401、403、404或410,以及OperationOutcome。

今天最重要的觀念是:

FHIR read需要的是Resource的邏輯id,不一定是醫院使用的病歷號。

另外,讀取Patient只會取得Patient Resource,不會自動取得病人的所有就醫、診斷、檢驗及用藥紀錄。

下一篇將介紹FHIR的Patient搜尋。當Client不知道Resource id,只有姓名、出生日期或病歷號時,就需要使用搜尋參數尋找資料。

明日預告

Day 18|如何搜尋FHIR Patient?

參考資料

  1. HL7 FHIR R4:RESTful API-Read
    https://hl7.org/fhir/R4/http.html#read

  2. HL7 FHIR R4:RESTful API-VRead
    https://hl7.org/fhir/R4/http.html#vread

  3. HL7 FHIR R4:Patient
    https://hl7.org/fhir/R4/patient.html

  4. HL7 FHIR R4:Resource
    https://hl7.org/fhir/R4/resource.html

  5. HL7 FHIR R4:OperationOutcome
    https://hl7.org/fhir/R4/operationoutcome.html

  6. HL7 FHIR R4:Security
    https://hl7.org/fhir/R4/security.html


上一篇
Day 16|用metadata認識一台FHIR Server
下一篇
Day 18|如何搜尋FHIR Patient?
系列文
《醫資生的 FHIR 30日入門:用 Postman 讀懂醫療資料交換》30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言