Laravelで最速でOpenAPI(Swagger)ドキュメントを自動生成する
TL;DR
- dedoc/Scrambleを使えば超ラクにOpenAPIを作成できるよ
- あまり細かな記述はできないけど、カスタムやアノテーション記述など自由度はある
- 全くドキュメントが更新されていない既存プロジェクトに突っ込むのもアリ
はじめに
ドキュメントってほんとに難しいですよね
なにが難しいかって高確率でソースと乖離するところ
最初のうちはドキュメント更新も頑張るんだけど、そのうちソースコードと乖離が大きくなってきてソースコードが仕様書みたいになっているプロジェクトに遭遇したことはありませんか?
「あっそれ仕様書と違ってここは◯◯が返って、ここには✕✕が追加されてて、それで、、、」
こういうやつ
私は何度もあります、、、
これを回避するためにドキュメントの自動生成に踏み切ろうかと思いましたが、なかなか自動化のための記述量が多かったり、学習コストが高いものが多く、なかなか導入に踏み切れなかった中、Scrambleという超イケてるライブラリを見つけたので共有の記事になります。
各種ライブラリ比較
OpenAPI自動生成となると色んなライブラリがあると思いますが、まずはじめにてっとり早く比較をします。
同じものを作った場合のサンプルを3つ
LoginというAPIのドキュメントを作成してみます。
-
L5-Swagger
有名なライブラリですね、これで試しに書いてみます。
/**
* @OA\Tag(
* name="auth",
* description="ログイン"
* )
*/
class Login extends Controller
{
/**
*
* @OA\Post(
* path="/login",
* operationId="authLogin",
* tags={"auth"},
* summary="ログイン",
* description="ユーザーのログイン処理を行い、ユーザー情報とAPIトークンを返却します。",
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* required={"email", "password"},
* @OA\Property(property="email", type="string", example="user@example.com"),
* @OA\Property(property="password", type="string", example="string")
* )
* ),
* @OA\Response(
* response=200,
* description="Success",
* @OA\JsonContent(ref="#/components/schemas/UserResource")
* ),
* @OA\Response(
* response=401,
* description="Authentication error",
* @OA\JsonContent(
* @OA\Property(property="message", type="string", example="Unauthenticated.")
* )
* ),
* @OA\Response(
* response=403,
* description="Authorization error",
* @OA\JsonContent(
* @OA\Property(property="message", type="string", example="Forbidden.")
* )
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(
* @OA\Property(property="message", type="string", example="The given data was invalid."),
* @OA\Property(
* property="errors",
* type="object",
* @OA\Property(
* property="email",
* type="array",
* @OA\Items(type="string", example="メールアドレスは必須です。")
* ),
* @OA\Property(
* property="password",
* type="array",
* @OA\Items(type="string", example="パスワードは必須です。")
* )
* )
* )
* )
* )
*/
public function __invoke(LoginRequest $request): UserResource
{
// ...
return (new UserResource($user))
->additional(['api_token' => $token]);
}
}
うーん、、、ちょっと思ってたのと違う感
自分は導入に至りませんでしたが、利用されている方は多いと思います。
-
swagger-php
こちらはGitHubのStar数的にも一番使われている数が多いのではないでしょうか。
サンプルを記載してみます
#[OA\Tag(
name: 'auth',
description: 'ログイン'
)]
class Login extends Controller
{
/**
* @param LoginRequest $request
* @return UserResource
*/
#[OA\Post(
path: '/login',
operationId: 'authLogin',
tags: ['auth'],
summary: 'ログイン',
description: 'ユーザーのログイン処理を行い、ユーザー情報とAPIトークンを返却します。',
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ['email', 'password'],
properties: [
new OA\Property(property: 'email', type: 'string', example: 'user@example.com'),
new OA\Property(property: 'password', type: 'string', example: 'string')
]
)
),
responses: [
new OA\Response(
response: 200,
description: 'Success',
content: new OA\JsonContent(ref: '#/components/schemas/UserResource')
),
new OA\Property(property: 'api_token', type: 'string', example: '1|abcdefghijklmnopqrstuvwxyz')
new OA\Response(
response: 401,
description: 'Authentication error',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.')
]
)
),
new OA\Response(
response: 403,
description: 'Authorization error',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'message', type: 'string', example: 'Forbidden.')
]
)
),
new OA\Response(
response: 422,
description: 'Validation error',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'message', type: 'string', example: 'The given data was invalid.'),
new OA\Property(
property: 'errors',
type: 'object',
properties: [
new OA\Property(
property: 'email',
type: 'array',
items: new OA\Items(type: 'string', example: 'メールアドレスは必須です。')
),
new OA\Property(
property: 'password',
type: 'array',
items: new OA\Items(type: 'string', example: 'パスワードは必須です。')
)
]
)
]
)
)
]
)]
public function __invoke(LoginRequest $request): UserResource
{
// ...
return (new UserResource($user))
->additional(['api_token' => $token]);
}
}
Attributeで書けるのでタイポはこちらのほうが気付きやすそうですね。
これは結構いい感じです。
そして今回紹介するdedoc/Scramble
/**
* @tags Auth
*/
class Login extends Controller
{
/**
* ログイン
*
* ユーザーのログイン処理を行い、ユーザー情報とAPIトークンを返却します。
*
* @unauthenticated
*/
public function __invoke(LoginRequest $request): UserResource
{
// ...
return (new UserResource($user))
->additional(['api_token' => $token]);
}
}
...!?
ほぼなんも書いてねぇ!!
そう、このdedoc/Scrambleは型などを推論して自動出力することに特化したライブラリになっています。
そのため、細かな記述はなくともそこそこのドキュメントを作ってくれます
上に書いたコードで実際に作られたドキュメントがこちら
Modelに$casts
指定したEnumやResource内の->whenLoaded
したコレクションまで読み取ってくれています。
更になにも書いていないのに422など発生し得るLaravelのデフォルト例外のレスポンスも勝手に書いてくれます。
additionalとかも読み取ってくれています。すごい。
ただし、Exampleの値は"string"とかばっかりになってちょっと微妙かも
これはPHPDocに@example
を書けば好きなサンプルを書けるようになるらしい
これみた感じ次の0.9.1アップデートで@example
は実装されそうです。
2022-10-09追記
@example
が実装されたようです
例:
class LocationsController
{
public function update(Request $request, Location $location)
{
$request->validate([
/**
* The location coordinates.
* @var array{lat: float, long: float}
* @example {"lat": 50.450001, "long": 30.523333}
*/
'coordinates' => 'array',
]);
}
}
こんな感じで書けばいい感じにexampleがOpenAPIに反映されます
導入
composer require dedoc/scramble
一応コンフィグもいじれるようにしたほうがいいかも
php artisan vendor:publish --provider="Dedoc\Scramble\ScrambleServiceProvider" --tag="scramble-config"
これで
にアクセスするとドキュメントが見れるようになります(URLは適時変更してください)できること
Requestの自動ドキュメント
FormRequestの自動ドキュメント化
または$request->validate
、Validator::make
しているコントローラーのRequestの自動ドキュメント化
対応しているルール一覧
- required
- string
- bool,boolean
- number
- int,integer
- array
- in,Rule::in
- nullable
- uuid
- exists(属性名がintid*_id)
- min(数値型のみ)
- max(数値型のみ)
- Enum
- confirmed
公式ドキュメントに詳しく書いている
レスポンスの自動ドキュメント
謎の技術を使ってかなり高い精度で型の推論をしてくれている
うまく推論が効かない場合はPHPDocを書けば良いみたいです
下記の図の1,2,3の優先度でドキュメントに反映されるそうです
/**
* @response TodoItemResource // 1 - PhpDoc
*/
public function update(Request $request, TodoItem $item): TodoItemResource // 2 - typehint
{
return new TodoItemResource($item); // 3 - code inference
}
対応しているレスポンス一覧
- API Resource
- API Resource Collection (匿名コレクションと専用クラスの両方)
- response()
- ResponseFactory
- response()->make(...)
- response()->json(...)
- response()->noContent()
- JsonResponse、Response
- Model
- 単純型:配列、文字列、数値など
- LengthAwarePaginator(PHPDocで記載の必要あり)
- デフォルトのLaravel例外
公式ドキュメントに詳しく書いている
コントローラーのメソッドのPHPDocがsummaryとdescriptionになる
/**
* ログイン
*
* ユーザーのログイン処理を行い、ユーザー情報とAPIトークンを返却します。
*
* @unauthenticated
*/
この部分ですね、行数でチェックしているようで、一番上の行がsummary、スペース空けて次のがdescriptionのようです。
@unauthenticated
の部分は認証が不要なルートに書くphpdocでScrambleに認証が不要なことを伝えています。
@tags
を使う
グルーピングに今回はシングルアクションコントローラのため、グルーピングするためにPHPDocに記述しましたが
コントローラー単位でグルーピングする場合、特に記述の必要はありません
Sanctum
APIのトークン認証なんかにも対応しています
デフォルトのSanctumのcreateToken()
とかで作るやつの場合、AppServiceProvider
に下記のように記述すると良いです
use Dedoc\Scramble\Scramble;
use Dedoc\Scramble\Support\Generator\OpenApi;
use Dedoc\Scramble\Support\Generator\SecurityScheme;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Scramble::extendOpenApi(function (OpenApi $openApi) {
$openApi->secure(
SecurityScheme::http('bearer', 'token')
);
});
}
画像のようにリクエストに認証がつきます
詳しくは公式ドキュメント
@throwsをどうも読み取ってくれるらしい
->findOrFail()
はどうも404の自動推論が効かなかったので、コントローラーのPHPDocに
@throws ModelNotFoundException
を記載してみたところ、404のレスポンスが自動生成された。
401については自動生成されなかったのでこのページを読みながらAuthenticationException
があったら401を追加するコードを自前で追加した。
※追記、どうもソースコード見た感じではAuthenticationException
を自動レスポンスするようなコードが見受けられた。最新版では対応されているかも。
→やってみたけど401じゃなくて403になる?バグかも?
ドキュメント生成にコマンドがいらない
ソースコードの更新とともにリアルタイムで反映されていきます
jsonのコマンドによる生成もできるようです
その他
開発が活発に行われている
Laravel11リリースと同時に速攻で対応していました。ありがたい。
またどんどん良い機能が追加されていきそうですね。
spatie/laravel-data対応
Scramble Proに課金する必要があるが、かなり有用なライブラリのため、組み合わせると強いかも。
laravel-dataでRequestやResourceに型付けしつつ、OpenAPIも自動生成できるようになる
料金はこんな感じだった
興味がある人は公式見てみることをおすすめします。
最後に
また導入したばかりであまり使いこなせていないので
また追記していきます
皆さんもよきScrambleライフを!!
Discussion