GraphQLビデオディスカバリーAPIの構築
ビデオデータベースが数十万件のレコードに成長したとき、私たちのAPIがボトルネックとなりました。
1つの画面を表示するために、トレンド動画、チャンネル詳細、タグ、視聴回数、およびおすすめを表示するための5つの異なるRESTコールが必要でした。低速な接続環境のモバイルユーザーは、1ページを読み込むだけで30回ものリクエストを送信しなければなりませんでした。
GraphQLはこれを解決します。クライアントは、必要なものを1回のリクエストで正確に要求できます。
私はStrawberryとFastAPIを使用して、本番環境用のビデオディスカバリーAPIを構築しました。その方法を以下に紹介します。
なぜStrawberryなのか?
GrapheneとAriadneをテストしましたが、以下の理由からStrawberryを選びました。
• 型ヒントがスキーマになります。Pythonコードを書くだけで、StrawberryがGraphQLの型を構築します。手動での同期は不要です。 • 非同期ネイティブです。これにより、APIはイベントループをブロックすることなく、多くのリクエストを処理できます。 • FastAPIとの統合がシームレスです。GraphQLエンドポイントは、既存のRESTルートのすぐ隣に配置できます。 • DataLoaderが組み込まれています。このツールは、N+1問題を標準で解決します。
データスタック
データはPHPサイトによって管理されているSQLiteデータベースに格納されています。高速な全文検索のためにSQLite FTS5を使用しています。
Pythonサービスはこのデータベースを読み取り専用レイヤーとして読み取ります。これにより、信頼できる唯一の情報源(single source of truth)が確保されます。
検索体験を向上させるために、FTS5の関連性スコアと人気シグナルを組み合わせています。これにより、1つのバイラル動画が他のすべての検索結果を圧倒してしまうのを防いでいます。
N+1問題の解決
GraphQLにおける一般的な罠は、N+1問題です。20個のビデオを取得し、その後それぞれのチャンネルを取得する場合、単純なAPIでは21回のデータベースクエリが実行されます。
DataLoaderはバッチ処理によってこれを修正します。1回のティックですべてのチャンネルIDを収集し、それらすべてを取得するために単一のクエリを実行します。これにより、クエリ時間は40msから5ms未満に短縮されました。
主要な設計上の選択
• 不透明なID (Opaque IDs): IDには文字列を使用しています。これにより、クライアントアプリを壊すことなく将来的な変更が可能になります。 • 意図的なNull許容性 (Intentional Nullability): どのフィールドが空になり得るかを定義します。これにより、クライアントはクラッシュすることなく欠落したデータを処理できます。 • 読み取り専用スキーマ: GraphQL APIは書き込みを処理しません。これにより、多くのセキュリティ上の懸念が解消されます。 • エッジキャッシュ: GraphQLはデフォルトでPOSTを使用するため、キャッシュが困難です。私はAutomatic Persisted Queries (APQ)を使用して、Cloudflare経由でのキャッシュを可能にしています。
カスタムエンドポイントが多すぎたり、複雑なクエリパラメータに苦労しているなら、このスタックは有力な選択肢となります。
