まだ実装してない API を Prism でモックするとフロントエンド開発が少しはかどった

## 導入前の課題
Web サービスの開発でフロントとバックエンドを並行してすすめてるけど、まだ API が実装できてなくてフロントの開発に困ったことはありませんか？
以前、API 定義は決まったがバックエンドの開発が 1-2 週間遅れそうとわかり、フロントの開発を先んじで行いたという状況になったので、その際に行った解決策をまとめてみました。

結論はタイトルにある通り「OpenAPI のスキーマ定義ファイル(以下 OpenAPI 定義ファイル)があるなら、Prism という OpenAPI 定義ファイルをもとに API モックサーバをたてられるツールが便利」です。

## 状況
ざっくり以下の様な状況でした

- フロントエンドとバックエンドを分けて、別リポジトリで開発
- フロントは Nuxt、TypeScript の SPA
- クライアントライブラリは Axios を利用
- API の仕様は OpenAPI に準拠し、YAML で書いて管理
- ローカルでの開発時にはテストデータを流し込んだバックエンドサービスを立ち上げて開発
- 新しい API 定義は決まったがバックエンドの開発が 1-2 週間遅れそうだがフロントは先んじて実装したい

## 解決策
API をモックするにあたり、いくつかのライブラリの候補がありましたが OpenAPI 定義ファイルがあったので Prism を使用しました

### [Prism](https://stoplight.io/open-source/prism/)
> Prism, an Open-Source HTTP Mock & Proxy Server
> Accelerate API development with realistic mock servers, powered by OpenAPI documents.

翻訳すると「Prism、オープンソースのHTTPモック＆プロキシサーバ。OpenAPIドキュメントを利用したリアルなモックサーバーでAPI開発を加速します」

StopLight 社が提供する OSS の API モックサーバーライブラリです。

- 導入が用意で API モックサーバーを簡単に立ち上げることができる
- OpenAPI 定義ファイルをもとにするので API モックサーバーと仕様のズレを気にしなくてよい
- 動的なレスポンスの生成が容易
- フロントのコード修正がほとんどない(エンドポイント周りは修正が必要なケースは実装次第である)
など OpenAPI 定義ファイルを中心としつつ、手軽に導入できることから今回は採用しました。

### Prism の導入
まずは Prism を導入していきます。Docker イメージもありますが今回は yarn を利用して環境を構築します

```bash
$ yarn add -D @stoplight/prism-cli
$ yarn prism mock --version # 4.1.2
```

