業務システム

FHIRとは?JP Coreのリソース構造とHAPI FHIRで試すREST APIの実例

FHIRとは?JP Coreのリソース構造とHAPI FHIRで試すREST APIの実例

FHIR(ファイア)は、HL7 Internationalが定める医療情報交換の標準規格で、患者や検査結果を「リソース」というJSONの単位で表し、HTTPのREST APIでやり取りします。この記事では、日本で使われる基底仕様R4 4.0.1と、その上に日本向けの制約を足したJP Core 1.2.0の関係を整理し、JP_Patientで必須になる識別子やカナ氏名の書き方を公式作例から読み解きます。後半ではHAPI FHIRのJPAサーバーをDockerで起動し、作成・更新・検索・$validateをcurlで試す手順を載せました。電子カルテ情報共有サービスに送るデータの細則は電子カルテ情報共有サービスの2文書5情報のFHIR送信を扱った記事に分けています。

まとめ:FHIRは版を固定したJP Coreの上で動く検証環境から始める

FHIRの中身は、140種類を超えるリソースの定義と、それを読み書きするREST APIの規約です。日本の実装は基底仕様R4 4.0.1を土台に、JP Coreが識別子の書式やカナ氏名などの制約を加え、さらに電子カルテ情報共有サービス向けのJP-CLINSのような用途別の実装ガイドが重なる三層になっています。どの層のどの版に合わせるかを最初に決めないと、検証を通ったデータが相手先で弾かれます。

手を動かすなら、HAPI FHIRのJPAサーバーにJP Coreのパッケージを読み込ませ、公式作例のPatientをPOSTして$validateに掛けるところまでなら、数個のコマンドで届きます。本番のFHIRサーバーを自前で持つかどうかは、認証と監査ログまで作り込む覚悟があるかが判断の基準です。既存製品の連携機能で足りる場面では、サーバーを自作しないほうが安く済みます。

FHIRの位置づけとHL7 v2・CDAとの違いを実装者の目線で整理

FHIRは「Fast Healthcare Interoperability Resources」の略です。同じHL7の系譜にあるv2メッセージやCDA文書と比べると、Webの開発者がそのまま読める形に寄せた点が最大の違いです。

HL7 International公開のR4 4.0.1とR5 5.0.0の使い分け

HL7 FHIRの公式サイトのトップは、2026年11月時点でv5.0.0(R5)を現行版として表示しています。それでも国内の実装で選ぶのはR4 4.0.1です。JP Coreも、後述するJP-CLINSも、R4 4.0.1を基底仕様として作られているからです。

R4とR5ではリソースの要素名や必須の度合いが一部違い、R5のサーバーにR4前提のJSONを送ると読めない要素が出ます。新規に作るシステムでも、国内の相手先とつなぐ限りはR4で固定し、R5への移行はJP Coreが対応版を出してから検討する順番が安全です。

区切り文字で書くHL7 v2メッセージとJSONのリソースを比べた差分

院内の部門システム間で今も多く流れているHL7 v2は、「PID」「OBX」といったセグメントを縦棒で区切った1行の文字列で、項目の意味は位置で決まります。FHIR R4のv2との比較ページも、v2の要素をFHIRのリソースへ1対1で写せるわけではないと書いています。

FHIRでは同じ患者情報が "family": "山田" のように名前付きの要素になり、JSONパーサーと一般的なHTTPクライアントだけで扱えます。読み手を選ばないぶん、受け取る側の実装は軽くなります。ただし院内の既存の流れをFHIRに置き換える必要は必ずしもありません。v2とFHIRの使い分けは医療のデータ連携と院内4方式の比較で扱っています。

FHIRリソースの構造とJP Coreが日本向けに足した制約・拡張

FHIRの学習コストの大半は、リソースの種類と、それに国別・用途別の制約を重ねる「プロファイル」の仕組みを掴むことにあります。

Patient・Observationなど140種超のリソースと成熟度Nの意味

