Laravel x OpenAPIによるスキーマ駆動開発の一例
弊社ではPHPフレームワークのLaravelを利用したREST APIの開発事例が多いのですが、最近はスキーマ駆動での開発が主流になってきています。
LaravelのREST API開発ではLaravel OpenAPIを活用した事例が多くある印象ですが、弊社では当該ライブラリを利用しないスキーマ駆動開発で手応えを感じ始めているため、本記事では弊社で行っている方法の一部をご紹介させていただきます。
なお、あくまで「こういった方法もあります」というご紹介であり、他の方法を否定する意図は一切ございませんので、その点はご了承ください。
スキーマ駆動開発とは
スキーマ駆動開発については多くの情報がありますので詳細は割愛させていただきますが、REST APIの文脈におけるスキーマ駆動開発は「はじめにOpenAPI仕様書(OAS)のスキーマを決定し、そのスキーマに基づいてバックエンド、フロントエンドを構築する開発」という理解が最も一般的だと考えています。
本記事でも上記を前提とさせていただいております。
開発の流れ
本章から具体的な方法をご紹介させていただきます。
まず、全体の流れは以下の図の通りです。
詳細について次章以降で詳細をご紹介させていただきます。
(本記事ではLaravelに主眼を置いてご紹介させていただく為、フロントエンドに関する情報は最低限に留めています)
スキーマの生成
まずは駆動元となるスキーマをStoplight Studioというツールで生成します。
スキーマの生成は
- 生のymlをそのまま書く
- PHPDocで書く
- Swagger Editorで書く
などいくつか手段があると思いますが、Stoplight Studioの
- 直接ymlを書かずGUIで書ける
- ローカルのスキーマファイルを直接更新できる
- ツールの見た目
といった点に魅力を感じ、採用しています。
実際の利用方法も少しだけご紹介させていただきます。
まずは公式サイトからアプリをダウンロードします。
アプリを起動すると以下のような画面が表示されます。
利用方法は
- 新規にプロジェクトを作成し、ゼロから生成
- Gitから読み込み
- 既存のファイル(ローカルのリポジトリ上にあるymlファイル)から読み込み
の3つがありますが、弊社ではLaravelアプリケーション内にschema.ymlというファイルを配置し、そちらを直接読み込んで利用しています。
読み込み後はGUIに従って設定していきます。
設定したサンプルがこちらです。
右上にあるCodeでGUI⇔ymlの切り替えが可能ですが、弊社内で利用している限りではymlを直接更新せずにすべてGUI上で設定が完結できています。
また、上記サンプルでは右上にWarningの表示がありますが、設定した内容がOASの仕様から逸脱していないかチェックもしてくれます。
詳細な利用方法はOASそのものの話も絡んでくるため、本記事では割愛させていただきます。
なお、弊社では
- Stoplightでスキーマファイルを生成、更新
- Pull Requestを作成
- 関係者間でレビュー
- マージ後、バックエンド、フロントエンド双方で開発
という流れを取っています。
API実装→テスト
上記の通りスキーマ決定後、バックエンド側で実際にAPI開発を行いますが、開発そのもので特殊なことは行っていないため割愛させていただき、本章ではその後のテストについてご紹介させていただきます。
スキーマ駆動開発での懸念として真っ先に挙がる事項に「**生成したスキーマと実態(実際のコード)に乖離がないか」**があると思います。
そこで、弊社では乖離を防ぐためにSpectatorというライブラリを利用したテストを行っています。
Spectatorについて、READMEには
Spectator provides light-weight OpenAPI testing tools you can use within your existing Laravel test suite.
Write tests that verify your API spec doesn’t drift from your implementation.
(Google翻訳)
Spectator は、既存の Laravel テスト スイート内で使用できる軽量の OpenAPI テスト ツールを提供します。 API 仕様が実装からずれていないことを確認するテストを作成します。
と記載されています。
翻訳の通り「スキーマと実装に乖離がないかテストする」ためのライブラリです。
実際の利用方法については、弊社メンバーが以前執筆した記事がございますので、リンクの紹介のみに留めさせていただきます。
LaravelでAPI仕様書(OAS)と実装の乖離を簡単に防ぐならSpectatorがおすすめ! – Qiita
動作確認
テストコードで仕様書との乖離がないかのチェックは行いますが、実際にAPIを実行して動作確認も行っています。
弊社では後述する理由によりSwagger UIで動作確認しています。
具体的には、LaravelアプリケーションにL5-Swaggerというライブラリを導入し、当該ライブラリを利用してLaravelアプリケーション内でSwagger UIを起動しています。
実際の利用方法も少しだけご紹介させていただきます。
まずはREADMEにしたがって以下を実行します。
# Laravel7以上の場合
## Install
composer require "darkaonline/l5-swagger"
## Configration
php artisan vendor:publish --provider "L5Swagger\\L5SwaggerServiceProvider"
php artisan l5-swagger:generate
続いて、Swagger UI用の設定を行います。
/*
* public/docs/schema.ymlを読み込み、
* http(s)://xxx/docでアクセスする例
*/
return [
// ...省略
'documentations' => [
'default' => [
// ...省略
'routes' => [
/*
* Swagger UI用のURL定義
*/
'api' => 'doc',
],
'paths' => [
// ...省略
/*
* 読み込むスキーマファイル(OAS)
*/
'docs_yaml' => 'schema.yml',
// ...省略
],
'defaults' => [
// ...省略
'routes' => [
/*
* スキーマファイルを配置している場所(public配下)
* ※本番環境などでは閲覧権限制御が必要です
*/
'docs' => 'docs',
// ...省略
こちらを設定してLaravelアプリケーションの/doc(例: http://localhost/doc)にアクセスすればSwagger UIが表示されます。
なお、本章の冒頭で
弊社では後述する理由により
と記載しましたが、LaravelによるREST APIの認証にLaravel Sanctumを利用している場合、
- 認証完了後、X-XSRF-TOKENがローカルに保存される
- 認証が必要なAPIではリクエストヘッダーにX-XSRF-TOKENを付加する必要がある
の2点から、L5-Swaggerによって生成されるSwagger UIのコードに以下の変更を加えることでストレスレスに動作確認をしています。
// requestInterceptor: function(request) {
// request.headers['X-CSRF-TOKEN'] = '{{ csrf_token() }}';
// return request;
// },
requestInterceptor: async function(request) {
if (!document.cookie.includes('XSRF-TOKEN')) {
await fetch('/sanctum/csrf-cookie');
}
request.headers['X-XSRF-TOKEN'] = unescape(document.cookie
.split('; ')
.find(row => row.startsWith('XSRF-TOKEN'))
.split('=')[1]);
request.headers['X-Requested-With'] = 'XMLHttpRequest';
return request;
},
TypeScript用の型定義ファイルを生成
フロントエンドでTypeScriptを利用する場合、生成したスキーマから型情報を自動生成するとフロントエンドでも非常にスムーズにAPI実行が可能になります。
OASからTypeScriptの型定義ファイルを自動生成するライブラリはいくつか選択肢があると思いますが、弊社ではopenapi-typescriptを利用しています。
当該ライブラリはnpxで利用可能なため、
# 実行コマンド
npx openapi-typescript 【Laravelアプリケーションのスキーマファイルパス】 --output 【出力するファイル名】
# 実例
npx openapi-typescript ~/laravel-app/public/docs/schema.yml --output src/types/generated/apiSchema.ts
のみで実行可能です。
以下が出力されるファイルの一例です。
なお、上記型ファイルをそのまま利用することも可能ですが、記述量がかなり多くなってしまうため、型ファイルに合わせたラッパーを準備することで簡単に扱うことができます。
例として、Axiosで扱う場合には以下記事で紹介されている方法を利用すると非常に便利です。
【OpenAPI】APIスキーマから勝手に型がつくaxiosを作って幸せになる【openapi-typescript】
実装方法は上記記事をご参照いただければと思いますので、本記事では実際に利用した場合の実例をご紹介させていただきます。
まずはリクエスト情報を入力する際の実例です。
上記GIFの通り、
- urlには対象となるAPIエンドポイントがサジェストされ、
- methodには選択したurlで指定可能なmethodのみがサジェストされ、
- data(Request Body)には対象のリクエストボディパラメータがサジェストされる
という動きをしてくれます。
続いてレスポンス情報を入力する際の実例です。
上記GIFの通り、
- 対象のエンドポイントのレスポンス情報がサジェストされる
という動きをしてくれます。
これらにより、
- 存在しないエンドポイントにアクセスする
- 必要なリクエストボディを間違える
- レスポンス内のプロパティをtypoする
などのエラーをTypeScript側で弾いてくれるため、フロントエンドから非常にストレスレスにAPI実行が可能になります。
おわりに
今回はLaravelによるスキーマ駆動開発の一例をご紹介させていただきました。
いずれの章も概要のみで詳細まではお伝えできておりませんが、Laravelのスキーマ駆動開発にお悩みの方の一助になればと思います。
弊社では、今回ご紹介させていただいた技術などを用いて、お客様と伴走しながらシステムを素早く作り、効率良く検証を進める「レンタル内製チーム」というサービスを展開しています。
ご相談は無料で承りますので、お気軽にご連絡ください。