コードファーストでOpenAPIを爆速で定義できるFastAPIを使おう!
テックブログ担当させていただくことになりましたTakです。業務ではJavaのサーバーサイド実装が主なのですが個人的にはPythonが好きです。私の業務では使っていませんがPythonについての記事になります。
OpenAPIとは?
REST APIを開発する際にAPI仕様書をOpenAPI Specificationで用意するのは一般的です。「OpenAPIとはなにか」が分からない場合はググってもらうとたくさん解説が見られるのでそちらをご覧ください。
簡単に言えば、「あるREST APIは、このようなパスで、このようなリクエストを受け付けてこのようなレスポンスを返すよ」というスキーマ定義です。
GraphQLのスキーマ定義についてはこの記事では書いてないです。
文章で書かれたAPIドキュメントは人間が読むだけのものになりますが、OpenAPIのスキーマ定義はツールに読み込ませて次のようなことができます。
APIリファレンスの文書を生成できます
APIクライアントを自動生成できます
API実装のスケルトンを自動生成できます
APIスキーマが決定すれば、APIクライアント(主にフロントエンド)と、API実装(サーバーサイド)が並行開発できます
この記事はOpenAPIについての記事ではないのでこの辺にしておきますが、REST API開発では欠かせない設計定義になります。
OpenAPIの定義ファイルをいつ作成するか?
OpenAPIの定義ファイル、いわゆるyamlやjsonファイルをいつ、どのように作成するのかというのはよく議論になります。
OpenAPIで仕様を決めてから実装を作成・生成するのがスキーマファーストというやり方です。スキーマファイルからコードのスケルトンを自動生成できたりします。
一方、コード書いてコードに合う形でOpenAPIのスキーマファイルを自動生成するのがコードファーストというやり方です。
普通は、「設計してから実装を作るのだから、スキーマ→コードのようにスキーマファーストにするのが当然だろう。いちいちコード書いていたら時間がかかるし、スキーマ定義を書くほうが楽だろう」と考えるでしょう。私も最初はそうでした。
ですが、PythonサーバーサイドフレームワークのFastAPIを使ってみてからは考えがガラリと変わりました。コードファーストでOpenAPIのスキーマファイルを作るほうが楽で正確になりました。
スキーマを作っている感覚はなく、コード書いていればスキーマが用意されているという気持ちです。
スキーマファーストのデメリット
スキーマファーストの場合のデメリットはこんなことがありました。
OpenAPIのyamlとコードの乖離
実際に動作するのはコードです。しかしOpenAPIで定義したとおりにコードが記述されている保証はどこにあるのでしょうか。
これはスキーマファイルからの自動生成コードを使うことで解決できます。
ですが、スキーマファイルからの自動生成コードを常に使い続けなければなりません。自動生成されたコードに直接手を入れてはなりません。なにか自動生成コードのほうに修正を入れたい場合は、生成する処理に干渉する仕組みが必要です。
自動生成コードはスキーマファイルからのビルドされた生成物なので本来はスキーマファイルのみをソースコードリポジトリに登録し、生成物である自動生成コードは登録しないぐらいの徹底さがほしいところです。
実際にはいろいろな手間もあるのでリポジトリ登録されていることが多かったです。そうなるとスキーマファイルのバージョンと自動生成コードのバージョンは常に揃っているのか? という不安が生まれてきます。また実際に動いているプロダクトがこのスキーマから生成されたもので合っているのか? という不安もあります。
自動生成されたコードが汚い
自動生成されたコードはテンプレートエンジンなどで作られたもので、あまりきれいじゃないコードだったりします。これはまあ中間生成物だと思って割り切って使いましょう。
この生成されたコードがlintに引っかかることもあるので適切に除外設定をしてlintエラーが放置されないようにしましょう。
スキーマ定義をキレイに書けない
定義ファイルなのですが、きちんと構造化されていなければならず、人によってはコピー&ペーストで定義されていたりして、キレイにyaml/jsonファイルを記述できていない問題があります。
ある程度エディタ側のサポートもありますが、属性の型ぐらいしか補完できなかったりして、きちんとモデルを作るか、コピー&ペーストで済まされるかは記述する人次第になりかねません。
コピー&ペーストだらけのスキーマファイルをリファクタリングするのも手作業になります。また逆にリファクタリングが徹底されたスキーマファイルは、参照だらけになって人間には可読性が低くなりかねません。もちろんAPIの利用者が読むときには生成したドキュメントを読むので問題ないのですが、編集者はyaml/jsonを読まないとなりません。
また1ファイルのyamlにREST APIのすべてを定義すると巨大なファイルになるため、ファイル分割などの検討も必要です。モデル定義とエンドポイントのファイルを分けて、最終的に結合するなどの仕組みの構築が必要になってきます。
コードファーストのメリット
そこでコードファーストです。フレームワークFastAPIのサンプルコードを紹介しながらコードファーストのメリットを書いていきます。
実装とAPI仕様書
OpenAPIのスキーマファイルを書くときに何を使っていましたか? FastAPIならPython対応のお好きなコードエディタで書けばいいので参照や補完が自然に書けます。コードだから当然ですね。
ただコードを書くだけでスキーマ定義になります。
OpenAPIのSwaggerドキュメントを見たい場合は、実行して /docsのパスにアクセスすればそれだけで閲覧できます。スキーマファイルからドキュメントをジェネレートするという作業はありません。また実行コードから生成されているのでスキーマとコードの乖離はありません。
こちらは、Todoというデータモデルを準備して、それを1件取得するAPIの例です。コードがそのままスキーマ定義に出力されます。
API仕様書のスタイル Redoc
/docsのパスでSwagger形式のAPIドキュメントが閲覧できますが、同じようなAPIドキュメントでもReDocというものもあります。実はFastAPIはReDocにも対応しています。/redocでアクセスするだけです。ねっ簡単でしょ?
コードのロジックを実装するのは時間がかかると心配するかもしれませんがOpenAPIのスキーマ定義するだけならば、インタフェースだけ書けばいいので中身はダミーでもOKです。そのやり方でしたら記述に手間はかからないので、フロントエンドのチームにすぐにOpenAPIを提供できます。エディタでスキーマファイルを書く代わりに、APIのインタフェースをコードで書くだけなのでより正確で早く書けますよね。
API仕様書をよりわかりやすく
コードを書いただけでは、APIの説明文が不足しています。コード上で説明文やコメントを書いてAPI仕様書をわかりやすくできます。
こちらは、APIのクエリーパラメータに説明文をつけた例です。
モデルの各属性に説明をつける例はこちらです。
ちなみにスキーマ定義に記述できる程度の基本的なバリデーションは、定義=そのままバリデーション実装になります。
入力データとしてこのモデルを使ったときにバリデーションチェックして422 Validation Errorを返す実装になります。便利ですね。
必須データ(required)な属性はデフォルト値がなければ特に意識することなく設定されます。
リファクタリングや型検索
yaml/jsonファイルをテキストエディタで記述するのとは違って、型情報を使って書いているので、Pythonの言語機能の型での検索ができます。このモデルをどこで利用しているかなどが簡単に見つけられます。
リファクタリングもできるので、モデルの名前を変えたりすることも簡単にできます。
まとめ
コードファーストでOpenAPIを爆速で作成する方法としてFastAPIをご紹介しました。個人的にはあまりにスキーマ定義が便利に書けるので、たとえスキーマファーストであっても、スキーマ定義を書くエディタとしてFastAPIを使ってもいいと思うほどです。
(ある程度Pythonの慣れは必要ですが)
サーバーサイドPythonというとdjangoやFlaskが有名ですが、最近はWebフレームワークとしてのPythonはそれほど使われていないと感じます。
ですが、機械学習やAI関連でPythonが人気なことから、それらとの相性の良さでそのままPythonサーバーサイドという選択肢もあります。
FastAPIは、PythonのType Hintを活用してコードファーストでOpenAPIを提供できます。サーバーサイドフレームワークとしても、非同期やwebsocketもサポートしており、決して他のWebフレームワークに見劣りしない良いものです。ぜひ使ってみてください!
この記事が気に入ったらサポートをしてみませんか?