R4のリソース一覧は、リソースをFoundation・Base・Clinical・Financial・Specializedの5分類で並べ、140種類を超えます。各リソースには成熟度が付き、「N」はNormative(規範)として後方互換が保証された状態です。R4でNになっているのは、Patient・Observation・Bundle・OperationOutcomeなど一部に限られます。

実務で最初に触るのは、患者のPatient、検査値やバイタルのObservation、傷病名のCondition、処方のMedicationRequest、複数リソースを束ねるBundleの5つです。成熟度が低いリソースは版の改定で要素が変わりやすいので、採用前に一覧の表示を確かめます。

JP Core 1.2.0のJP_Patientで必須になる識別子と氏名カナ表記

JP CoreはFHIRを国内で使うための共通の実装ガイドで、公開履歴によれば最新版は2025年7月30日の1.2.0です。JP Core Patientプロファイルでは、基底仕様で任意だった identifier が1件以上の必須になり、その value も必須です。

公式作例のJSONを見ると、日本固有の書き方が3つ読み取れます。

  • 患者IDの system は urn:oid:1.2.392.100495.20.3.51. に「1」と保険医療機関番号10桁を続けたOIDで、施設ごとに名前空間が分かれる
  • 氏名は name を2件持ち、漢字には拡張 iso21090-EN-representation の値IDE、カナには値SYLを付けて区別する
  • 人種はJP Core独自の拡張JP_Patient_Raceで表し、宗教と出生地はHL7標準の拡張をそのまま使う

特に識別子のOIDは、院内の患者番号をそのまま入れるだけでは足りず、医療機関番号を正しく組み込まないと他施設のデータと衝突します。患者マスタからFHIRを組み立てるときに最初に詰まるのがこの部分です。

基底仕様・JP Core・JP-CLINSの三層と版を固定する理由

JP Coreの上には、用途ごとの実装ガイドが載ります。電子カルテ情報共有サービス向けのJP-CLINSは、最新の1.2.0ではなく、2024年9月29日に公開された専用版1.1.2-clinsを土台にしています。同じ「JP Core準拠」でも、送り先によって参照する版が違うということです。

このため、FHIRのデータを作るシステムでは、基底仕様・JP Core・用途別ガイドの3つの版番号を設定値として持ち、検証にも同じ版のパッケージを使います。版を混ぜると、手元の検証では通ったのに相手のサーバーでプロファイル違反になる、という事故が起きます。

HAPI FHIRのJPAサーバーをDockerで起動して手元に検証環境を作る

FHIRの理解は、仕様書を読むより実際にリクエストを投げるほうが早く進みます。検証用のサーバーには、Java製のオープンソース実装HAPI FHIRのJPAサーバーが手軽です。

hapiproject/hapiイメージの起動とmetadataでの疎通を確かめる

HAPI FHIR JPA Server StarterのREADMEは、公式イメージを8080番で起動する方法を示しています。HAPI FHIRのGetting Startedにあるとおり組み込みのH2データベースを同梱しているため、外部のDBなしで起動できます。Docker Hubのタグ一覧では、2026年10月2日に出たv8.12.0-2がlatestと同じ版です。検証でも版はタグで固定します。

起動後、ブラウザで開くWebテスターは http://localhost:8080/、REST APIのベースURLは http://localhost:8080/fhir/ です。/fhir/metadata がCapabilityStatementを返せば、サーバーは受け付けられる状態にあります。

JP Core 1.2.0パッケージを起動時に読み込ませる環境変数の設定

JP_Patientで検証するには、JP Coreのパッケージをサーバーに入れておく必要があります。READMEは hapi.fhir.implementationguides 配下の設定を環境変数で渡す例を載せており、パッケージ名は配布ファイルの package.json にある jp-core.r4、版は 1.2.0 です。

