fhir-developer-skill

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

FHIR Developer Skill

FHIR开发者技能

Quick Reference

快速参考

HTTP Status Codes

HTTP状态码

CodeWhen 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
状态码适用场景
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
必填字段缺失、枚举值无效、违反业务规则

Required Fields by Resource (FHIR R4)

按资源划分的必填字段(FHIR R4)

ResourceRequired FieldsEverything Else
Patient(none)All optional
Observation
status
,
code
Optional
Encounter
status
,
class
Optional (including
subject
,
period
)
Condition
subject
Optional (including
code
,
clinicalStatus
)
MedicationRequest
status
,
intent
,
medication[x]
,
subject
Optional
Medication(none)All optional
Bundle
type
Optional

资源类型必填字段其他字段
Patient均为可选
Observation
status
,
code
可选
Encounter
status
,
class
可选(包括
subject
,
period
Condition
subject
可选(包括
code
,
clinicalStatus
MedicationRequest
status
,
intent
,
medication[x]
,
subject
可选
Medication均为可选
Bundle
type
可选

Required vs Optional Fields (CRITICAL)

必填字段与可选字段(重点)

Only validate fields with cardinality starting with "1" as required.
CardinalityRequired?
0..1
,
0..*
NO
1..1
,
1..*
YES
Common mistake: Making
subject
or
period
required on Encounter. They are 0..1 (optional).

仅验证基数以"1"开头的字段为必填项。
基数是否必填?
0..1
,
0..*
1..1
,
1..*
常见错误:将Encounter的
subject
period
设为必填项。它们的基数是0..1(可选)。

Value Sets (Enum Values)

值集(枚举值)

Invalid enum values must return
422 Unprocessable Entity
.
无效的枚举值必须返回
422 Unprocessable Entity

Patient.gender

Patient.gender

male | female | other | unknown
male | female | other | unknown

Observation.status

Observation.status

registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown
registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown

Encounter.status

Encounter.status

planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown
planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown

Encounter.class (Common Codes)

Encounter.class(常用编码)

CodeDisplayUse
AMB
ambulatoryOutpatient visits
IMP
inpatient encounterHospital admissions
EMER
emergencyEmergency department
VR
virtualTelehealth
编码显示名称使用场景
AMB
ambulatory门诊就诊
IMP
inpatient encounter住院收治
EMER
emergency急诊科室
VR
virtual远程医疗

Condition.clinicalStatus

Condition.clinicalStatus

active | recurrence | relapse | inactive | remission | resolved
active | recurrence | relapse | inactive | remission | resolved

Condition.verificationStatus

Condition.verificationStatus

unconfirmed | provisional | differential | confirmed | refuted | entered-in-error
unconfirmed | provisional | differential | confirmed | refuted | entered-in-error

MedicationRequest.status

MedicationRequest.status

active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown
active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown

MedicationRequest.intent

MedicationRequest.intent

proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option
proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option

Bundle.type

Bundle.type

document | message | transaction | transaction-response | batch | batch-response | history | searchset | collection

document | message | transaction | transaction-response | batch | batch-response | history | searchset | collection

Validation Pattern

验证模式

Python/FastAPI:
python
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:
typescript
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
):
python
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

Python/FastAPI:
python
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:
typescript
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
):
python
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

Coding Systems (URLs)

编码系统(URL)

SystemURL
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
系统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

Common LOINC Codes (Vital Signs)

常用LOINC编码(生命体征)

CodeDescription
8867-4
Heart rate
8480-6
Systolic blood pressure
8462-4
Diastolic blood pressure
8310-5
Body temperature
2708-6
Oxygen saturation (SpO2)

编码描述
8867-4
心率
8480-6
收缩压
8462-4
舒张压
8310-5
体温
2708-6
血氧饱和度(SpO2)

Data Type Patterns

数据类型模式

Coding (direct) vs CodeableConcept (wrapped)

Coding(直接使用)与CodeableConcept(包装使用)

Coding - Used by
Encounter.class
:
json
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
CodeableConcept - Used by
Observation.code
,
Condition.code
:
json
{"coding": [{"system": "http://loinc.org", "code": "8480-6"}], "text": "Systolic BP"}
Coding - 用于
Encounter.class
json
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
CodeableConcept - 用于
Observation.code
,
Condition.code
json
{"coding": [{"system": "http://loinc.org", "code": "8480-6"}], "text": "Systolic BP"}

Reference

Reference

json
{"reference": "Patient/123", "display": "John Smith"}
json
{"reference": "Patient/123", "display": "John Smith"}

Identifier

Identifier

json
{"system": "http://hospital.example.org/mrn", "value": "12345"}

json
{"system": "http://hospital.example.org/mrn", "value": "12345"}

Common Mistakes

常见错误

