GraphQL API にデータを投入するツールがなかったので作った話
はじめに
こんにちは、Zenn 記事投稿 2 本目の @jackchuka です。
前回は、Go 製の CLI ツールを作った話をしましたが、今回は一転して TypeScript で開発した、GraphQL API にデータを投入するための CLI ツール GQL Ingest を紹介したいと思います。
開発の背景
先日、とあるプロジェクトで、データを GraphQL で作成されたバックエンドに投入する依頼が届きました。いかにもよくある内容で、大量のデータが埋め込まれた CSV ファイルからデータを投入するというものでした。
昨今のモダンなアプリケーション開発では、GraphQL API を利用することが増えてきましたが、GraphQL でのデータ投入のベストプラクティスやサポートするツールは少なく、特に大規模なデータセットを効率的に処理できるものはほとんど存在しませんでした。
「ないのなら作ってしまおう」ということで、GQL Ingest という新しい CLI ツールを開発し、OSS として公開しました。
重視したポイント
実際の動作をお見せする前に、今回作るに当たり事前に重視したポイントを以下に記載します:
- 宣言的な設定: 1 CSV ファイルに対して、どの GraphQL Mutation を実行するかを明確に定義できるようにしたい
- 高速な処理: 大量のデータセットを効率的に処理するため、並列処理をサポートしたい
- 再試行機能: 負荷やネットワークの一時的なエラーに対しても再試行できるようにしたい
- 依存関係の管理: エンティティ間の依存関係を考慮し、正しい順序でデータを投入できるようにしたい
これらを満たしていれば、実務においても十分に使えるツールになると考え、開発を進めました。
実際の使用例
以上のポイントを踏まえ完成したツールをお見せします。
以下は、3 つのエンティティ(商品、サプライヤー、倉庫)を持つデータセットを GraphQL API に投入した例です。いずれも Concurrency 10 で並列処理を行い、依存関係を考慮して順序よくデータを投入しています。
$ npx @jackchuka/gql-ingest -e <YOUR_GRAPHQL_ENDPOINT> -c ./data
Starting seed data generation...
Discovered 3 mapping files: items.json, suppliers.json, warehouses.json
Processing 1 dependency waves...
Wave 1: Processing entities [items, suppliers, warehouses]
Processing entity: data/mappings/items.json
Processing entity: data/mappings/suppliers.json
Processing entity: data/mappings/warehouses.json
Processing 32 rows with concurrency: 10
Processing 1 rows with concurrency: 10
Processing 18 rows with concurrency: 10
📊 Progress: 10/32 (31.3%) - Chunk 1: 10 ✓, 0 ✗
📊 Progress: 1/1 (100.0%) - Chunk 1: 1 ✓, 0 ✗
📊 Progress: 10/18 (55.6%) - Chunk 1: 10 ✓, 0 ✗
📊 Progress: 20/32 (62.5%) - Chunk 2: 10 ✓, 0 ✗
📊 Progress: 18/18 (100.0%) - Chunk 2: 8 ✓, 0 ✗
📊 Progress: 30/32 (93.8%) - Chunk 3: 10 ✓, 0 ✗
📊 Progress: 32/32 (100.0%) - Chunk 4: 2 ✓, 0 ✗
📊 Processing Summary:
Total Processed: 51
✓ Successes: 51
✗ Failures: 0
Success Rate: 100.0%
Duration: 1.88s
Avg Request Time: 489ms
📋 Per-Entity Breakdown:
items: 32 total (32 ✓, 0 ✗) - 100.0% success - 1.88s
suppliers: 18 total (18 ✓, 0 ✗) - 100.0% success - 1.27s
warehouses: 1 total (1 ✓, 0 ✗) - 100.0% success - 0.82s
ご覧のとおり、最後の出力では、各エンティティの処理状況や成功率、平均リクエスト時間などの詳細なメトリクスが表示される点も個人的には気に入っています。
🎯 柔軟な設定
ここからは細かい設定に少し触れていきたいと思います。
GQL Ingestの設定のフォルダ構成は若干オピニオネイテッドですが、--configはフォルダを期待しており、CSV ファイルと GraphQL のマッピングを JSON 形式で定義したもの、挙動を設定をする YAML ファイルを配置することで、データ投入の準備が整います。
config/
├── data/ # CSVファイル
├── graphql/ # GraphQL mutation定義
├── mappings/ # CSVとGraphQLのマッピング設定
└── config.yaml # 並列処理・再試行設定
例えば、商品データの設定は以下のようになります:
mappings/items.json:
{
"csvFile": "data/items.csv",
"graphqlFile": "graphql/items.graphql",
"mapping": {
"name": "item_name",
"sku": "item_sku"
}
}
graphql/items.graphql:
mutation CreateItem($name: String!, $sku: String!) {
createItem(input: { name: $name, sku: $sku }) {
id
name
sku
}
}
🚀 並列処理
使用例にあった通り、GQL Ingest はエンティティレベルと行レベルの並列処理をサポートしています。
以下のような設定を行うことで、投入先の GraphQL API の性能に応じて、並列度を調整できます。
また、特定エンティティに対しての並列度をそれぞれ調整することも可能です。
parallelProcessing:
concurrency: 10 # 1エンティティあたり10行を並列処理
entityConcurrency: 3 # 最大3つのエンティティを同時処理
preserveRowOrder: false # 高速化のために順序を保持しない
entityConfig:
items:
concurrency: 20 # itemsエンティティは20行を並列処理
🧑🤝🧑 依存関係の管理
加えて、エンティティ間の依存関係を定義することで、正しい順序でデータを投入できます。例えば、 orders は items の完了後に処理する必要がある場合、以下のように設定します。
entityDependencies:
orders: ["items"] # 依存関係の定義、ordersはitemsの完了後に処理
🔄 リトライ機構
そして、データ投入中いちばん厄介な問題の一つである、API リクエストの一時的な失敗に対しても自動で再試行を行う機能も搭載しました。
ネットワークの問題やサーバーの一時的な障害で、リクエストが失敗した場合、以下のように設定することで、最大 5 回まで再試行し、指数バックオフを適用します。
retry:
maxAttempts: 5 # 最大5回まで再試行
baseDelay: 2000 # 初回は2秒待機
exponentialBackoff: true # 指数的に待機時間を増加
retryableStatusCodes: # 再試行対象のHTTPステータス
- 408, 429, 500, 502, 503, 504
インストールと使い方
GQL Ingest は npm パッケージとして公開されており、以下のコマンドでインストールできます。
# グローバルインストール
npm install -g @jackchuka/gql-ingest
# またはnpxで直接実行
npx @jackchuka/gql-ingest \
--endpoint https://your-api.com/graphql \
--config ./config \
--headers '{"Authorization": "Bearer TOKEN"}'
まとめ
いかがでしたでしょうか? GQL Ingest は、GraphQL API へのデータ投入を私なりに効率化するために作成したツールです。宣言的な設定、並列処理、依存関係の管理、再試行機能など、多くの機能を備えています。
実際に関わっているプロジェクトでも活用しており、大変重宝しています。
もし GraphQL API へのデータ投入でお困りの方がいれば、GQL Ingest をぜひ試してみてください。
もし気に入っていただけたら、GitHub でスター ⭐ をつけていただけると嬉しいです!
フィードバックやコントリビューションも大歓迎です!
リンク
Discussion