# HAPI FHIR JPAサーバーをR4で起動し、JP Core 1.2.0を読み込ませる
docker run -d --name hapi-fhir -p 8080:8080 \
  -e "hapi.fhir.fhir_version=R4" \
  -e "hapi.fhir.implementationguides.jpcore.name=jp-core.r4" \
  -e "hapi.fhir.implementationguides.jpcore.version=1.2.0" \
  -e "hapi.fhir.implementationguides.jpcore.packageUrl=https://jpfhir.jp/fhir/core/1.2.0/jp-core.r4-1.2.0.tgz" \
  -e "hapi.fhir.implementationguides.jpcore.installMode=STORE_AND_INSTALL" \
  hapiproject/hapi:v8.12.0-2

# 起動には数十秒かかる。CapabilityStatementのFHIR版が4.0.1なら準備完了
curl -s http://localhost:8080/fhir/metadata | grep -o '"fhirVersion"[^,]*'

installModeのSTORE_AND_INSTALLは、パッケージを保存するだけでなくプロファイルなどの定義をサーバーに登録する指定です。H2はコンテナを消すとデータも消えるので、残したい場合はREADMEのPostgreSQL設定に切り替えます。

curlで試すFHIR REST APIの作成・読取・更新と版の管理

FHIR R4のRESTful APIの章は、作成がPOST、読取がGET、更新がPUTという素直な対応を定めています。Content-Typeは application/fhir+json を使います。

JP_Patient準拠の患者をPOSTし201とLocationヘッダを確かめる

公式作例から必要な要素だけを残した patient.json を用意します。meta.profile にJP_PatientのURLを書いておくと、後の検証でどのプロファイルに照らすかが明確になります。

{
  "resourceType": "Patient",
  "meta": { "profile": ["http://jpfhir.jp/fhir/core/StructureDefinition/JP_Patient"] },
  "identifier": [
    { "system": "urn:oid:1.2.392.100495.20.3.51.11311234567", "value": "00000010" }
  ],
  "name": [
    {
      "extension": [{ "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation", "valueCode": "IDE" }],
      "use": "usual", "text": "山田 太郎", "family": "山田", "given": ["太郎"]
    },
    {
      "extension": [{ "url": "http://hl7.org/fhir/StructureDefinition/iso21090-EN-representation", "valueCode": "SYL" }],
      "use": "usual", "text": "ヤマダ タロウ", "family": "ヤマダ", "given": ["タロウ"]
    }
  ],
  "gender": "male",
  "birthDate": "1970-01-01"
}
# 同じ識別子の患者がいなければ作成する(条件付き作成)
curl -si -X POST http://localhost:8080/fhir/Patient \
  -H "Content-Type: application/fhir+json" \
  -H "If-None-Exist: identifier=urn:oid:1.2.392.100495.20.3.51.11311234567%7C00000010" \
  --data-binary @patient.json | grep -iE "^HTTP|^Location|^ETag"

成功すると201 Createdが返り、Locationヘッダには [base]/Patient/[id]/_history/1 の形で新しいIDと版が入ります。If-None-Existは仕様上の条件付き作成で、一致する患者が1件あれば作成せずに200を返し、複数あれば412で処理を止める仕様です。連携処理の再送で同じ患者が二重登録される事故を、この1行で防げます。

If-MatchとETagで競合する更新を防ぐPUTと412が返る条件

FHIRのサーバーはリソースを更新するたびに versionId を1つ進め、ETagヘッダに W/"1" の形で返すよう推奨されています。更新時にこの値をIf-Matchに付けると、取得後に他の誰かが書き換えていた場合は更新が拒否されます。

# 取得した版(ここでは1)を前提に更新する。PATIENT_IDはPOSTのLocationから読み替える
curl -si -X PUT http://localhost:8080/fhir/Patient/PATIENT_ID \
  -H "Content-Type: application/fhir+json" \
  -H 'If-Match: W/"1"' \
  --data-binary @patient_updated.json | grep -iE "^HTTP|^ETag"

PUTで送るJSONには "id": "PATIENT_ID" を入れておきます。1回目は成功してETagが W/"2" に変わり、同じコマンドをもう一度送ると版が合わず、更新は失敗です。R4の仕様は版の競合に409 Conflictか412 Precondition Failedを返すとしているので、クライアントは両方を「取り直してから再試行」の合図として扱います。過去の版は /Patient/PATIENT_ID/_history/1 で読めます。