公式の例で使用されているOpenAPI ｖ3 の[サンプルスキーマ](https://github.com/stoplightio/prism/blob/master/examples/petstore.oas3.yaml)を使用します。

本記事では `sample.yaml` として配置しました。
モックサーバー起動用のスクリプトを追加しておきます

```json:package.json
{
  ...
  "scripts": {
    "serve": "prism mock sample.yaml",
    "serve:dynamic": "prism mock -d sample.yaml"
  },
  "devDependencies": {
    "@stoplight/prism-cli": "^4.1.2"
  }
}
```

なにもオプションを指定しなければ、スキーマの Example に設定した値が返却されます。

また `-d` オプションを付与することでレスポンスを動的に生成してくれます.
このときスキーマの中で値の範囲や、パターンを指定しているとその制限にあった値を生成してくれます。
number 型の minimum, maximum や、string の minLength, maxLength, pattaern などです。

動的レスポンスを生成する設定で API モックサーバーを起動します

```bash
# デフォルトだと port 4010 でモックサーバーが起動します。-p オプションで指定も可能です。
$ yarn serve:dynamic
```


本記事では認証せずに使用できる `/no_auth/pets/:id` の Path でレスポンスを見てみます。

```bash
$ curl -i 'http://127.0.0.1:4010/no_auth/pets/1234'                                                                                                                      
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: *
Content-type: application/json
Content-Length: 1716
Date: Thu, 03 Dec 2020 09:29:59 GMT
Connection: keep-alive

{"name":"in ex minim","photoUrls":[....

# 同じ Path でも動的に生成されるのでレスポンスの内容が変わっています
$ curl -i 'http://127.0.0.1:4010/no_auth/pets/1234'                                                                                                                      
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: *
Content-type: application/json
Content-Length: 1648
Date: Thu, 03 Dec 2020 09:30:45 GMT
Connection: keep-alive

{"name":"anim consequat","photoUrls":[...
```

name というクエリパラメータが必須な `/no_auth/pets` のレスポンスはどうなるか見てみます。

```bash
# 必須の name がないので 422 がかえってきてます
$ curl -i 'http://127.0.0.1:4010/no_auth/pets'                                                                                                                           
HTTP/1.1 422 Unprocessable Entity
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: *
content-type: application/problem+json
Content-Length: 363
Date: Thu, 03 Dec 2020 09:33:21 GMT
Connection: keep-alive

{"type":"https://stoplight.io/prism/errors#UNPROCESSABLE_ENTITY","title":"Invalid request","status":422,"detail":"Your request is not valid and no HTTP validation response was found in the spec, so Prism is generating this error for you.","validation":[{"location":["query"],"severity":"Error","code":"required","message":"should have required property 'name'"}]}%       

# name が存在する正しいリクエストなので 200 がかえってきてます
$ curl -i 'http://127.0.0.1:4010/no_auth/pets?name=abcde'                                                                                                                
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: *
Content-type: application/json
Content-Length: 31983
Date: Thu, 03 Dec 2020 09:35:56 GMT
Connection: keep-alive

[{"name":"eu elit","photoUrls":[...

```

このように、OpenAPI 定義ファイルさえあれば簡単に定義にそった API モックサーバーが建てられます。

CORS に関してデフォルトでは

- プリフライトリクエストには 204 を返す(単純ではないリクエスト時に発生)
- すべてのOriginを許可する
という[設定](https://meta.stoplight.io/docs/prism/docs/guides/cors.md)です。

CORS に関しては以前書いた[記事](https://qiita.com/kutakutat/items/afa6f796a9d4e4ffdaa4)を参考として貼っておきます。


## その他検討したライブラリ
その他候補に上がっていたのは以下のようなライブラリでした。

### [json-server](https://github.com/typicode/json-server)
json でレスポンスを書くだけのシンプルな API モックサーバーのためのライブラリ。
`db.json` を使用してレスポンスを書けば動作するお手軽さ。
ただし、ネストした Path へのリクエスト(ex. `/posts/:id/comments`) を書くには `route.json` を使用する必要があります。より柔軟に行いたいときには

```js

const jsonServer = require('json-server')
```

とすると `jsonServer.create()` で Express のインスタンスが帰ってくるのでよしなに middlware を噛ませたり、Error レスポンスを定義できたりします。


### [MirageJS](https://miragejs.com/)
API モックサーバーではなく、ブラウザ上で application が行う XMLHttpRequest, fetch リクエストを傍受してレスポンスをモックしてくれるライブラリ。
調査不足感が否めないですが、[公式の例で使用されている以下のようなモジュール](https://github.com/miragejs/examples/blob/master/vue-axios-test-utils/src/server.js)を使用すれば
 Vue.js だったら `App.vue`,  Nuxt.js だったらプラグインとして読み込めば XMLHttpRequest, fetchを傍受してレスポンスをモックしてくれるそうです。

```js
import { createServer, Model } from "miragejs";

export function makeServer({ environment = "development" } = {}) {
  let server = createServer({
    environment,

    models: {
      user: Model,
    },

    seeds(server) {
      server.create("user", { name: "Bob" });
      server.create("user", { name: "Alice" });
    },

    routes() {
      this.namespace = "api";

      this.get("/users", (schema) => {
        return schema.users.all();
      });
    },
  });

  return server;
}
```


## まとめ
OpenAPI 定義ファイルがあるなら、Prism という OpenAPI 定義ファイルをもとに API モックサーバをたてられるツールが便利でした。
以下のような気になる点もありますが、個人的には、非常に手軽で OpenAPI 定義ファイルを Single Truth とできるので好みでした。

- バックエンドサービスも立ち上げながら、API モックサーバ0も使う場合、モックを利用したいリクエストだけモックのエンドポイントを向くようにする必要がある。
- 動的に発生させたレスポンスの制御には限界がある。ex. 現在の位置から 1 km 圏内の建物の緯度経度をほしくても、スキーマでは `-90 ~ 90` と制限してたら世界中に散らばった緯度経度情報が返ってくる

また MirageJS をちゃんと試してみたいなと思ってます。

## 参考

- [Prism公式](https://meta.stoplight.io/docs/prism/README.md)
- [本当に使ってよかったOpenAPI (Swagger) ツール](https://future-architect.github.io/articles/20191008/)