TL;DR
HL7 FHIR(Fast Healthcare Interoperability Resources)は、RESTful APIとJSON/XMLレスポンスを活用した医療データ交換の最新標準です。患者情報や観察結果、薬剤など標準化されたリソースを提供し、OAuth 2.0認証とアプリ統合用SMART on FHIRを組み込みます。本ガイドは、FHIRのアーキテクチャ、リソースタイプ、検索パラメーター、認証、本番導入戦略を実装観点で解説します。
はじめに
医療データの断片化により、米国医療システムは毎年300億ドル以上のコストを強いられています。開発者にとって、HL7 FHIR APIの統合はCMSで義務化され、EpicやCernerなど主要EHRベンダーも標準で採用しています。
実際、FHIR対応アプリによる医療現場では、ケア連携時間が40%短縮され、FAXによる記録要求が85%削減されています。強固なFHIR API連携は、EHR、患者ポータル、ケア連携プラットフォーム間でのデータ交換をシームレスにします。
このガイドは、HL7 FHIR APIの統合プロセスを、実装手順・コード例を交えて網羅します。FHIRのアーキテクチャ、リソースタイプ、検索パラメーター、OAuth 2.0認証、SMART on FHIR連携、本番環境デプロイまで、実践的に解説します。本番運用可能なFHIR統合を構築しましょう。
💡 Apidogは医療API統合を効率化
FHIRエンドポイントのテスト、スキーマ検証、認証フローのデバッグ、API仕様のドキュメント化を1つのワークスペースで。FHIR実装ガイドのインポート、モックレスポンス作成、テストシナリオ共有も可能。
HL7 FHIRとは?
FHIR (Fast Healthcare Interoperability Resources) は、Health Level Seven International (HL7) によって策定された、電子的な医療情報交換の標準フレームワークです。RESTful API、JSON/XML、OAuth 2.0など最新Web技術を活用できます。
FHIRリソースタイプ
FHIRは140以上のリソースタイプを持ち、以下が主要コアリソースです。
| リソース | 目的 | 一般的なユースケース |
|---|---|---|
| Patient | デモグラフィック情報 | 患者検索、登録 |
| Practitioner | 医療従事者情報 | ディレクトリ、スケジューリング |
| Encounter | 受診/入院 | ケアエピソード、請求 |
| Observation | 臨床データ | バイタル、検査結果、評価 |
| Condition | 問題/診断 | 問題リスト、ケアプランニング |
| MedicationRequest | 処方箋 | e-処方、薬剤履歴 |
| AllergyIntolerance | アレルギー | 安全確認、アラート |
| Immunization | 予防接種 | 予防接種記録 |
| DiagnosticReport | 検査/画像診断レポート | 結果配信 |
| DocumentReference | 臨床文書 | CCD、退院サマリー |
FHIR APIアーキテクチャ
FHIRはRESTful APIパターンでリソースを操作します。
https://fhir-server.com/fhir/{resourceType}/{id}
FHIRバージョン比較
| バージョン | ステータス | ユースケース |
|---|---|---|
| R4 (4.0.1) | 現在のSTU | 本番環境での実装 |
| R4B (4.3) | 試用実装 | 早期導入者 |
| R5 (5.0.0) | ドラフトSTU | 将来の実装 |
| DSTU2 | 非推奨 | レガシーシステムのみ |
CMSは認定EHRに対し、患者・医療提供者アクセスAPIでFHIR R4対応を必須としています。
開始する:FHIRサーバーへのアクセス
ステップ1:FHIRサーバーの選択
以下のいずれかのサーバーを用途・コスト・運用形態に応じて選択します。
| サーバー | タイプ | コスト | 最適な用途 |
|---|---|---|---|
| Azure API for FHIR | マネージド | 従量課金制 | エンタープライズ、Azureユーザー |
| AWS HealthLake | マネージド | 従量課金制 | AWS環境 |
| Google Cloud Healthcare API | マネージド | 従量課金制 | GCP環境 |
| HAPI FHIR | オープンソース | セルフホスト | カスタムデプロイ |
| Epic FHIR Server | 商用 | Epic顧客 | Epic EHRとの統合 |
| Cerner Ignite FHIR | 商用 | Cerner顧客 | Cerner EHRとの統合 |
ステップ2:サーバー認証情報の取得
クラウドFHIRサービスの場合の認証情報取得例:
# Azure API for FHIR
# 1. Azure PortalでFHIRサービスを作成
# 2. 認証を構成 (OAuth 2.0 または AAD)
# 3. FHIRエンドポイントを取得: https://{service-name}.azurehealthcareapis.com
# 4. OAuth用のクライアントアプリを登録
# AWS HealthLake
# 1. AWSコンソールでデータストアを作成
# 2. IAMロールを構成
# 3. エンドポイントを取得: https://healthlake.{region}.amazonaws.com
ステップ3:FHIR RESTful操作の理解
FHIRは標準HTTPメソッドでリソースを操作します。
| 操作 | HTTPメソッド | エンドポイント | 説明 |
|---|---|---|---|
| Read | GET | /{resourceType}/{id} |
特定のリソースを取得 |
| Search | GET | /{resourceType}?param=value |
リソースを検索 |
| Create | POST | /{resourceType} |
新しいリソースを作成 |
| Update | PUT | /{resourceType}/{id} |
リソースを置き換え |
| Patch | PATCH | /{resourceType}/{id} |
部分的な更新 |
| Delete | DELETE | /{resourceType}/{id} |
リソースを削除 |
| History | GET | /{resourceType}/{id}/_history |
リソースのバージョン履歴 |
ステップ4:最初のFHIR呼び出しを行う
エンドポイントが正しく動作するか、最初のリクエストで確認します。
curl -X GET "https://fhir-server.com/fhir/metadata" \
-H "Accept: application/fhir+json" \
-H "Authorization: Bearer {token}"
期待されるレスポンス例:
{
"resourceType": "CapabilityStatement",
"status": "active",
"date": "2026-03-25",
"fhirVersion": "4.0.1",
"rest": [{
"mode": "server",
"resource": [
{ "type": "Patient" },
{ "type": "Observation" },
{ "type": "Condition" }
]
}]
}
コアFHIR操作
患者リソースの読み取り
患者IDでリソースを取得する実装例:
const FHIR_BASE_URL = process.env.FHIR_BASE_URL;
const FHIR_TOKEN = process.env.FHIR_TOKEN;
const fhirRequest = async (endpoint, options = {}) => {
const response = await fetch(`${FHIR_BASE_URL}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${FHIR_TOKEN}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text || response.statusText}`);
}
return response.json();
};
const getPatient = async (patientId) => {
const patient = await fhirRequest(`/Patient/${patientId}`);
return patient;
};
const patient = await getPatient('12345');
console.log(`Patient: ${patient.name[0].given[0]} ${patient.name[0].family}`);
console.log(`DOB: ${patient.birthDate}`);
console.log(`Gender: ${patient.gender}`);
患者リソースの構造
{
"resourceType": "Patient",
"id": "12345",
"identifier": [
{
"use": "usual",
"type": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR"
}]
},
"system": "http://hospital.example.org",
"value": "MRN123456"
}
],
"name": [
{
"use": "official",
"family": "Smith",
"given": ["John", "Michael"]
}
],
"telecom": [
{
"system": "phone",
"value": "555-123-4567",
"use": "home"
},
{
"system": "email",
"value": "john.smith@email.com"
}
],
"gender": "male",
"birthDate": "1985-06-15",
"address": [
{
"use": "home",
"line": ["123 Main Street"],
"city": "Springfield",
"state": "IL",
"postalCode": "62701"
}
]
}
リソースの検索
患者名・生年月日などで検索するコード例:
const searchPatients = async (searchParams) => {
const query = new URLSearchParams();
if (searchParams.name) query.append('name', searchParams.name);
if (searchParams.birthDate) query.append('birthdate', searchParams.birthDate);
if (searchParams.identifier) query.append('identifier', searchParams.identifier);
if (searchParams.gender) query.append('gender', searchParams.gender);
const response = await fhirRequest(`/Patient?${query.toString()}`);
return response;
};
const results = await searchPatients({ name: 'Smith', birthDate: '1985-06-15' });
console.log(`Found ${results.total} patients`);
results.entry.forEach(entry => {
const patient = entry.resource;
console.log(`${patient.name[0].family}, ${patient.name[0].given[0]}`);
});
共通の検索パラメーター
| リソース | 検索パラメーター | 例 |
|---|---|---|
| Patient | name, birthdate, identifier, gender, phone, email | ?name=Smith&birthdate=1985-06-15 |
| Observation | patient, code, date, category, status | ?patient=123&code=8480-6&date=ge2026-01-01 |
| Condition | patient, clinical-status, category, onset-date | ?patient=123&clinical-status=active |
| MedicationRequest | patient, status, intent, medication | ?patient=123&status=active |
| Encounter | patient, date, status, class | ?patient=123&date=ge2026-01-01 |
| DiagnosticReport | patient, category, date, status | ?patient=123&category=laboratory |
検索修飾子
| 修飾子 | 説明 | 例 |
|---|---|---|
:exact |
完全一致 | name:exact=Smith |
:contains |
部分一致 | name:contains=smi |
:missing |
値の有無 | phone:missing=true |
: (プレフィックス) |
プレフィックス演算子 | birthdate=ge1980-01-01 |
日付・数値の検索プレフィックス
| プレフィックス | 意味 | 例 |
|---|---|---|
eq |
等しい | birthdate=eq1985-06-15 |
ne |
等しくない | birthdate=ne1985-06-15 |
gt |
より大きい | birthdate=gt1980-01-01 |
lt |
より小さい | birthdate=lt1990-01-01 |
ge |
以上 | birthdate=ge1980-01-01 |
le |
以下 | birthdate=le1990-01-01 |
sa |
より後 | date=sa2026-01-01 |
eb |
より前 | date=eb2026-12-31 |
臨床データの操作
Observation(バイタルサイン)の作成
バイタル値(例:血圧)を記録する例:
const createObservation = async (observationData) => {
const observation = {
resourceType: 'Observation',
status: 'final',
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/observation-category',
code: 'vital-signs'
}]
}
],
code: {
coding: [{
system: 'http://loinc.org',
code: observationData.code,
display: observationData.display
}]
},
subject: {
reference: `Patient/${observationData.patientId}`
},
effectiveDateTime: observationData.effectiveDate || new Date().toISOString(),
valueQuantity: {
value: observationData.value,
unit: observationData.unit,
system: 'http://unitsofmeasure.org',
code: observationData.ucumCode
},
performer: [
{
reference: `Practitioner/${observationData.performerId}`
}
]
};
const response = await fhirRequest('/Observation', {
method: 'POST',
body: JSON.stringify(observation)
});
return response;
};
// 使用例
const systolicBP = await createObservation({
patientId: '12345',
code: '8480-6',
display: 'Systolic blood pressure',
value: 120,
unit: 'mmHg',
ucumCode: 'mm[Hg]',
performerId: '67890'
});
console.log(`Observation created: ${systolicBP.id}`);
一般的なLOINCコード
| コード | 表示 | カテゴリ |
|---|---|---|
| 8480-6 | 収縮期血圧 | バイタルサイン |
| 8462-4 | 拡張期血圧 | バイタルサイン |
| 8867-4 | 心拍数 | バイタルサイン |
| 8310-5 | 体温 | バイタルサイン |
| 8302-2 | 身長 | バイタルサイン |
| 29463-7 | 体重 | バイタルサイン |
| 8871-5 | 呼吸数 | バイタルサイン |
| 2339-0 | 血糖 [質量/容積] | 検査 |
| 4548-4 | ヘモグロビンA1c | 検査 |
| 2093-3 | コレステロール [質量/容積] | 検査 |
Condition(問題リスト項目)の作成
患者の問題リストに診断を追加:
const createCondition = async (conditionData) => {
const condition = {
resourceType: 'Condition',
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: conditionData.status || 'active'
}]
},
verificationStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
code: 'confirmed'
}]
},
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-category',
code: conditionData.category || 'problem-list-item'
}]
}
],
code: {
coding: [{
system: 'http://snomed.info/sct',
code: conditionData.sctCode,
display: conditionData.display
}]
},
subject: {
reference: `Patient/${conditionData.patientId}`
},
onsetDateTime: conditionData.onsetDate,
recordedDate: new Date().toISOString()
};
const response = await fhirRequest('/Condition', {
method: 'POST',
body: JSON.stringify(condition)
});
return response;
};
// 使用例
const diabetes = await createCondition({
patientId: '12345',
sctCode: '44054006',
display: 'Type 2 Diabetes Mellitus',
status: 'active',
category: 'problem-list-item',
onsetDate: '2024-01-15'
});
一般的なSNOMED CTコード
| コード | 表示 | カテゴリ |
|---|---|---|
| 44054006 | 2型糖尿病 | 問題 |
| 38341003 | 高血圧 | 問題 |
| 195967001 | 喘息 | 問題 |
| 13645005 | 慢性閉塞性肺疾患 | 問題 |
| 35489007 | うつ病性障害 | 問題 |
| 22298006 | 心筋梗塞 | 問題 |
| 26929004 | アルツハイマー病 | 問題 |
| 396275006 | 変形性関節症 | 問題 |
患者の薬剤情報の取得
アクティブな薬剤依頼を取得:
const getPatientMedications = async (patientId) => {
const response = await fhirRequest(
`/MedicationRequest?patient=${patientId}&status=active`
);
return response;
};
const medications = await getPatientMedications('12345');
medications.entry?.forEach(entry => {
const med = entry.resource;
console.log(`${med.medicationCodeableConcept.coding[0].display}`);
console.log(` Dose: ${med.dosageInstruction[0]?.text}`);
console.log(` Status: ${med.status}`);
});
検査結果の取得
検査レポートや観察値を取得する例:
const getPatientLabResults = async (patientId, options = {}) => {
const params = new URLSearchParams({
patient: patientId,
category: options.category || 'laboratory'
});
if (options.dateFrom) {
params.append('date', `ge${options.dateFrom}`);
}
const response = await fhirRequest(`/DiagnosticReport?${params.toString()}`);
return response;
};
const getLabValue = async (patientId, loincCode) => {
const params = new URLSearchParams({
patient: patientId,
code: loincCode
});
const response = await fhirRequest(`/Observation?${params.toString()}`);
return response;
};
const hba1c = await getLabValue('12345', '4548-4');
hba1c.entry?.forEach(entry => {
const obs = entry.resource;
console.log(`HbA1c: ${obs.valueQuantity.value} ${obs.valueQuantity.unit}`);
console.log(`Date: ${obs.effectiveDateTime}`);
});
OAuth 2.0とSMART on FHIR
FHIR認証の理解
FHIRサーバーはOAuth 2.0(OpenID Connect付き)による認証を採用します。
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ クライアント │───▶│ 認証 │───▶│ FHIR │
│ (アプリ) │ │ サーバー │ │ サーバー │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 1. 認証リクエスト │ │
│───────────────────▶│ │
│ │ │
│ 2. ユーザーログイン │ │
│◀───────────────────│ │
│ │ │
│ 3. 認証コード │ │
│───────────────────▶│ │
│ │ │
│ 4. トークンリクエスト │ │
│───────────────────▶│ │
│ │ 5. トークン + ID │
│◀───────────────────│ │
│ │ │
│ 6. APIリクエスト │ │
│────────────────────────────────────────▶│
│ │ │
│ 7. FHIRデータ │ │
│◀────────────────────────────────────────│
SMART on FHIRアプリの起動
実装例(PKCE対応):
const crypto = require('crypto');
class SMARTClient {
constructor(config) {
this.clientId = config.clientId;
this.redirectUri = config.redirectUri;
this.issuer = config.issuer;
this.scopes = config.scopes;
}
generatePKCE() {
const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
return { codeVerifier, codeChallenge };
}
buildAuthUrl(state, patientId = null) {
const { codeVerifier, codeChallenge } = this.generatePKCE();
this.codeVerifier = codeVerifier;
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: this.scopes.join(' '),
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
aud: this.issuer,
launch: patientId ? `patient-${patientId}` : null
});
return `${this.issuer}/authorize?${params.toString()}`;
}
async exchangeCodeForToken(code) {
const response = await fetch(`${this.issuer}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: this.redirectUri,
client_id: this.clientId,
code_verifier: this.codeVerifier
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
patientId: data.patient,
encounterId: data.encounter
};
}
}
// 使用例
const smartClient = new SMARTClient({
clientId: 'my-app-client-id',
redirectUri: 'https://myapp.com/callback',
issuer: 'https://fhir.epic.com',
scopes: [
'openid',
'profile',
'patient/Patient.read',
'patient/Observation.read',
'patient/Condition.read',
'patient/MedicationRequest.read',
'offline_access'
]
});
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`Redirect to: ${authUrl}`);
必須SMARTスコープ
| スコープ | パーミッション | ユースケース |
|---|---|---|
openid |
OIDC認証 | すべてのアプリに必須 |
profile |
ユーザープロファイル情報 | 医療従事者ディレクトリ |
patient/Patient.read |
患者デモグラフィック情報の読み取り | 患者検索 |
patient/Observation.read |
観察結果の読み取り | バイタル、検査 |
patient/Condition.read |
状態の読み取り | 問題リスト |
patient/MedicationRequest.read |
薬剤情報の読み取り | 薬剤履歴 |
patient/*.read |
すべての患者リソースの読み取り | 患者データの全体像 |
user/*.read |
アクセス可能なすべてのリソースの読み取り | 医療従事者ビュー |
offline_access |
リフレッシュトークン | 長時間セッション |
認証済みFHIRリクエストの作成
class FHIRClient {
constructor(accessToken, fhirBaseUrl) {
this.accessToken = accessToken;
this.baseUrl = fhirBaseUrl;
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${this.accessToken}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text}`);
}
return response.json();
}
async getPatient(patientId) {
return this.request(`/Patient/${patientId}`);
}
async searchPatients(params) {
const query = new URLSearchParams(params);
return this.request(`/Patient?${query.toString()}`);
}
}
// OAuthコールバック後の使用例
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);
バッチおよびトランザクション操作
バッチリクエスト
複数の独立リクエストを一括で実行:
const batchRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'batch',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
const bundle = await batchRequest([
{ method: 'GET', url: 'Patient/12345' },
{ method: 'GET', url: 'Patient/12345/Observation?category=vital-signs' },
{ method: 'GET', url: 'Patient/12345/Condition?clinical-status=active' }
]);
bundle.entry.forEach((entry, index) => {
console.log(`Response ${index}: ${entry.response.status}`);
console.log(entry.resource);
});
トランザクションリクエスト
複数リソースをアトミックに作成・更新:
const transactionRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'transaction',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
const transaction = await transactionRequest([
{
method: 'POST',
url: 'Patient',
resource: {
resourceType: 'Patient',
name: [{ family: 'Doe', given: ['Jane'] }],
gender: 'female',
birthDate: '1990-01-01'
}
},
{
method: 'POST',
url: 'Condition',
resource: {
resourceType: 'Condition',
clinicalStatus: { coding: [{ code: 'active' }] },
code: { coding: [{ system: 'http://snomed.info/sct', code: '38341003' }] },
subject: { reference: 'Patient/-1' }
}
}
]);
サブスクリプションとWebhook
FHIRサブスクリプション (R4B+)
リソースの変更通知を受信:
const createSubscription = async (subscriptionData) => {
const subscription = {
resourceType: 'Subscription',
status: 'requested',
criteria: subscriptionData.criteria,
reason: subscriptionData.reason,
channel: {
type: 'rest-hook',
endpoint: subscriptionData.endpoint,
payload: 'application/fhir+json'
}
};
const response = await fhirRequest('/Subscription', {
method: 'POST',
body: JSON.stringify(subscription)
});
return response;
};
const subscription = await createSubscription({
criteria: 'DiagnosticReport?category=laboratory&patient=12345',
reason: 'Monitor patient lab results',
endpoint: 'https://myapp.com/webhooks/fhir'
});
FHIR Webhookの処理
通知を受信して処理するExpress例:
const express = require('express');
const app = express();
app.post('/webhooks/fhir', express.json({ type: 'application/fhir+json' }), async (req, res) => {
const notification = req.body;
if (notification.subscription !== expectedSubscription) {
return res.status(401).send('Unauthorized');
}
if (notification.event?.resourceType === 'DiagnosticReport') {
const reportId = notification.event.resourceId;
const report = await fhirRequest(`/DiagnosticReport/${reportId}`);
await processLabResult(report);
}
res.status(200).send('OK');
});
一般的な問題のトラブルシューティング
問題: 401 Unauthorized
症状: 「Unauthorized」や「Invalid token」エラー
対策:
- トークンの有効期限を確認
- スコープが必要なリソースをカバーしているか確認
- Authorizationヘッダー付与を確認
- FHIRサーバーURLとトークンのaudが一致しているか確認
問題: 403 Forbidden
症状: トークン有効だがアクセス拒否
対策:
- ユーザー権限を確認
- 患者コンテキストの一致を確認
- SMARTスコープが操作対象を含むか確認
- リソースレベルACLの確認
問題: 404 Not Found
症状: リソースやエンドポイントが存在しない
対策:
- リソースIDの正確性を確認
- FHIRベースURLの正確性を確認
- サーバーでタイプがサポートされているか確認
- バージョン固有URL(R4/R4B等)を確認
問題: 422 Unprocessable Entity
症状: 作成/更新時のバリデーションエラー
対策/例:
const error = await response.json();
error.issue?.forEach(issue => {
console.error(`Severity: ${issue.severity}`);
console.error(`Location: ${issue.expression?.join('.')}`);
console.error(`Message: ${issue.details?.text}`);
});
主な原因例:
- 必須フィールド欠落
- コードシステム値不正
- 参照形式ミス
- 日付形式の誤り
本番デプロイチェックリスト
本番運用前の実装必須項目:
- [ ] SMART on FHIRでOAuth 2.0認証を構成
- [ ] トークンリフレッシュロジック実装
- [ ] エラー処理の整備
- [ ] PHIを含まない包括的なロギング
- [ ] レートリミット導入
- [ ] 指数バックオフ付きリトライ実装
- [ ] 複数EHRベンダーでのテスト
- [ ] FHIRバリデーターでリソース検証
- [ ] FHIR操作のドキュメント化
- [ ] 監視・アラート設定
- [ ] トラブル対策ランブック作成
FHIR検証
const { FhirValidator } = require('fhir-validator');
const validator = new FhirValidator('4.0.1');
const validateResource = async (resource) => {
const validationResult = await validator.validate(resource);
if (!validationResult.valid) {
validationResult.issues.forEach(issue => {
console.error(`Validation Error: ${issue.message}`);
console.error(`Location: ${issue.path}`);
});
throw new Error('Resource validation failed');
}
return true;
};
await validateResource(patientResource);
実世界のユースケース
患者ポータル統合
- 課題: 複数医療機関の記録にアクセス不可
- 解決: Epic/Cerner統合SMART on FHIRアプリ
- 結果: 患者利用率80%、記録要求50%削減
実装ポイント:
- SMARTアプリ起動
- Patient, Observation, Condition, MedicationRequestのreadのみ許可
- リフレッシュトークンで永続セッション
- モバイルUI
臨床意思決定支援
- 課題: 予防ケアの機会逸失
- 解決: リアルタイムFHIRクエリでケアギャップ検出
- 結果: HEDISスコア25%向上
実装ポイント:
- 医療従事者向けSMARTアプリ
- Patient, Condition, Observation, Immunizationをクエリ
- ガイドライン準拠のケアギャップ計算
- EHR内でインライン推奨
集団健康分析
- 課題: プロバイダ間データ不完全
- 解決: FHIRバルクデータエクスポート利用
- 結果: 360度患者ビュー、PMPMコスト削減
実装ポイント:
- $exportによるバルクデータアクセス
- データウェアハウスに毎晩エクスポート
- リスク層別化モデル
- ケアマネージャーへのアラート
結論
HL7 FHIRは現代医療システムの相互運用性を支えます。
主なポイント:
- FHIR R4が現行の標準
- SMART on FHIRでセキュアなOAuth 2.0認証
- 標準リソース型(患者・観察・状態・薬剤)による一貫したデータアクセス
- 柔軟な検索パラメーター
- バッチ/トランザクションで業務ワークフロー自動化
- Apidogで効率的なFHIR APIテスト・ドキュメント化
FAQ
HL7 FHIRは何のために使われますか?
FHIRは電子カルテ、患者ポータル、モバイルアプリ等の間で標準化された医療データ交換を実現します。代表的用途は患者アクセス、臨床意思決定支援、集団健康、ケア連携など。
FHIRを始めるにはどうすればよいですか?
公開FHIRサーバー(HAPI FHIRテストサーバー等)にアクセス、またはクラウドFHIRサービス(Azure API for FHIR、AWS HealthLake等)をセットアップし、リソース読み取りや検索クエリを試してください。
HL7 v2とFHIRの違いは?
HL7 v2はイベント駆動・パイプ区切りのメッセージ交換(ADT等)、FHIRはリソースベースRESTful API+JSON/XML。FHIRはWeb/モバイルアプリ連携に最適です。
FHIRはHIPAA準拠ですか?
FHIR自体はデータ形式標準で、HIPAA準拠は実装依存です。暗号化/認証/アクセス制御/監査を必ず実装してください。セキュアなアクセスにはSMART on FHIR+OAuth 2.0を推奨。
SMARTスコープとは?
SMARTスコープはFHIRリソースごとに細かいアクセス権を付与します(例: patient/Observation.read)。必要最小限のスコープのみ要求しましょう。
FHIRでリソースを検索するには?
GETクエリパラメータを使います。例:/Patient?name=Smith&birthdate=ge1980-01-01
修飾子(:exact, :contains)やプレフィックス(gt, lt, ge, le)も利用できます。
Bulk FHIRとは?
Bulk FHIR($export)は、NDJSON形式で大規模データセットを非同期エクスポートする仕組みです。集団分析・データウェアハウス用。
FHIRのバージョン管理は?
特定バージョン(基本はR4)をターゲットに、バージョン固有エンドポイントを利用。CapabilityStatementでサポート内容を確認しましょう。
FHIRをカスタム拡張できますか?
はい。FHIR拡張でカスタムデータ要素を追加可能。Implementation Guideで定義し、広く共有する場合はHL7に登録します。
FHIR開発に役立つツールは?
HAPI FHIR(OSSサーバー)、FHIRバリデーター、Postmanコレクション、Apidog(APIテスト・ドキュメント化)などが利用できます。

Top comments (0)