$validateにJP_Patientのprofileを指定して適合性を検査する

保存とは別に、データがプロファイルに合っているかだけを確かめる操作が$validateです。profileパラメータにJP_PatientのURLを渡すと、結果がOperationOutcomeで返ります。

# 保存せずにJP_Patientへの適合だけを確かめる
curl -s -X POST \
  'http://localhost:8080/fhir/Patient/$validate?profile=http://jpfhir.jp/fhir/core/StructureDefinition/JP_Patient' \
  -H "Content-Type: application/fhir+json" \
  --data-binary @patient.json

patient.json から identifier を消して送ると、JP_Patientで必須の要素が欠けている旨のエラーがissueに並びます。基底仕様のPatientとして検証しただけではこの欠落は見つかりません。JP Coreを読み込ませた意味はここにあります。

FHIR検索パラメータで患者と検査結果をまとめて取り出す書き方

FHIRの検索はURLのクエリで書きます。R4の検索の章に型ごとの書式があり、結果は常にBundleで返ります。

identifierのsystem|value指定とbirthdateのge・le接頭辞

識別子のようなtoken型は system|value の形で、OIDまで含めて指定すると施設をまたいでも一意に引けます。日付には ge(以上)、le(以下)、gt、lt などの接頭辞を付け、同じパラメータを2回並べると範囲指定になります。

# 施設のOIDと患者番号で1件に絞る(| は %7C にエンコード)
curl -s 'http://localhost:8080/fhir/Patient?identifier=urn:oid:1.2.392.100495.20.3.51.11311234567%7C00000010'

# 1970年代生まれの男性を10件ずつ、件数だけを先に確かめる
curl -s 'http://localhost:8080/fhir/Patient?birthdate=ge1970-01-01&birthdate=le1979-12-31&gender=male&_summary=count'
curl -s 'http://localhost:8080/fhir/Patient?birthdate=ge1970-01-01&birthdate=le1979-12-31&gender=male&_count=10'

_summary=count はリソースを返さずに件数だけをBundleのtotalに入れます。大量のデータを引く前に件数を見ておく目的は、取得を何回のバッチに分けるかを先に決めることです。2ページ目以降は、返ってきたBundleのlinkにあるURLをたどります。

_includeと連鎖検索でObservationから患者を一度に引くクエリ

検査結果の一覧画面を作るとき、Observationを取ってから患者を1件ずつ読みに行くと、往復の回数が件数分だけ増えます。参照先を同じBundleに入れて返させるのが _include で、参照先の条件で絞り込むのが連鎖検索です。

# 患者番号で検査結果を絞り、参照先のPatientも同じBundleに含める
curl -s 'http://localhost:8080/fhir/Observation?subject:Patient.identifier=urn:oid:1.2.392.100495.20.3.51.11311234567%7C00000010&_include=Observation:subject&_sort=-date'

同じBundleに入ったPatientは、entryのsearch.modeが include になっているため区別が可能です。逆向きに、患者を起点に関連する検査結果を含めたいときは _revinclude=Observation:subject を使います。どの検索パラメータに対応しているかはサーバーごとに違うため、metadataのCapabilityStatementで確かめてから画面の設計に入ります。

FHIRサーバーを自前で持つか既存製品の連携機能に任せるかの判断

検証環境がすぐ立つことと、本番でFHIRサーバーを運用できることは別の話です。ここでは、どこまでを自前で作るかの線引きを示します。

HAPI FHIRを本番に置くときに追加で要るDB・認証・監査ログ

Starterの既定構成は、検証のための最小構成です。本番に置くなら、少なくとも次の作り込みが要ります。

  • H2から外部のPostgreSQLなどへの切り替えと、バックアップ・復旧手順の整備
  • OAuth 2.0などによるアクセス制御。既定ではAPIに誰でも書き込める
  • 誰がどの患者を参照したかを残す監査ログと、その保管期間の設計
  • リソース数の増加に合わせた検索インデックスとサーバーのサイジング

