Postmanとの相性抜群。超絶簡単 CData API ServerでデータベースをREST API化する手順

これは[CData Software Advent Calendar 2023](https://qiita.com/advent-calendar/2023/cdatasoftware) 13日目の記事。

本記事では、[CData API Server](https://www.cdata.com/jp/apiserver/)を活用して、自分のMac環境で動いているデータベース（MySQL on Docker）をREST API化するための手順を解説する。できるだけ設定内容や手順を細かく書いているので、試してみたい方は是非写経してみてください。

# モチベーション

きっかけは、CData Software Japanの杉本さん（@sugimomoto）からのおすすめだった。それで [CData API Server](https://www.cdata.com/jp/apiserver/) の存在をはじめて知った。

[CData API Server](https://www.cdata.com/jp/apiserver/)（以下、CAS）とは、ざっくり説明すると、RDBなど、さまざまなデータソースに対してのデータ取得、更新のためのREST APIを公開できるプロダクトである。ただ、単にREST APIを公開できるだけでなく、安全にデータアクセスするためのセキュリティと認証機能だったり、データのフィルタリング、変換、集計のためのクエリ言語やデータ変換機能など、実運用に必要な機能を備えている。詳しくは[プロダクトページ](https://www.cdata.com/jp/apiserver/)を参照ください。

この存在を知ってまず思ったのが、データソース接続のためのドライバーやライブラリのインストールができないような環境において、とても有効だろうなと。CASにより、REST APIといった比較的に接続敷居の低いインターフェースを通じてアクセスできるようになるわけだが、例えばセンサー、ミニデバイスなどIoT環境だったり、制約の多いサンドボックス環境などでは、REST APIを通じてデータソースとの連携ができるようになるのは嬉しいはず。

特に、私の場合、日常的にAPIテストでおなじみの[Postman](https://www.postman.com/)を使っているのだが、Postmanではその制約上（＊補足）、外部のデータソースと連携をするにはそのデータソース操作のためのAPIを経由する必要があったりする。そういったモチベーションから本プロダクトを試してみたわけだ。

＊ [補足] [Postman](https://www.postman.com/)では、Postmanスクリプトを使うことでAPIリクエストの自動化、テストの実行、データの操作、環境変数の設定など、さまざまなタスクを実行が可能になる。これはJavaScript言語をベースとしてPostmanサンドボック環境で実行可能となっている。しかしサンドボック環境なので、ビルトインなライブラリは限定されており、現時点（2023年12月現在）では、データベースなど外部データソースへのアクセスにはREST APIなどHTTPインターフェースを経由する必要がある。

# 検証環境

| 環境 | 内容 |
| --- | --- |
| ローカルOS | macOS Ventura 13.6 (チップ Apple M2 Pro ) |
| Java (CData API Serverの実行環境) | Openjdk 21.0.1 2023-10-17 LTS |
| MySQL (今回のデータソース) |  MySQL 5.7 (コンテナとしてDockerで実行) |

# 事前準備

CASを動かすためのJava環境と、今回のデータソースであるMySQL（コンテナ）を準備する。

## Java環境の用意

宗教上の理由からOracle JDKではなく、OpenJDK（Eclipse Temurin（Adoptium））をインストールしている。

- **Temurin™ for macOS aarch64** pkgを[adopium](https://adoptium.net/download/) サイトからダウンロード
- pkgファイルを実行。 Eclipse Temurin 21.0.1+12-LTSは `/Library/Java/JavaVirtualMachines/temurin-21.jdk`にインストールされる

チェック
```jsx
$ java --version

openjdk 21.0.1 2023-10-17 LTS
OpenJDK Runtime Environment Temurin-21.0.1+12 (build 21.0.1+12-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.1+12 (build 21.0.1+12-LTS, mixed mode)

$ /usr/libexec/java_home

/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home

```

JAVA_HOMEの設定
    
```jsx
export JAVA_HOME=`/usr/libexec/java_home`
```

## MySQL on Dockerの設定

今回利用したMySQL on Dockerの一通りの設定は、こちらの[リポジトリ](https://github.com/yokawasa-sandbox/mysql-docker)にアップしている。

まずは、MySQL on Dockerのための設定一式をgit clone。

```sh
git clone https://github.com/yokawasa-sandbox/mysql-docker.git
```

docker-composeでMySQLコンテナを起動。

```sh
cd mysql-docker
docker-compose up -d
```

次のような出力がされ、無事MySQLコンテナが立ち上がる。

```
[+] Building 0.0s (0/0)  
[+] Running 3/3
 ✔ Network mysql-docker_default  Created
 ✔ Volume "mysql-docker_mysql"   Created
 ✔ Container mysql               Started
```

次のように、立ち上げたMySQLコンテナにログイン

```sh
docker exec -it mysql /bin/bash
```

サンプルデータ追加のためのSQL文である[insert.sql](https://github.com/yokawasa-sandbox/mysql-docker/blob/main/examples/insert.sql)が`examples/insert.sql`に配置しているので、それを次のようにINSERT。MySQLユーザーとパスワードは、[docker-compose.yml](https://github.com/yokawasa-sandbox/mysql-docker/blob/main/docker-compose.yml)に記述してあるとおり、それぞれ`user`、`password`で実行する。

```sh
bash-4.2# cd /var/tmp
bash-4.2# mysql -u user -p db < examples/insert.sql
```

INSERTした内容を確認してみる。下記のように4つのテーブルができていればOK。

```sh
bash-4.2# mysql -u user -p db 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db                 |
+--------------------+
2 rows in set (0.01 sec)

mysql> use db
mysql> show tables;
+---------------+
| Tables_in_db  |
+---------------+
| Articles      |
| AuthorArticle |
| Authors       |
| Books         |
+---------------+
```

これで準備は完了。

# CData API Server (CAS)の設定・実行

いよいよCASを設定、実行していく。

## ダウンロードと展開

[こちら](https://www.cdata.com/jp/apiserver/download/)のページよりCASのzipファイルをダウンロードする。 ローカルOSがmacOSなのでCross-Platform (Java)を選択してCDataAPIServer.zipダウンロード。

ダウンロードしたzipファイルを解凍。
    
```sh
unzip CDataAPIServer.zip

tree -L 1
.
├── admin
├── api
├── apiserver.jar
├── apiserver.logging.properties
├── apiserverRealm.properties
├── db
├── lib
├── logs
├── readme.md
├── service.sh
├── settings.cfg
├── tmp
└── webapp
```

CASには、最初からJetty（Webサーバー兼Java Servletコンテナ）がバンドルされたパッケージが含まれている。ファイルで言うとapiserver.jar。ほとんどのシナリオにおいてはこれでOKとのこと。

ちなみに、CASは、任意のJavaサーブレットコンテナ（Tomcat、JBoss、WebSphere、WebLogicなど）でホストすることもできるとのことで、したがってインストールパッケージには、これらJavaサーバーにデプロイするための`.war`ファイルも含まれている。

ここでは、シンプルにJettyバンドルのjarファイルを実行する。

## CAS ログインサービスの設定


CASの設定ファイルは`webapp/apiserver.xml`に配置されている。このファイルのloginServiceの箇所をみていると、次のようになっており、apiserverRealm.propertiesが、そのための設定ファイルであることがわかる
        
```xml
  <Set name="loginService">
    <New class="org.eclipse.jetty.security.HashLoginService">
      <Set name="name">APIServerRealm</Set>
      <Set name="config"><SystemProperty name="apiserver.home" default="."/>/apiserverRealm.properties</Set>
    </New>
  </Set>
```

`apiserverRealm.properties`には下記フォーマットで各ユーザーのパスワード、ロールを記入することになっている。

```
user : pass, <user role>
```

とりあえず、ここでは次のようにadminユーザーのパスワードを`admin`、ロールを`cdata_admin`で設定した。

```sh
admin:admin,cdata_admin
```

## CASの開始

それでは次のコマンドでCASを起動。これでCASが http://localhost:8080 で受付可能となる。

```sh
java -jar apiserver.jar
```

## 管理用APIへのアクセス

http://localhost:8080/ にアクセスすると次のようなログインページが表示される。

![cdata-login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/e58328e4-5ea4-33a4-8d30-b400c78a8769.png)

ここで先程設定したadminユーザー（パスワードadmin）でログインする。

![cdata-first-access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/37f19354-ee54-2973-d614-08603107555c.png)

上記のようにライセンスが無いことを示すWARNINGが表示される。


## ライセンス追加

`情報`タブをクリックしてライセンス追加する。ここでは「`30日の評価版をアクティベート`」から評価ライセンスを追加する。

![cdata-license.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/7e20fbbd-5988-f266-3f7b-f00c20fe6e05.png)

## DB設定追加

`設定`タブをクリックして `+接続を追加`をクリックしてREST API化したいデータベースを選択する。ここではMySQLを選択する。

![Screenshot 2023-12-13 at 14.43.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/3c1df7e0-fec6-f094-0f06-18ee354af753.png)

次のように、MyQLのドライバークラスを選択肢、サーバーホスト、ポート、ユーザー、パスワード、データベース名といった接続設定を追加する。

![mysql-conn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/0159a4c2-0e8b-b759-c5fb-b11b3c239f7b.png)

## リソースの追加

`設定` > `リソース` で `リソースを追加`を選択する。上記で設定したデータ接続を選択すると、次のように追加するリソースを選択するボックスが表示される

![resource-select.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/5974b1f3-5e04-3095-f829-e05db9692b9e.png)

ここでは、全てのDBのテーブルを選択してリソースとして登録する。

![resource-select-output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/7a7b9432-edff-f613-2e57-64bb6e68741c.png)

## ユーザー追加

REST API利用のためのユーザーを追加する。`設定` > `ユーザー` で `＋追加`を選択する。ここでは、次のように `user: myuser`として`変更を保存`すると、認証トークン( 例: U3tpuIc0OXe0DjqDg67g ) が発行される。REST APIリクエストでこの認証トークンが必要となる。

![add-mysql-user.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/b4667b68-5ada-0764-080d-3cab29246889.png)

これでMySQL連携のためのREST APIにアクセスする準備が完了。

# REST APIへのアクセス

それでは、REST APIにアクセスしてみる。`API`タブをクリックするとリソースアクセスのためのAPI一覧が表示される。ここではBooksテーブルリソースのためのAPIを選択。

![API-access-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/0b13a84c-53d5-e671-f9dc-f176eeedd42a.png)

ここではBooksのアイテム取得のためのAPIを選択してみる。すると次のように、そのAPIのドキュメントが表示される。見ての通り、サンプルリクエストのためのcURLやJavaScriptコードが表示される。また、アイテム取得内容を制御するためのリクエストパラメータの詳細も書かれている。

![Books-API-details.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/5e265254-4c7c-b169-e4b1-6dcd4353d458.png)

## curlコマンドでテスト

それでは、上記のAPIドキュメントページに表示されているcURLをそのままコピペしてテストしてみる。`MY_AUTH_TOKEN`のところは先程取得した認証トークンに置き換えが必要。

```sh
curl --header "x-cdata-authtoken: MY_AUTH_TOKEN" "http://localhost:8080/api.rsc/db_Books/"
```

実行すると下記のように無事結果が取得できた。ここではjqで出力結果のJSONを整形。

```sh
CDataAPIServer 
curl --header "x-cdata-authtoken: U3tpuIc0OXe0DjqDg66g" "http://localhost:8080/api.rsc/db_Books/" | jq

  
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   430    0   430    0     0   4575      0 --:--:-- --:--:-- --:--:--  4831
{
  "@odata.context": "http://localhost:8080/api.rsc/$metadata#db_Books",
  "value": [
    {
      "ArticleTitle": "Buy a paper",
      "Author": "James Elk",
      "Copyright": "1932-01-01"
    },
    {
      "ArticleTitle": "Buy a paper",
      "Author": "Mary G. Lee",
      "Copyright": "1932-01-01"
    },
    {
      "ArticleTitle": "How write a paper",
      "Author": "Tom M Ride",
      "Copyright": "1934-01-01"
    },
    {
      "ArticleTitle": "Sell a paper",
      "Author": "Henry S. Thompson",
      "Copyright": "1966-01-01"
    }
  ]
}
```

## Postmanでテスト

繰り返し実行、複雑な実行するならば、cURLではツラいですよね。というわけで、同じことを本命Postmanでテストしてみる。

Postmanでは`インポート`を選択すると下記のようなインポート用のボックスが表示される。ここのリクエストバーにcURLコマンドをそのままペーストするだけでAPIリクエスト設定がPostmanにインポートできる。

![Screenshot 2023-12-13 at 16.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/669b9cc7-ae63-5817-2827-367213de5f91.png)

`送信`ボタンを押して、無事同じ結果が得られた。

![Screenshot 2023-12-13 at 16.27.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/d82a6048-d0d5-e473-05f2-600841ee97c1.png)

ここでは、取得系APIしか解説していないが、当然、更新系POST、PUT、DELETEなリクエストも問題なく実行できた。超絶に簡単である。

## Postmanのプレリクエストスクリプトでデータベースのデータを取得する例

Postmanにおける外部データベースのデータ活用例として、APIリクエスト送信時に外部データベースのデータを活用してリクエスト送信データを変えたいケースがあるかと思う。
そういう場合、PostmanではAPIリクエスト送信前に実行されるプレリクエストスクリプトを活用するのが一般的である。プレリクエストスクリプトにて外部データベースからデータを取得し、変数を通じてリクエスト送信データをセットすることでリクエスト送信データを変えることができる。

以下、今回作成したREST APIを通じてBookアイテムを取得するスニペットです。取得したデータから必要な部分をご自分で取り出してリクエスト送信内容を変えてみてください。

```js
const getRequest = {
  url: 'http://localhost:8080/api.rsc/db_Books/',
  method: 'GET',
  header: {
    'Content-Type': 'application/json',
    'x-cdata-authtoken': '<認証トークン>'
  }
}
pm.sendRequest(getRequest, function (err, response) {
    console.log(response.json());
});

```

以下、スニペットをプレリクエストスクリプトで実行した結果をコンソール表示しているイメージ。

![Screenshot 2023-12-13 at 23.19.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/e7af2324-17a7-64ce-0abe-f3a913741dcd.png)



# おわりに

CASを活用することで、データベースを超絶簡単にREST API化できることが分かった。
先述の通り、Postmanではその制約上、外部のデータソースと連携をするにはそのデータソース操作のためのAPIを経由する必要があったりするわけだが、CASを使えばそれが比較的簡単にできそうだ。
本質的には、その取得したデータをどう活用していくかが重要だったりするわけだが、とにかく外部データソース連携のための1つの選択肢としてCASは良さそう。