在上一篇文章中,我們認識了FHIR Server的CapabilityStatement。
CapabilityStatement就像Server的能力說明書,可以告訴Client:
如果一台FHIR Server在Patient的interaction中宣告支援:
{
"code": "read"
}
就代表Client可以讀取一筆已知id的Patient Resource。
今天不進行實際API操作,而是透過Request與Response範例,認識FHIR的read互動,以及讀取Patient時需要注意的問題。
本文所有姓名、編號、網址及醫療資料均為虛構教學範例。
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格式回傳。
在FHIR RESTful API中,GET主要用於讀取或搜尋資料。
GET具有Safe Method的概念,表示Client提出GET Request時,不應要求Server修改或刪除目標Resource。
因此:
GET /Patient/patient-001
只是在取得Patient資料,不是建立、更新或刪除Patient。
Server可能記錄存取Log、更新快取或產生稽核紀錄,但這些系統行為不等於修改Client要求讀取的Patient內容。
以下面這個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
FHIR read需要知道兩項關鍵資訊:
例如:
Patient/patient-001
如果只知道病人姓名或病歷號,通常不能直接組成read URL,而要先使用FHIR搜尋功能找出符合條件的Patient。
可以整理成:
| 已知資料 | 適合的互動 |
|---|---|
| 已知Patient id | read |
| 只知道姓名 | search |
| 只知道病歷號 | search |
| 只知道出生日期 | search |
| 需要符合多項條件 | search |
read和search都可能使用GET,但URL及Response結構不同。
這是讀取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。
可以先查看以下欄位:
"resourceType": "Patient"
確認Resource類型為Patient。
"id": "patient-001"
確認回傳的id與Request URL中的id一致。
Request:
/Patient/patient-001
Response:
"id": "patient-001"
"identifier": [
{
"system": "https://hospital.example.org/mrn",
"value": "MRN0001"
}
]
查看病歷號等業務識別資料。
"name": [
{
"text": "王小明"
}
],
"birthDate": "2000-01-01"
這些欄位可以協助確認病人身分,但正式系統不能只依靠姓名判斷,因為可能有同名病人。
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 |
除了:
Content-Type: application/fhir+json
Server還可能回傳:
ETag: W/"3"
Last-Modified: Thu, 03 Sep 2026 08:30:00 GMT
ETag可以表示Resource的版本資訊。
ETag: W/"3"
可能對應:
"meta": {
"versionId": "3"
}
Last-Modified: Thu, 03 Sep 2026 08:30:00 GMT
表示Resource最後修改時間,可能對應:
"lastUpdated": "2026-09-03T08:30:00Z"
這些Header可以協助Client處理快取、版本及更新衝突。
Patient中可能包含:
"meta": {
"versionId": "3",
"lastUpdated": "2026-09-03T08:30:00Z"
}
versionId表示這筆Resource目前的版本識別碼。
如果Patient資料曾經更新,例如新增電話或修改地址,Server可能產生新版本。
要注意:
versionId不是Patient id。versionId不是病歷號。versionId也不一定是單純從1開始累加的數字。版本識別方式由FHIR Server管理,Client不應自行推測產生規則。
一般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。
如果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以前存在,但目前已被刪除,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 id」不代表具有讀取資料的合法權限。
不會。
執行:
GET /Patient/patient-001
通常只會回傳Patient Resource。
Patient可能包含:
它不會自動包含:
這些資料會存在於其他Resource中,並透過Reference指向Patient。
例如,Observation可能包含:
"subject": {
"reference": "Patient/patient-001"
}
如果要取得病人的Observation,需要另外搜尋相關Resource。
可能。
FHIR Patient中的許多欄位都是選填,Server也可能依照使用者權限只回傳部分內容。
例如:
{
"resourceType": "Patient",
"id": "patient-001",
"name": [
{
"text": "王小明"
}
]
}
這仍然可能是一筆符合FHIR基本結構的Patient,即使它沒有:
Resource是否符合實際業務需求,還要確認:
因此,read成功不代表所有希望取得的欄位一定存在。
醫療資料具有高度敏感性。
即使Client有權讀取Patient,系統也不一定需要回傳所有欄位。
例如,一個只負責顯示叫號資訊的系統,可能只需要:
它不一定需要病人的完整地址、電話或其他健康資訊。
這與資料最小化概念有關:
只提供完成特定目的所需要的資料,避免不必要的個人資料揭露。
FHIR定義資料交換結構,但哪些資料可以被誰讀取,仍需要透過權限、同意、組織政策及法規進行控制。
概念上可以依照以下順序理解:
Content-Type: application/fhir+json
確認Response Body是FHIR JSON。
成功時:
"resourceType": "Patient"
錯誤時可能是:
"resourceType": "OperationOutcome"
"id": "patient-001"
確認與URL中的Resource id相符。
確認版本及最後更新時間。
例如identifier、name、gender及birthDate。
這是一種閱讀Response的思考順序,並非實際操作步驟。
GET https://hospital.example.org/fhir/Patient/patient-001
Accept: application/fhir+json
Server可能依序確認:
200 OK
Content-Type: application/fhir+json
ETag: W/"3"
{
"resourceType": "Patient",
"id": "patient-001",
"meta": {
"versionId": "3"
}
}
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]
成功時通常會收到:
失敗時則可能收到401、403、404或410,以及OperationOutcome。
今天最重要的觀念是:
FHIR read需要的是Resource的邏輯id,不一定是醫院使用的病歷號。
另外,讀取Patient只會取得Patient Resource,不會自動取得病人的所有就醫、診斷、檢驗及用藥紀錄。
下一篇將介紹FHIR的Patient搜尋。當Client不知道Resource id,只有姓名、出生日期或病歷號時,就需要使用搜尋參數尋找資料。
Day 18|如何搜尋FHIR Patient?
HL7 FHIR R4:RESTful API-Read
https://hl7.org/fhir/R4/http.html#read
HL7 FHIR R4:RESTful API-VRead
https://hl7.org/fhir/R4/http.html#vread
HL7 FHIR R4:Patient
https://hl7.org/fhir/R4/patient.html
HL7 FHIR R4:Resource
https://hl7.org/fhir/R4/resource.html
HL7 FHIR R4:OperationOutcome
https://hl7.org/fhir/R4/operationoutcome.html
HL7 FHIR R4:Security
https://hl7.org/fhir/R4/security.html