关于 fhir-developer-skill
fhir-developer-skill 为实现 FHIR R4 API 的开发者提供参考规范和代码模式,涵盖资源校验、REST 操作及常见响应约定。还包含 FastAPI 项目脚手架脚本。
主要功能
- 01FHIR R4 必填字段与枚举参考
- 02REST 状态码和响应规范
- 03Coding 与 CodeableConcept 示例
- 04FastAPI 项目脚手架脚本
适用场景
- 01检查资源字段与枚举校验
- 02实现 FHIR 错误响应
- 03搭建 FastAPI FHIR 项目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ anthropics/ healthcare --skill fhir-developer-skill安装后在 Claude Code 中输入/fhir-developer-skill确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/bundles.mdpagination.mdresource-examples.mdsmart-auth.mdscripts/setup_fhir_project.py详细介绍
FHIR 开发者技能
快速参考
HTTP 状态码
| 代码 | 使用场景 |
|---|---|
200 OK | 读取、更新或搜索成功 |
201 Created | 创建成功(包含 Location 标头) |
204 No Content | 删除成功 |
400 Bad Request | JSON 格式错误,或 resourceType 错误 |
401 Unauthorized | 缺少、过期、已撤销或格式错误的令牌(RFC 6750) |
403 Forbidden | 令牌有效,但权限范围不足 |
404 Not Found | 资源不存在 |
412 Precondition Failed | If-Match ETag 不匹配(不是 400!) |
422 Unprocessable Entity | 缺少必填字段、枚举值无效或违反业务规则 |
各资源的必填字段(FHIR R4)
| 资源 | 必填字段 | 其他所有字段 |
|---|---|---|
| Patient | (无) | 全部可选 |
| Observation | status、code | 可选 |
| Encounter | status、class | 可选(包括 subject、period) |
| Condition | subject | 可选(包括 code、clinicalStatus) |
| MedicationRequest | status、intent、medication[x]、subject | 可选 |
| Medication | (无) | 全部可选 |
| Bundle | type | 可选 |
必填字段与可选字段(重要)
仅将基数以“1”开头的字段视为必填。
| 基数 | 是否必填? |
|---|---|
0..1、0..* | 否 |
1..1、1..* | 是 |
常见错误:将 subject 或 period 设为 Encounter 的必填字段。它们的基数是 0..1(可选)。
值集(枚举值)
枚举值无效时,必须返回 422 Unprocessable Entity。
Patient.gender
male | female | other | unknown
Observation.status
registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown
Encounter.status
planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown
Encounter.class(常见代码)
| 代码 | 显示名称 | 用途 |
|---|---|---|
AMB | 门诊 | 门诊就诊 |
IMP | 住院就诊 | 住院治疗 |
EMER | 急诊 | 急诊科 |
VR | 虚拟 | 远程医疗 |
Condition.clinicalStatus
active | recurrence | relapse | inactive | remission | resolved
Condition.verificationStatus
unconfirmed | provisional | differential | confirmed | refuted | entered-in-error
MedicationRequest.status
active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown
MedicationRequest.intent
proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option
Bundle.type
document | message | transaction | transaction-response | batch | batch-response | history | searchset | collection
验证模式
Python/FastAPI:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
def operation_outcome(severity: str, code: str, diagnostics: str):
return {
"resourceType": "OperationOutcome",
"issue": [{"severity": severity, "code": code, "diagnostics": diagnostics}]
}
VALID_OBS_STATUS = {"registered", "preliminary", "final", "amended",
"corrected", "cancelled", "entered-in-error", "unknown"}
@app.post("/Observation", status_code=201)
async def create_observation(data: dict):
if not data.get("status"):
return JSONResponse(status_code=422, content=operation_outcome(
"error", "required", "Observation.status is required"
), media_type="application/fhir+json")
if data["status"] not in VALID_OBS_STATUS:
return JSONResponse(status_code=422, content=operation_outcome(
"error", "value", f"Invalid status '{data['status']}'"
), media_type="application/fhir+json")
# ... create resource
TypeScript/Express:
const VALID_OBS_STATUS = new Set(['registered', 'preliminary', 'final', 'amended',
'corrected', 'cancelled', 'entered-in-error', 'unknown']);
app.post('/Observation', (req, res) => {
if (!req.body.status) {
return res.status(422).contentType('application/fhir+json')
.json(operationOutcome('error', 'required', 'Observation.status is required'));
}
if (!VALID_OBS_STATUS.has(req.body.status)) {
return res.status(422).contentType('application/fhir+json')
.json(operationOutcome('error', 'value', `Invalid status '${req.body.status}'`));
}
// ... create resource
});
Pydantic v2 模型(使用 Literal,不要使用 const=True):
from typing import Literal
from pydantic import BaseModel
class Patient(BaseModel):
resourceType: Literal["Patient"] = "Patient"
id: str | None = None
gender: Literal["male", "female", "other", "unknown"] | None = None
编码系统(URL)
| 系统 | URL |
|---|---|
| LOINC | http://loinc.org |
| SNOMED CT | http://snomed.info/sct |
| RxNorm | http://www.nlm.nih.gov/research/umls/rxnorm |
| ICD-10 | http://hl7.org/fhir/sid/icd-10 |
| v3-ActCode | http://terminology.hl7.org/CodeSystem/v3-ActCode |
| Observation Category | http://terminology.hl7.org/CodeSystem/observation-category |
| Condition Clinical | http://terminology.hl7.org/CodeSystem/condition-clinical |
| Condition Ver Status | http://terminology.hl7.org/CodeSystem/condition-ver-status |
常见 LOINC 代码(生命体征)
| 代码 | 描述 |
|---|---|
8867-4 | 心率 |
8480-6 | 收缩压 |
8462-4 | 舒张压 |
8310-5 | 体温 |
2708-6 | 血氧饱和度(SpO2) |
数据类型模式
Coding(直接)与 CodeableConcept(包装)
Coding - 用于 Encounter.class:
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
CodeableConcept - 用于 Observation.code、Condition.code:
{"coding": [{"system": "http://loinc.org", "code": "8480-6"}], "text": "Systolic BP"}
参考
{"reference": "Patient/123", "display": "John Smith"}
标识符
{"system": "http://hospital.example.org/mrn", "value": "12345"}
常见错误
| 错误 | 正确做法 |
|---|---|
将 Encounter 上的 subject 或 period 设为必填 | 两者均为 0..1(可选)。只有 status 和 class 是必填项 |
将 CodeableConcept 用于 Encounter.class | class 直接使用 Coding:{"system": "...", "code": "AMB"} |
| ETag 不匹配时返回 400 | 对 If-Match 失败使用 412 Precondition Failed |
| 枚举值无效时返回 400 | 对验证错误使用 422 Unprocessable Entity |
| 忘记 Content-Type 标头 | 始终设置 Content-Type: application/fhir+json |
| 创建时缺少 Location 标头 | 返回 Location: /Patient/{id},状态为 201 Created |
资源结构
如需所有资源的完整 JSON 示例,请参阅 references/resource-examples.md。
错误响应速查:
{
"resourceType": "OperationOutcome",
"issue": [{"severity": "error", "code": "not-found", "diagnostics": "Patient/123 not found"}]
}
RESTful 端点
POST /[ResourceType] # Create (returns 201 + Location header)
GET /[ResourceType]/[id] # Read
PUT /[ResourceType]/[id] # Update
DELETE /[ResourceType]/[id] # Delete (returns 204)
GET /[ResourceType]?param=value # Search (returns Bundle)
GET /metadata # CapabilityStatement
POST / # Bundle transaction/batch
条件操作
If-Match(乐观锁):
- 客户端发送:
If-Match: W/"1" - 不匹配时返回
412 Precondition Failed
If-None-Exist(条件创建):
- 客户端发送:
If-None-Exist: identifier=http://mrn|12345 - 存在匹配项:返回现有资源(200)
- 无匹配项:创建新资源(201)
参考文件
如需详细指南,请参阅:
- 资源示例:Patient、Observation、Encounter、Condition、MedicationRequest、OperationOutcome、CapabilityStatement 的完整 JSON 结构
- SMART on FHIR 授权:OAuth 流程、作用域语法(v1/v2)、后端服务、作用域强制执行
- 分页:搜索结果分页、
_count/_offset参数、链接关系 - Bundle 操作:事务与批处理语义、原子性、处理顺序
实现清单
- 在所有响应中设置
Content-Type: application/fhir+json - 在资源中返回
meta.versionId和meta.lastUpdated - 创建时返回
Location标头:/Patient/{id} - 返回
ETag标头:W/"{versionId}" - 对所有错误响应使用 OperationOutcome
- 验证必填字段 → 缺失时返回 422
- 验证枚举值 → 无效时返回 422
- 搜索返回包含
type: "searchset"的 Bundle
快速启动脚本
如需使用正确的 Pydantic v2 模式搭建新的 FHIR API 项目:
python scripts/setup_fhir_project.py my_fhir_api
创建一个 FastAPI 项目,其中包含正确的模型、OperationOutcome 辅助函数和 Patient CRUD 端点。