fhir-developer-skill by anthropics/healthcare
npx skills add https://github.com/anthropics/healthcare --skill fhir-developer-skill| 代码 | 使用场景 |
|---|---|
200 OK | 读取、更新或搜索成功 |
201 Created | 创建成功(需包含 Location 头部) |
204 No Content | 删除成功 |
400 Bad Request | JSON 格式错误,错误的 resourceType |
401 Unauthorized |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 令牌缺失、过期、撤销或格式错误 (RFC 6750) |
403 Forbidden | 令牌有效但权限不足 |
404 Not Found | 资源不存在 |
412 Precondition Failed | If-Match ETag 不匹配 (不是 400!) |
422 Unprocessable Entity | 缺少必填字段,枚举值无效,违反业务规则 |
| 资源 | 必填字段 | 其他字段 |
|---|---|---|
| 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..* | 是 |
常见错误:将 Encounter 的 subject 或 period 设为必填。它们是 0..1(可选)。
无效的枚举值必须返回 422 Unprocessable Entity。
male | female | other | unknown
registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown
planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown
| 代码 | 显示名称 | 用途 |
|---|---|---|
AMB | ambulatory | 门诊就诊 |
IMP | inpatient encounter | 住院 |
EMER | emergency | 急诊科 |
VR | virtual | 远程医疗 |
active | recurrence | relapse | inactive | remission | resolved
unconfirmed | provisional | differential | confirmed | refuted | entered-in-error
active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown
proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option
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 |
|---|---|
| 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 |
| 代码 | 描述 |
|---|---|
8867-4 | 心率 |
8480-6 | 收缩压 |
8462-4 | 舒张压 |
8310-5 | 体温 |
2708-6 | 血氧饱和度 (SpO2) |
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 是必填的 |
对 Encounter.class 使用 CodeableConcept | 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"}]
}
POST /[ResourceType] # 创建 (返回 201 + Location 头部)
GET /[ResourceType]/[id] # 读取
PUT /[ResourceType]/[id] # 更新
DELETE /[ResourceType]/[id] # 删除 (返回 204)
GET /[ResourceType]?param=value # 搜索 (返回 Bundle)
GET /metadata # CapabilityStatement
POST / # Bundle transaction/batch
If-Match (乐观锁):
If-Match: W/"1"412 Precondition FailedIf-None-Exist (条件创建):
If-None-Exist: identifier=http://mrn|12345详细指南请参阅:
_count/_offset 参数、链接关系Content-Type: application/fhir+jsonmeta.versionId 和 meta.lastUpdatedLocation 头部: /Patient/{id}ETag 头部: W/"{versionId}"type: "searchset" 的 Bundle要使用正确的 Pydantic v2 模式搭建一个新的 FHIR API 项目:
python scripts/setup_fhir_project.py my_fhir_api
创建一个包含正确模型、OperationOutcome 助手和 Patient CRUD 端点的 FastAPI 项目。
每周安装次数
100
代码库
GitHub 星标数
117
首次出现
2026年1月23日
安全审计
安装于
codex77
opencode76
gemini-cli74
claude-code69
github-copilot67
cursor60
| Code | When to Use |
|---|---|
200 OK | Successful read, update, or search |
201 Created | Successful create (include Location header) |
204 No Content | Successful delete |
400 Bad Request | Malformed JSON, wrong resourceType |
401 Unauthorized | Missing, expired, revoked, or malformed token (RFC 6750) |
403 Forbidden | Valid token but insufficient scopes |
404 Not Found | Resource doesn't exist |
412 Precondition Failed | If-Match ETag mismatch (NOT 400!) |
422 Unprocessable Entity | Missing required fields, invalid enum values, business rule violations |
| Resource | Required Fields | Everything Else |
|---|---|---|
| Patient | (none) | All optional |
| Observation | status, code | Optional |
| Encounter | status, class | Optional (including subject, period) |
| Condition | subject |
Only validate fields with cardinality starting with "1" as required.
| Cardinality | Required? |
|---|---|
0..1, 0..* | NO |
1..1, 1..* | YES |
Common mistake : Making subject or period required on Encounter. They are 0..1 (optional).
Invalid enum values must return 422 Unprocessable Entity.
male | female | other | unknown
registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown
planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown
| Code | Display | Use |
|---|---|---|
AMB | ambulatory | Outpatient visits |
IMP | inpatient encounter | Hospital admissions |
EMER | emergency | Emergency department |
VR | virtual | Telehealth |
active | recurrence | relapse | inactive | remission | resolved
unconfirmed | provisional | differential | confirmed | refuted | entered-in-error
active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown
proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option
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 Models (use Literal, not 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
| System | 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 |
| Code | Description |
|---|---|
8867-4 | Heart rate |
8480-6 | Systolic blood pressure |
8462-4 | Diastolic blood pressure |
8310-5 | Body temperature |
2708-6 | Oxygen saturation (SpO2) |
Coding - Used by Encounter.class:
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
CodeableConcept - Used by 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"}
| Mistake | Correct Approach |
|---|---|
Making subject or period required on Encounter | Both are 0..1 (optional). Only status and class are required |
Using CodeableConcept for Encounter.class | class uses Coding directly: {"system": "...", "code": "AMB"} |
| Returning 400 for ETag mismatch | Use 412 Precondition Failed for If-Match failures |
For complete JSON examples of all resources, see references/resource-examples.md.
Quick reference for error responses:
{
"resourceType": "OperationOutcome",
"issue": [{"severity": "error", "code": "not-found", "diagnostics": "Patient/123 not found"}]
}
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 (optimistic locking):
If-Match: W/"1"412 Precondition FailedIf-None-Exist (conditional create):
If-None-Exist: identifier=http://mrn|12345For detailed guidance, see:
_count/_offset parameters, link relationsContent-Type: application/fhir+json on all responsesmeta.versionId and meta.lastUpdated on resourcesLocation header on create: /Patient/{id}ETag header: W/"{versionId}"type: "searchset"To scaffold a new FHIR API project with correct Pydantic v2 patterns:
python scripts/setup_fhir_project.py my_fhir_api
Creates a FastAPI project with correct models, OperationOutcome helpers, and Patient CRUD endpoints.
Weekly Installs
100
Repository
GitHub Stars
117
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex77
opencode76
gemini-cli74
claude-code69
github-copilot67
cursor60
飞书OpenAPI Explorer:探索和调用未封装的飞书原生API接口
37,900 周安装
Optional (including code, clinicalStatus) |
| MedicationRequest | status, intent, medication[x], subject | Optional |
| Medication | (none) | All optional |
| Bundle | type | Optional |
| Condition Clinical | http://terminology.hl7.org/CodeSystem/condition-clinical |
| Condition Ver Status | http://terminology.hl7.org/CodeSystem/condition-ver-status |
| Returning 400 for invalid enum values | Use 422 Unprocessable Entity for validation errors |
| Forgetting Content-Type header | Always set Content-Type: application/fhir+json |
| Missing Location header on create | Return Location: /Patient/{id} with 201 Created |