特に認証と監査ログは、医療情報を扱う以上ガイドラインへの適合が問われる部分です。医療情報システムの安全管理の要件と、SS-MIX2の格納データからFHIR化できる範囲は医療情報システムとSS-MIX2・FHIR接続の実務で整理しています。

FHIRサーバーを自作しないほうがよい場面と開発を外に出す範囲

電子カルテのベンダがFHIRの出力機能を提供していて、送り先が電子カルテ情報共有サービスのように決まっているなら、自前のFHIRサーバーは要りません。院内に持つのは、送信前のデータを確かめる検証の仕組みだけで十分です。電子カルテ側の対応状況の見方は電子カルテの種類と標準仕様対応の判断にまとめています。

自前のFHIRサーバーやAPI層が要るのは、複数の部門システムのデータを集めて外部のアプリやPHRに出す場合、独自のシステムがデータの出どころになっている場合です。この層は、変換・検証・認証・監査を受け持つ独立したシステムとして切り出せます。要件を固めて見積もりたい場合は医療機関向けシステム開発で相談を受けています。

よくある質問

FHIRについて、検索の多い質問を5つ取り上げます。

FHIRとHL7は何が違うのですか?

HL7は医療情報の標準を作る団体(HL7 International)と、その団体が定めた規格群の総称です。FHIRはその規格群の1つで、v2メッセージやCDA文書の後に作られた、REST APIとJSONを前提にした規格に当たります。「HL7 FHIR」と並べて書くのは、HL7が定めたFHIRという意味です。院内では今もHL7 v2が多く使われており、FHIRがそれを置き換えたわけではありません。

FHIRの読み方と、R4・R5のどちらを使えばよいかを教えてください

読み方は「ファイア」です。版は、国内で使う限りR4 4.0.1を選びます。HL7の公式サイトはR5 5.0.0を現行版として表示していますが、JP Core 1.2.0も電子カルテ情報共有サービス向けのJP-CLINSも基底仕様はR4 4.0.1です。R5を使うのは、海外のサービスと接続するなど、相手先がR5を指定している場合に限られます。

JP Coreに準拠していれば電子カルテ情報共有サービスにそのまま送れますか?

送れません。電子カルテ情報共有サービスはJP Coreの上にあるJP-CLINSの規則に従う必要があり、土台のJP Coreも専用版の1.1.2-clinsです。Bundleの組み方や識別子の採番など、JP Coreには無い規則も加わります。具体的な送信仕様は電子カルテ情報共有サービスのFHIR送信の解説で扱っています。

FHIRのAPIを外部のスマホアプリやPHRに公開してもよいですか?

規格上は可能ですが、検証用の既定構成のまま公開してはいけません。HAPI FHIRのStarterは既定でアクセス制御を持たないため、OAuth 2.0による認可、患者本人に見せる範囲の制御、参照の監査ログを先に作ります。患者が自分の情報を管理するPHRとの役割分担はPHRと電子カルテ・EHRの役割分担の解説を参照してください。

FHIRの検証に使える公開のテストデータはありますか?

まずはJP Coreの実装ガイドにある公式作例が使えます。Patientのほか、Observationや処方などのプロファイルごとにJSONが公開されており、本文で使ったpatient.jsonもJP Core 1.2.0の作例から要素を絞ったものです。作例はプロファイルに適合する前提で作られているので、一部の要素を消したり書式を崩したりして、$validateがエラーを返すかを試す材料になります。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.03 テックブログ ノジマ不正アクセスで約700万件漏えい:対象確認・窓口と乗っ取り注文を止める実装
  2. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号
  3. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  4. 2026.11.04 テックブログ CVE-2026-119977:WordPress更新中のRCEと安全な更新手順
  5. 2026.11.02 テックブログ アララの不正アクセス:メール約7,700万件の持ち出しと契約企業が行うAPIキー再設定・代替送信・連名報告

RELATED POSTS 関連記事

目次