Prism mock に BasePath を適用したい人生だった
あらすじ
OpenAPIの開発環境を構築中にPrismでモックサーバーを立ち上げようとしたところ、BasePathを適用することができなかった。つまり /api/v1/xxxx
のようなエンドポイントを設定したかったのですが、どうやら OpenAPI 3.0 では実現することが難しいようでした。
なぜなのか、どうしたらよいのか。
BasePath について
まず、BasePathという概念について。
APIのURLに /api/v1
のような接頭パスを付与することがよくある。管理上APIのURLは /api
のようなパスに束ねておきたいし、後方互換性などの理由からバージョニングのため /v1
のようなパスから配信したりします。
当然このパスはすべてのAPIエンドポイントに共通して付与されるものだから、一々 path
に含めておくのは無駄だし、共通設定として記載しておきたい。OpenAPI 3.0 の仕様では、 servers.url
の指定がそれにあたるようですが、Prismによるモックサーバーではその指定は考慮されません。
servers:
- url: /api/v1
path:
/hello:
get:
などのように指定した場合、期待したいリクエストは GET http://localhost:4010/api/v1/hello
なのだけど、これは404になる。 http://localhost:4010/hello
なら取得できる。
BasePath に関するディスカッション
cf) option to specify basepath in prism mock server
ここでPrismのモックサーバーにBasePathを適用するための方法について議論が交わされていました。ものすごくざっくりと紹介します。
ユーザー側の主張
prism mock
に--basepath /api/v1
のようなオプションを追加してモックサーバーのURLにBasePathを適用できるようにしてほしい。
開発側の主張
そのような機能を実装する予定はない。
FAQ に記載してあるように、ユーザー間で混乱が生じるからだ。
ユーザー側の主張
オプションで追加するのだからデフォルトの挙動は変わらないのに、誰が混乱するっていうんだい?
雰囲気で読んでみたが多分こんな感じ。とりあえず、そういった機能は実装するつもりはなさそうだというのが読み取れました。
結局お前が頼りだ、Nginx
上のディスカッションでも一部のエンジニアが触れているように、結局Nginxでproxyしてなんとかするというのが現実的な解決策です。
例えばの前提として、フロントエンドの開発環境が :3000
で、Prismのモックサーバーは :4010
で立ち上がっていて、API の BasePath は /api/v1
を想定する。これらを束ねて同じオリジン( localhost:4000
)から配信出来るようにnginxをセットアップする。
- フロントエンドDevサーバー(Next.js)
:3000
- Prismモックサーバー
:4010
- nginx
:4000
まずはnginxの設定ファイルを書いてプロジェクトルートにでも置いておく。 nginx自体はdockerで立ち上げるので、 proxy_pass
のURLにはホストOSを指す host.docker.internal
を使う。
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://host.docker.internal:3000/;
}
location /api/v1/ {
proxy_pass http://host.docker.internal:4010/;
}
}
nginxの起動はnpmスクリプトで行う。
"scripts": {
"dev": "next dev -H 0.0.0.0",
"api:mock": "prism mock ./openapi/api.yaml",
"nginx": "docker run --rm -v ${PWD}/nginx.conf:/etc/nginx/conf.d/default.conf -p 4000:80 nginx"
},
next dev
に -H 0.0.0.0
を渡しているのは、WSL環境で起動した場合にこれをしないとアクセスできなかったからで、環境によっては不要かもしれません。
あとは dev
と api:mock
を起動してから nginx
を起動すれば localhost:4000
でプレビューできるはず。Next.jsアプリ側からAPIにアクセスするには /api/v1/hello
にリクエストを送る。
const res = await fetch("/api/v1/hello");
おわりに
一応無事開発環境は整ったわけですが、2つ起動すればいいはずのところを3つのスクリプトを起動しなければならないのはモヤるポイントではあります。まさにこのモヤポイントが上のディスカッションで盛んに議論されていたわけで。
(とはいえオリジン揃えたりするなら結局nginxの力は借りなきゃいけないのか?)
ということでよりスマートな解決方法をご存知の方がいらっしゃったら是非教えてください。
Discussion