画像変換 API の設計パターン - URL ベース、リクエストボディ、非同期処理の比較
画像変換 API の設計要件と主要パターン
画像変換 API は、クライアントからのリクエストに応じて画像のリサイズ、フォーマット変換、クロップ、フィルタ適用などの処理を行い、変換後の画像を返すサービスです。設計にあたっては、レイテンシ、スループット、コスト、キャッシュ効率のバランスを考慮する必要があります。
主要な設計パターン:
- URL パラメータ方式: 画像 URL にクエリパラメータやパスセグメントで変換指示を埋め込む。CDN キャッシュと相性が良く、GET リクエストのみで完結する。Cloudinary、imgix が採用
- REST API 方式: POST リクエストのボディに画像データと変換パラメータを送信し、変換後の画像をレスポンスとして返す。複雑な変換指示や画像アップロードと変換の同時実行に適する
- 非同期キュー方式: 変換リクエストをキューに投入し、バックグラウンドで処理する。完了後に Webhook やポーリングで結果を取得する。大量バッチ処理や重い変換に適する
設計判断の軸:
- レイテンシ要件: リアルタイム表示が必要か (URL 方式)、数秒の遅延が許容されるか (REST)、数分かかっても良いか (非同期)
- 変換の複雑さ: 単純なリサイズ/フォーマット変換か (URL)、複数ステップの合成処理か (REST/非同期)
- 画像のソース: 既にストレージにある画像か (URL)、クライアントからアップロードする画像か (REST)
- 処理量: 1 枚ずつリアルタイムか (URL/REST)、数千枚のバッチか (非同期)
URL パラメータ方式 - CDN フレンドリーな設計
URL パラメータ方式は、画像の URL 自体に変換指示を含める設計です。同じ URL は常に同じ結果を返す (冪等性) ため、CDN キャッシュが最も効果的に機能します。
URL 設計パターン:
- クエリパラメータ方式:
/images/photo.jpg?w=800&h=600&fit=cover&format=webp&quality=80 - パスセグメント方式:
/images/w_800,h_600,c_fill,f_webp,q_80/photo.jpg(Cloudinary スタイル) - ディレクトリ方式:
/images/800x600/cover/webp/80/photo.jpg
推奨パラメータ設計:
- w (width): 出力幅 (ピクセル)。0 または省略でアスペクト比維持
- h (height): 出力高さ (ピクセル)。0 または省略でアスペクト比維持
- fit: リサイズモード。
cover(クロップ)、contain(余白)、fill(引き伸ばし)、inside(収まるサイズ) - format: 出力フォーマット。
auto(Accept ヘッダーで自動判定)、webp、avif、jpeg、png - quality: 圧縮品質 (1-100)。
autoで画像内容に応じた自動最適化 - dpr: デバイスピクセル比。
2で 2x 解像度の画像を生成
実装アーキテクチャ (AWS):
- CloudFront → Lambda@Edge (または CloudFront Functions) → S3 Origin
- CloudFront がキャッシュミスした場合のみ Lambda が起動し、S3 からオリジナル画像を取得して変換する
- 変換結果は CloudFront にキャッシュされ、同じ URL への 2 回目以降のリクエストは Lambda を経由しない
- キャッシュヒット率 90% 以上を目標とし、Lambda の実行回数を最小化する
REST API 方式 - 柔軟な変換指示と画像アップロード
REST API 方式は、HTTP POST リクエストで画像データと変換パラメータを送信し、変換結果を返す設計です。URL 方式では表現しきれない複雑な変換や、クライアントからの画像アップロードと同時変換に適しています。
API 設計例:
POST /api/v1/transform Content-Type: multipart/form-data
リクエストボディ:
- image: 画像ファイル (multipart) または画像 URL (JSON)
- operations: 変換操作の配列。順序通りに適用される
JSON リクエスト例:
{"source": "https://example.com/photo.jpg", "operations": [{"type": "resize", "width": 800, "height": 600, "fit": "cover"}, {"type": "format", "output": "webp", "quality": 80}, {"type": "watermark", "image": "logo.png", "position": "bottom-right", "opacity": 0.5}]}
REST 方式の利点:
- 複雑な変換パイプライン: 複数の操作を順序付きで指定できる。ウォーターマーク追加、テキストオーバーレイ、複数画像の合成など
- バリデーション: リクエストボディを JSON Schema で厳密にバリデーションできる
- 認証・認可: API キーや JWT トークンで利用者を制限し、利用量を管理できる
- エラーハンドリング: 詳細なエラーレスポンス (不正なパラメータ、サポートされないフォーマットなど) を返せる
レスポンス設計:
- 成功時:
Content-Type: image/webpで変換後の画像バイナリを直接返す。または JSON で変換後画像の URL を返す - 失敗時:
{ "error": "invalid_parameter", "message": "Width must be between 1 and 10000", "field": "operations[0].width" }
注意点: POST リクエストは CDN でキャッシュされないため、同じ変換を繰り返しリクエストされる場合はサーバー側でキャッシュ (Redis, DynamoDB) を実装する必要があります。
非同期処理方式 - 大量バッチと重い変換への対応
非同期方式は、変換リクエストを即座に受け付けてジョブ ID を返し、バックグラウンドで処理を実行する設計です。処理完了後に Webhook 通知またはポーリングで結果を取得します。
非同期方式が適するケース:
- 大量バッチ処理: 数百〜数千枚の画像を一括変換する場合。同期処理ではタイムアウトする
- 重い変換処理: AI 背景除去、超解像 (アップスケーリング)、動画からのサムネイル生成など、1 枚あたり数秒〜数十秒かかる処理
- リソース制約: Lambda の 15 分タイムアウトや 10GB メモリ制限を超える処理
アーキテクチャ (AWS):
- リクエスト受付: API Gateway → Lambda → SQS キューにメッセージ投入。即座にジョブ ID を返す
- 処理実行: SQS → Lambda (または ECS Fargate) で画像変換を実行。結果を S3 に保存
- 完了通知: 処理完了時に EventBridge → SNS で Webhook 通知。または DynamoDB にステータスを書き込み、クライアントがポーリング
API 設計:
- ジョブ投入:
POST /api/v1/jobs→{ "job_id": "abc123", "status": "queued" } - ステータス確認:
GET /api/v1/jobs/abc123→{ "status": "completed", "result_url": "https://..." } - バッチ投入:
POST /api/v1/batchで複数画像を一括投入
ステータス遷移: queued → processing → completed / failed
DLQ (Dead Letter Queue) の設計: 処理に失敗したメッセージは DLQ に移動し、手動確認後にリトライまたは破棄する。3 回リトライしても失敗する場合は DLQ 行きとし、アラートを発報する。
セキュリティとレート制限 - 悪用防止の設計
画像変換 API は計算リソースを消費するため、悪用防止のセキュリティ設計が不可欠です。無制限にリクエストを受け付けると、DDoS 攻撃やリソース枯渇の原因になります。
認証・認可:
- API キー: 各クライアントに固有の API キーを発行し、リクエストヘッダー (
X-API-Key) で送信させる - 署名付き URL: URL パラメータ方式では、HMAC 署名をパラメータに含めて改ざんを防止する。
?w=800&h=600&sig=a1b2c3 - Referer 制限: 許可されたドメインからのリクエストのみ受け付ける (CDN レベルで設定)
レート制限:
- リクエスト数制限: API キーごとに 1 分あたり 100 リクエスト、1 日あたり 10,000 リクエストなどの上限を設定
- 帯域幅制限: 1 日あたりの転送量上限を設定し、大量の大きな画像の変換を制限
- 同時実行数制限: 1 クライアントあたりの同時処理数を制限し、リソースの独占を防止
- 実装: API Gateway のスロットリング、Lambda の同時実行数制限、DynamoDB でのカウンター管理
入力バリデーション:
- 画像サイズ制限: アップロード画像の最大サイズを制限 (例: 25MB)。Lambda のペイロード制限 (6MB) を超える場合は S3 経由でアップロード
- 出力サイズ制限: 出力画像の最大解像度を制限 (例: 10,000 × 10,000 px)。巨大な画像生成によるメモリ枯渇を防止
- フォーマット検証: アップロードされたファイルのマジックバイトを検証し、拡張子偽装を検出する
- パラメータ範囲: width/height は 1-10000、quality は 1-100 など、各パラメータの有効範囲を厳密に検証する
キャッシュ戦略とコスト最適化
画像変換 API のコストの大部分は計算リソース (Lambda 実行時間) と帯域幅です。効果的なキャッシュ戦略でコストを 80-90% 削減できます。
多層キャッシュ設計:
- CDN キャッシュ (L1): CloudFront で変換済み画像をエッジにキャッシュ。TTL は 30 日以上を推奨。URL が変わらない限りオリジンにリクエストが到達しない
- オリジンキャッシュ (L2): S3 に変換済み画像を永続保存する。CDN キャッシュが期限切れになっても、S3 から即座に配信できる (Lambda の再実行不要)
- メモリキャッシュ (L3): Lambda の実行環境が再利用される場合、/tmp ディレクトリに直近の変換結果をキャッシュする (最大 10GB)
キャッシュキーの設計:
- URL パラメータ方式: URL 全体がキャッシュキーになる。パラメータの順序を正規化 (アルファベット順) し、同じ変換が異なるキーでキャッシュされることを防ぐ
- REST 方式: リクエストボディのハッシュ (SHA-256) をキャッシュキーとする
キャッシュ無効化:
- オリジナル画像が更新された場合、関連する全変換キャッシュを無効化する必要がある
- バージョニング: ファイル名にバージョン番号を含める (
photo-v2.jpg) か、クエリパラメータでバージョンを指定する (?v=2) - CloudFront Invalidation: 緊急時は
/*で全キャッシュを無効化できるが、コストがかかるため頻繁な使用は避ける
コスト最適化のベストプラクティス:
- 人気の変換パターン (サムネイル 200 × 200、OGP 1,200 × 630など) を事前生成し、S3 に保存しておく
- Lambda のメモリを適切に設定する (Sharp による画像変換は 1769MB で最適なコスト効率)
- 不要な変換バリエーションを生成しないよう、許可するパラメータの組み合わせを制限する