こんにちは、セーフィーでバックエンドのエンジニアをしております神田です。
今回は、バックエンドのAPIを開発する際に使用しているフレームワークについてお話ししていこうと思います!
API開発で使用しているフレームワークの紹介
まず初めに、現在使用しているフレームワークについて紹介します。
Tornado
セーフィーのサービスができた当初(2014年)から使われています。
複数あるフレームワークの中から、APIのみで使用する前提で
- パフォーマンス
- 開発のしやすさ
- デバッグのしやすさ
の観点から、当時最も条件を満たしているために採用されたそうです。
FastAPI
FastAPI自体は2018年にリリースされ、近年人気を集めているフレームワークです。
セーフィーでは2020年以降に新たに作成されたAPIサーバーで使用されています。導入の理由はこれからお話しします!
FastAPIを使用するまでの流れ
もともとはtornadoですべてのAPIを開発していましたが、完全に新規のコンポーネントを開発することになり、せっかく新しく作るのであればtornadoにこだわる必要もないとのことで、その当時評判になりつつあったFastAPIをお試しで採用してみようという話になりました。
これが2020年ごろの話で、創業当初にはなかったFastAPIを導入するきっかけになりました。
FastAPIの何がよくて採用したのか
実際にFastAPIのどのようなところが魅力的で、導入に至ったのかをお話ししたいと思います。
速い
名前からもわかるように、FastAPIは速いことが特徴の1つで、公式ドキュメントにも「NodeJS や Go 並みのとても高いパフォーマンス 」と書かれています。
参考
Web Framework Benchmarks
で確認すると、2020年時点ではFastAPIはかなり速いフレームワークであることがわかります。
Web Framework Benchmarksでベンチマークが示されているフレームワークの中で、FastAPIは全体4位、tornadoは29位でした。
表1. 1リクエストあたり20クエリでの1秒あたりのレスポンス(2022年時点)
フレームワーク | 1リクエストあたり20クエリでの1秒あたりのレスポンス |
---|---|
FastAPI | 12,991 |
Tornado | 1,354 |
ドキュメントが自動生成される
FastAPIでは作成したAPIのドキュメントが自動で生成されます。
ドキュメントを別途作成する手間がなくなるので、
- ドキュメントの作成忘れ
- ドキュメント作成の手間
がなくなります。
実際に使ってみて感じること
現在、当初からあるコンポーネントはtornado、新規のコンポーネントはFastAPIで開発しているので、実際に開発していて両者にどのような違いがあり、FastAPIにどのような良さがあるかを2点お話ししようと思います。
ドキュメント自動生成が本当に助かる
FastAPIの採用理由でも触れましたが、ドキュメントが自動生成されることで得られるメリットは想像以上でした。
tornadoでAPIを開発する場合、
- 仕様書のPRを作成
- APIを実装するリポジトリでAPIを実装しPRを作成
- APIに修正が入った場合も2つのリポジトリでPRを作成
という作業が必要になっています。
リポジトリが分かれている上、両者が連動されていないため、とくに、緊急に軽微な修正を行った際にドキュメントの修正漏れが発生してしまいます。
そうすると、APIを組み込むフロントエンドとモバイルの方が誤ったドキュメントを見ながら組み込んでしまい、うまく動作しないなどということが発生しかねません。。
FastAPIではこのような問題を全て手間なく解決してくれました!
簡単にデータの堅牢性が保たれる
FastAPIはpydanticがベースになっていて、リクエスト・レスポンスのデータ型を簡単に定義することができます。
下記のようなAPIを考えます。(例のためなので内容に意味はありません)
FastAPI
@router.get("/api/hoge") async def get_hoge( id: Optional[int] = Query(max title="id"), name: str = Query(max_length=200, title="名前") ):
id, nameをリクエストパラメータにもつgetのAPIです。 idは数値で任意パラメータ、 nameは文字列で必須パラメータであることがわかります。これをtornadoで書くと、
Tornado
class HogeHandler(self): def async_get(self): id = self.get_argument("id", None) if not instance(id, int): raise Exception() name = self.get_argument("name", None) if name is None: raise Exception() if not instance(name,str): raise Exception() if len(name) > 200: raise Exception()
条件分岐をたくさん書かなければなりません。。
このように、FastAPIではデフォルトで簡単にバリデーションの記述ができます。また、FastAPIの方がどのようなバリデーションをしているのか一目で理解できる上、自前で用意する必要がないため、データの堅牢性が保たれ、予想外のエラー発生を防いでくれます!
さらに、pydanticのBaseModelを使用すれば、ネストしたデータのバリデーションもできますし、レスポンスの型定義をすることで必要なデータを返却し忘れることもなくなります。
まとめ
セーフィーでは、使い慣れた技術や手法を常に選ぶのではなく、その都度最適な技術を選定して開発を行っています。実際、FastAPIを導入することでたくさんの恩恵を受け作業効率も上がりました。 技術は日々進化するので、新しい技術情報にアンテナをはってより良いプロダクト・開発環境を作っていきたいな〜と思います!