MistakeCorrect 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 mismatchUse
412 Precondition Failed
for If-Match failures
Returning 400 for invalid enum valuesUse
422 Unprocessable Entity
for validation errors
Forgetting Content-Type headerAlways set
Content-Type: application/fhir+json
Missing Location header on createReturn
Location: /Patient/{id}
with 201 Created

错误做法正确做法
将Encounter的
subject
period
设为必填项
两者基数均为0..1(可选)。仅
status
class
为必填项
Encounter.class
使用CodeableConcept
class
直接使用Coding:
{"system": "...", "code": "AMB"}
ETag不匹配时返回400If-Match失败时返回
412 Precondition Failed
无效枚举值返回400验证错误返回
422 Unprocessable Entity
忘记设置Content-Type请求头始终设置
Content-Type: application/fhir+json
创建操作时缺少Location请求头返回
Location: /Patient/{id}
并伴随201 Created状态码

Resource Structures

资源结构

For complete JSON examples of all resources, see references/resource-examples.md.
Quick reference for error responses:
json
{
  "resourceType": "OperationOutcome",
  "issue": [{"severity": "error", "code": "not-found", "diagnostics": "Patient/123 not found"}]
}

所有资源的完整JSON示例,请查看**references/resource-examples.md**。
错误响应快速参考:
json
{
  "resourceType": "OperationOutcome",
  "issue": [{"severity": "error", "code": "not-found", "diagnostics": "Patient/123 not found"}]
}

RESTful Endpoints

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

POST   /[ResourceType]              # 创建(返回201 + Location请求头)
GET    /[ResourceType]/[id]         # 读取
PUT    /[ResourceType]/[id]         # 更新
DELETE /[ResourceType]/[id]         # 删除(返回204)
GET    /[ResourceType]?param=value  # 搜索(返回Bundle)
GET    /metadata                    # CapabilityStatement
POST   /                            # Bundle事务/批量操作

Conditional Operations

条件操作

If-Match (optimistic locking):
  • Client sends:
    If-Match: W/"1"
  • Mismatch returns
    412 Precondition Failed
If-None-Exist (conditional create):
  • Client sends:
    If-None-Exist: identifier=http://mrn|12345
  • Match exists: return existing (200)
  • No match: create new (201)

If-Match(乐观锁):
  • 客户端发送:
    If-Match: W/"1"
  • 不匹配时返回
    412 Precondition Failed
If-None-Exist(条件创建):
  • 客户端发送:
    If-None-Exist: identifier=http://mrn|12345
  • 存在匹配项:返回现有资源(200)
  • 无匹配项:创建新资源(201)

Reference Files

参考文档

For detailed guidance, see:
  • Resource Examples: Complete JSON structures for Patient, Observation, Encounter, Condition, MedicationRequest, OperationOutcome, CapabilityStatement
  • SMART on FHIR Authorization: OAuth flows, scope syntax (v1/v2), backend services, scope enforcement
  • Pagination: Search result pagination,
    _count
    /
    _offset
    parameters, link relations
  • Bundle Operations: Transaction vs batch semantics, atomicity, processing order

如需详细指导,请查看:
  • 资源示例:Patient、Observation、Encounter、Condition、MedicationRequest、OperationOutcome、CapabilityStatement的完整JSON结构
  • SMART on FHIR授权:OAuth流程、权限范围语法(v1/v2)、后端服务、权限范围强制执行
  • 分页:搜索结果分页、
    _count
    /
    _offset
    参数、链接关系
  • Bundle操作:事务与批量操作语义、原子性、处理顺序

Implementation Checklist

实施检查清单

  1. Set
    Content-Type: application/fhir+json
    on all responses
  2. Return
    meta.versionId
    and
    meta.lastUpdated
    on resources
  3. Return
    Location
    header on create:
    /Patient/{id}
  4. Return
    ETag
    header:
    W/"{versionId}"
  5. Use OperationOutcome for all error responses
  6. Validate required fields → 422 for missing
  7. Validate enum values → 422 for invalid
  8. Search returns Bundle with
    type: "searchset"

  1. 所有响应设置
    Content-Type: application/fhir+json
  2. 资源返回
    meta.versionId
    meta.lastUpdated
  3. 创建操作返回Location请求头:
    /Patient/{id}
  4. 返回ETag请求头:
    W/"{versionId}"
  5. 所有错误响应使用OperationOutcome
  6. 验证必填字段 → 缺失时返回422
  7. 验证枚举值 → 无效时返回422
  8. 搜索操作返回
    type: "searchset"
    的Bundle

Quick Start Script

快速启动脚本

To scaffold a new FHIR API project with correct Pydantic v2 patterns:
bash
python scripts/setup_fhir_project.py my_fhir_api
Creates a FastAPI project with correct models, OperationOutcome helpers, and Patient CRUD endpoints.
使用正确的Pydantic v2模式搭建新的FHIR API项目:
bash
python scripts/setup_fhir_project.py my_fhir_api
创建一个FastAPI项目,包含正确的模型、OperationOutcome辅助工具和Patient CRUD端点。