Node.jsでFHIRサーバを立ち上げてみた

# Node.jsでFHIRサーバを立ち上げてみた

Node.jsで動くFHIRサーバを構築～実行テストまでを紹介します。
Asymmetrik社が開発したFHIRサーバ（[node-fhir-server-mongo](https://github.com/Asymmetrik/node-fhir-server-mongo)：https://github.com/Asymmetrik/node-fhir-server-mongo ）を使用します。

本記事は、ヘルスビット株式会社におけるプロジェクトの一環として、[@S_aiueo32](https://qiita.com/S-aiueo32)さんと調査、作成しております。

## FHIRとは

情報通信技術の発展により、身の回りの情報を電子化し、保存・共有・活用するシーンが増えてきています。保健医療情報も例外ではなく、特にカルテの電子化は地方の病院まで広く行き届いています。しかし、電子保健医療情報には明確に定められたフォーマットが無く、医療機関間での相互運用に難があります。この障壁をなくすため、HL7(Health Level 7)というNPOが組織され、標準データフォーマットの提唱が行われています。

FHIR(Fast Healthcare Interoperability Resources; fireと同じ発音)は、HL7が開発者フレンドリーを目指して策定した規格です。データフォーマット自体は既存の規格(HL7 2.xやHL7 3.xなど)とほぼ変わりませんが、データ表現にJSONやXMLを用い、HTTPを介したRESTful APIとして機能を提供することで開発を容易にしています。現在Release 4までバージョンがあり、つい最近もGCPやAzureでもヘルスケア用APIとして提供開始され、日に日に注目を浴びるようになってきています。

## Asymmetrik

Asymmetrikはメリーランド州を拠点とした2008年に創業したソフトウェア会社で、ヘルスケアのソフトウェア開発やデータ解析をしています。

## node-fhir-server-mongo の実行方法

***以降`node-fhir-server-mongo/`をルートフォルダとします。例えば、`node-fhir-server-mongo/src/config.js`を参照するときは`./src/config.js`と表記します。***

## 環境構築

動作確認環境

- Windows10
- Ubuntu18.04

WindowsもLinux環境でも以下のテストに関しては基本的に同じです。
実用上はLinux環境のほうが便利かと思いますが、FHIRの動作テストするのみならば、どちらでも問題ありません。
動作確認してませんが、どのOSでも下記必要なソフトウェアが用意できれば動作すると思われます。


### 必要なソフトウェア

- mongoDB
- Node.js
- npm
- Git
- （ツール）Postman
  - 相当するツールなら何でも良い

#### (Windows)各種インストール

下記ソフトウェアをダウンロード・インストールして完了です。
各種インストール方法については多く資料があるため、詳細はそちらに任せます。

- [MongoDB install](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/)
- [Node.js](https://nodejs.org/en/)
- [git](https://gitforwindows.org/)
- [Postman](https://www.getpostman.com/)

#### (Ubuntu)各種インストール

まっさらな状態から環境を作る想定です。nodejs、npm、git、mongodbをインストールします。

```bash
# nodejs npm gitのインストール
sudo apt install -y nodejs npm git
# mongodbのインストール
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
eho "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
sudo apt update
sudo apt install mongodb-org
```

mongodbはインストールが終わった後、サービスを実行しておく必要があります。

```bash
# mongodbのサービスを実行
sudo service mongod start
```

Postmanは、UbuntuならばUbuntuのソフトウェアセンターからでもインストール可能です。Postmanは、Google Chromeのアプリとして使用もできます。どちらでも問題ありません。

----

## node-fhir-server-mongoのインストール（とテスト）

以降、両方の環境で実行できます。

1. コンソール（ターミナル、コマンドプロンプト、Powershell）を起動
2. node-fhir-server-mongoの取得

```bash
git clone https://github.com/Asymmetrik/node-fhir-server-mongo.git
```

3. フォルダへ移動

```bash
cd node-fhir-server-mongo/
```

4. npmのインストール

```bash
npm install
```

とくに問題がなければ、errorはでません。warningはでるかもしれません。

5. FHIRサーバの起動

```bash
npm run start
```

6. Postmanを起動
7. `localhost:3000/3_0_1/metadata`にGETを送る
   - URLに`localhost:3000/3_0_1/metadata`を入力
   - Sendボタンを押す

![metadata画像](https://user-images.githubusercontent.com/14970497/53960228-14351b00-4129-11e9-9614-67ceb2303d40.jpg "metadata")

8. error以外が返ってきたらOK、もしくはコンソール上に`info: Metadata.generateCapabilityStatement`が表示されていればOK

----

## 患者データ：作成と更新

### Postman：PUTによる更新

`localhost:3000/3_0_1/Patient/hogehoge1234`にPUTを送ることで、患者データを更新します。

ヘッダーとボディにいくつか記述をします。ちなみに、```hogehoge1234```部分はIDで任意に変更可能です。ボディに記述するidの項目と同じにします。

PUTとして送る内容は下記になります。

```
Create Patient

PUT /3_0_1/Patient/hogehoge1234 HTTP/1.1
Host: localhost:3000
Content-Type: application/fhir+json
Cache-Control: no-cache

# Raw Body
{
  "resourceType" : "Patient",
  "id" : "hogehoge1234",
}
```

Postmanで実行する場合は、次の設定をしてください。

- Header のタブ
  - Content-Type
    - application/fhir+json
  - Cache-Control
    - no-cache

![header画像](https://user-images.githubusercontent.com/14970497/53960227-139c8480-4129-11e9-8b8d-814dee3945c3.jpg "header")

- Body のタブ
  - Rowのラジオボタンを選択します。選択すると、直下にテキストボックスが現れます。下記をテキストボックスに記入します。

```body
{
  "resourceType" : "Patient",
  "id" : "hogehoge1234"
}
```

![body画像](https://user-images.githubusercontent.com/14970497/53960233-14cdb180-4129-11e9-8986-b91dd438c32a.jpg
 "body")

Error が出てると、返却値orコンソールに404エラーが表示されます。
正常に実行されるとPostman上では返却値はありません。
また、正常に実行されるとき、コンソール上には```info: Patient >>> update``` が表示されます。
それ以外のときは、更新に失敗しています。

補足ですが、上記ボディの内容は、最小の情報のみ記述しています。患者データとして扱えるフィールドは、[FHIR](https://www.hl7.org/fhir/index.html)を参照してください。具体的には、Level 3、4、5の項目を参照してください。患者以外データに関しても、どういう項目および情報を扱えるかはFHIRを参照してください。

----

### 患者データ：取得

`localhost:3000/3_0_1/Patient/hogehoge1234`にGETを送ります。
正常に処理されると、Bodyに上で入力したデータがPostman上に表示されます。

コンソールには```info: Patient >>> searchById```が表示されます。

![return画像](https://user-images.githubusercontent.com/14970497/53960231-14cdb180-4129-11e9-9ab9-7bc4eaa0e02a.jpg "return")

----

## Basic認証によるアクセスコントロール

**この方法および、紹介するコードは安全な方法ではありません。テストおよび動作確認のみに使用してください。**

Basic認証によるアクセスコントールをnode-fhir-server-mongoに実装します。より強固な認証システムもほぼ同様に実装できます。

認証システムは`./src/strategy`内で実装し`./src/config.js`で設定します。

### 認証システムの実装

[node-fhir-server-coreのwiki](https://github.com/Asymmetrik/node-fhir-server-core/wiki/Access-Control)を参考にしています。Basic認証は[PassportJS](https://www.npmjs.com/package/passport-http)を使用します。

PassportJS（正確にはpassport-http）を使用するため、まず、ルートフォルダで

```bash
npm install --save passport-http
```

を実行します。

`./src/strategy`内に`mybasic.js`ファイルを作成します。
ファイルには下記を記述します。

```./src/strategy/mybasic.js
const {BasicStrategy} = require('passport-http');

module.exports.strategy = new BasicStrategy((usename, password done) => {
    // ここでusernameとpasswordの値を決定
    if (username === 'user' && password === 'pass') {
        return done(null, {});
    } else {
        return done(new Error('Invalid Username/Password'));
    }
});
```

作成したファイルを、node-fhir-server-mongoの認証としてパスを通します。
`./src/config.js`に下記を追記します。

```./src/config.js
// 35行目付近。authブロック内
auth: {
  resourceServer: env.RESOURCE_SERVER,
  strategy: {
    name: 'basic',
    service: './src/strategies/mybasic.js'
  }
},
```

変更のあと、サーバを再起動します。ルートフォルダ（env.jsonのあるフォルダ）で

```bash
npm run start
```

します。

### 認証の動作確認

認証失敗により、アクセスできないことを確認します。
「患者データ：取得」の項と同様に、`localhost:3000/3_0_1/Patient/hogehoge1234`にGETを送ります。
このとき、postman上では、権限がない旨の返答が表示されます。

![unauthorized画像](https://user-images.githubusercontent.com/14970497/53960230-14351b00-4129-11e9-94b7-72d0db23ca2f.jpg "unauth")

次に、認証を通します。
postmanのAuthタブを開き、Basic認証を選択します。usernameには`user`を、passwordには`pass`を入力します。

![Auth画像](https://user-images.githubusercontent.com/14970497/53960229-14351b00-4129-11e9-9c61-e4348047e610.jpg "auth")

その後、「患者データ：取得」の項と同様に、`localhost:3000/3_0_1/Patient/hogehoge1234`にGETを送ります。認証が通っていれば、患者データが表示されます。

実行するとよくわかると思いますが、上記コードによる認証の実装は大変危険です。重ねて表記しますが、**この方法および、紹介するコードは安全な方法ではありません。テストおよび動作確認のみに使用してください。**
OAuth2を用いる場合でも、認証システムは`./src/strategies/`にコードを置き、`./src/config.js`にパスを通すことで実装できます。

----

## コード解説

簡単ですが、患者データ以外のデータを扱うため、あるいは強固な認証を実装するために簡単なコード解説をします。
node-fhir-server-mongoおよびcoreについては、[node-fhir-server-coreのwiki](https://github.com/Asymmetrik/node-fhir-server-core/wiki)が参考になります。


### 設定ファイル

主な設定ファイルは2種類です。サーバのアドレスや新規作成ファイルへのパスを定義します。

- `./env.json`
- `./src/config.js`

自前で実装をする場合、対応するファイルを`./src/services`や`./src/strategies`に置き、`./src/config.js`でパスを指定するという流れになります。

### env.json

mongoDBのサーバアドレスやサーバのポート、認証サーバのアドレスなどを設定できます。
テストでは一切変更しません。

設定可能項目は以下

- mongoDBのホストネーム
- mongoDBのDB名
- 出力ログの設定
- FHIRのサーバーポート
- SSL鍵と証明書
- リソースサーバ
- 認証サーバ
- ホワイトリスト
- Client_ID
- Client_Secret
- Introspection_URL

### src/config.js

config.jsでは、

- セキュリティの設定
- サーバ設定オプション
- 操作できるデータ項目の設定
が設定できます。

初期設定では、以下のデータが操作できるようになっています。

- 機関(organization)
- 患者(patient)
- アレルギー耐性(allergyintolerance)

対応するコードをコメント・コメント解除することで動作を指定できます。
細かい動作を記述するには、対応するコードを直接いじればよいです。

例えば、

```./src/config.js
// ./src/config.js 89行目付近
// コメントを解除すると動作する
[RESOURCES.PATIENT]: {
  service: './src/services/patient/patient.service.js',
  versions: [ VERSIONS['3_0_1'], VERSIONS['1_0_2'] ]
},
// コメントアウトすると動作しない
//[RESOURCES.ACCOUNT]: {
//  service: './src/services/account/account.service.js',
//  versions: [ VERSIONS['3_0_1'] ]
//},
```

の部分が、データ項目の設定部分になっています。コメント解除することで、アクセスできるようになります。逆に、コメントアウトすることでアクセスできなくなります。

挙動の実装は`./src/services/patient/patient.service.js`にします。自分で実装する場合は、認証の実装と同様に`./src/services/patient/`内に自分で実装したファイルを置き、`./src/config.js`に記述します。

セキュリティの設定とサーバの設定は34行目以降にあります。
認証の実装と設定でも記述した箇所に相当します。
サーバのアドレスは`./env.json`にあるため、そちらも参照してください。

### node-fhir-server

node-fhir-server-mongoは、同じくAsymmetrik社が開発している[node-fhir-server-core](https://github.com/Asymmetrik/node-fhir-server-core)をラップします。
基本的には上記にあるnode-fhir-server-mongoのコンフィグ（`./src/config.js`）やソースに直接追記することでFHIRの機能実装をします。

### ./src/services 以下(mongo)

FHIRで扱うデータ処理を実装しています。基本的には、バージョンごとのパーサーと、呼ばれた時の各振る舞いが記述されます。例えば、`./src/services/patient/patient.service.js`の400行目付近まではバージョンごとのパーサーが、以降は実際に`localhost:3000/3_0_1/Patient/`へアクセスしたときの動作が、記述されています。動作がおかしい場合や、未定義となっている動作がある場合は`./src/servicers`以下のコードを編集する必要があります。

細かい定義は[FHIR](https://www.hl7.org/fhir/index.html)を参照するべきですが、ソースとして記述しているのはこのあたりとなります。

### OAuthサーバについて

参考資料：[FHIRサーバのアクセスコントロール](https://github.com/Asymmetrik/node-fhir-server-core/wiki/Access-Control)

本記事では触れていませんが、OAuthを用いた認証に対応しています。OAuthを使うには、別途OAuthサーバを立てる必要があります。

また、scopeを用いた権限制御ができる模様です。scopeの定義は`./node_modules/@asymmetrik/node-fhir-server-core/src/server/profiles/common.scopes.js`あたりが参考になると思われます。
他、FHIRクライアントのexampleも参考になると思われます。

### FHIRサーバにおけるルーティングについて

参考資料：[expressのルーティング](https://expressjs.com/ja/guide/routing.html)

FHIRサーバのルーティングは、表面上は`./src/config.js`で制御されます。configでコメント解除された機能のみがルーティングされます。
ルーティングを変更する必要がある場合、複数ファイルにまたがった変更が必要です。基本的には、変更せず使用するように設計されていると思われます。

実際にルーティングを設定するファイルは、node-fhir-server-core、すなわち、`./node_modules/@asymmetrik/node-fhir-server-core`で実行されます。具体的には、`./node_modules/@asymmetrik/node-fhir-server-core/src/server/route-setter.js`です。
中を見ていただくとわかりますが、route-setterではconfig等で記述された値をルーティングします。そのため、"config等"のファイルすべてに変更を加えなければなりません。

処理順は以下のようになります。

1. `./srcindex.js`が実行される
2. `./src/config.js`の値を取得
3. node-fhir-server-coreがroute-setterを呼ぶ
4. configの値に該当する各ファイルを呼ぶ
5. route-stterがルーティングを定義

4の該当する各ファイルは、node-fhir-server-mongoのみでなく、node-fhir-server-coreも含みます。node-fhir-server-coreに含まれるほぼすべての階層で変更するため、実行エラーを確認しながら修正を加える必要があります。

----

## まとめ

Node.jsで動作するFHIRサーバを構築し、簡単なやり取りをするところまでを紹介しました。また、簡単ですが理解できた範囲でコードの解説しました。

ヘルスビット株式会社および同社インターン生の@S_aiueo32氏に多大な助力ををいただきましたことを改めて感謝いたします。