API Gateway と Hasura で構築する高速な GraphQL API

はじめに

モダンなアプリケーション開発では、複数のデータソースを統一的に扱う必要があります。API GatewayHasura を組み合わせることで、スケーラブルで保守性の高い API インフラを実現できます。

本記事では、この二つのツールの特性を理解し、実装例を通じて実践的な活用方法を解説します。

API Gateway とは

概要

AWS API Gateway は、アプリケーションとバックエンドサービスの間に位置するマネージドなフロントドアです。クライアントのリクエストを適切なバックエンドにルーティングし、セキュリティ、スケーリング、監視を統一的に管理します。

主な役割

  • トラフィック管理: 数十万の同時リクエストを自動的に処理
  • 認証・認可: IAM、Cognito、Lambda authorizer など複数の方式に対応
  • レート制限: 顧客ティアごとの使用量制御
  • キャッシング: レスポンスキャッシュで応答速度を向上
  • 監視: CloudWatch や X-Ray による詳細なメトリクス収集

API Gateway のアーキテクチャ

Client Requests
    ↓
API Gateway(エントリーポイント)
    ↓
認証・認可・レート制限・キャッシング
    ↓
バックエンドサービス(Lambda・EC2・HTTP エンドポイント)
    ↓
Response → Client

Hasura とは

概要

Hasura は、GraphQL エンジンとして機能するオープンソースツールです。データベースやマイクロサービスを GraphQL インターフェースで統一し、自動的に本番対応の API を生成します。

従来の GraphQL との違い

従来の GraphQL サーバーでは、各フィールドに対してレゾルバー関数を手書きする必要があります。一方、Hasura はコンパイラ・アプローチを採用し、GraphQL クエリを直接最適化された SQL クエリに変換します。

GraphQL Query
    ↓
GraphQL AST にパース
    ↓
SQL AST に変換
    ↓
変数と変換を適用
    ↓
最適化された SQL を実行

主な機能

  • 自動スキーマ生成: データベーステーブルから完全な GraphQL API を自動生成
  • マルチソース対応: PostgreSQL、マイクロサービス API、REST API を統合
  • リアルタイム: GraphQL Subscription で変更を即座に配信
  • 行レベルセキュリティ: ロールベースの細粒度なアクセス制御
  • カスタムロジック: Actions や Event Triggers で ビジネスロジックを追加

API Gateway と Hasura の連携

アーキテクチャパターン

Client Applications
    ↓
AWS API Gateway(REST/HTTP/WebSocket)
    - 認証・認可
    - レート制限
    - キャッシング
    ↓
Hasura GraphQL Layer
    - スキーマ統合
    - フィールドレベルのアクセス制御
    - リアルタイム Subscription
    ↓
バックエンドデータソース
    - データベース
    - マイクロサービス API
    - 外部 GraphQL/REST API

連携のメリット

機能API GatewayHasura統合効果
リクエストルーティング-完全なトラフィック管理
認証戦略複数対応ヘッダーベース階層的なセキュリティ
レート制限API レベルクエリレベル包括的な制御
リアルタイムWebSocketSubscription 内蔵シームレスなリアルタイム
データ統合オーケストレーション統一化統一されたインターフェース
監視インフラレベルクエリレベルフルスタック可視性

実装例

例1: E-コマース API の構築

複数のマイクロサービスを統合した E-コマース API を構築する場合を考えます。

構成要素:

  • 商品カタログサービス(GraphQL API)
  • 在庫管理(PostgreSQL データベース)
  • 決済プロセッサー(REST API)
  • CMS 連携(REST API)

実装ステップ:

1. Hasura の設定

# docker-compose.yml
version: "3.6"
services:
  postgres:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_DB: ecommerce
    volumes:
      - db_data:/var/lib/postgresql/data
 
  hasura:
    image: hasura/graphql-engine:latest
    depends_on:
      - postgres
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:password@postgres:5432/ecommerce
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_ADMIN_SECRET: admin_secret
    ports:
      - "8080:8080"
 
volumes:
  db_data:

2. 在庫データベースのセットアップ

CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  price DECIMAL(10, 2) NOT NULL,
  description TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
 
CREATE TABLE inventory (
  id SERIAL PRIMARY KEY,
  product_id INTEGER REFERENCES products(id),
  quantity INTEGER NOT NULL,
  warehouse_id INTEGER,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
 
-- インデックスを作成してクエリを高速化
CREATE INDEX idx_inventory_product_id ON inventory(product_id);
CREATE INDEX idx_products_name ON products(name);

3. REST API を GraphQL にラップ

Hasura の Actions を使用して、決済プロセッサーの REST API を GraphQL にラップします。

# Actions を定義
mutation ProcessPayment($amount: Float!, $currency: String!) {
  processPayment(amount: $amount, currency: $currency) {
    transaction_id
    status
    timestamp
  }
}

Hasura Admin Console で以下を設定:

  • Webhook URL: https://payment-service.example.com/process
  • 入出力型定義
  • エラーハンドリング

4. API Gateway のセットアップ

# AWS CLI で REST API を作成
aws apigateway create-rest-api \
  --name "ECommerceAPI" \
  --description "Unified E-Commerce API"

API Gateway にて:

  • リソース /graphql を作成
  • Hasura エンドポイントへのプロキシ統合を設定
  • JWT 認証用の Lambda authorizer を追加

5. 認証の実装

# Lambda Authorizer (JWT 検証)
import json
import jwt
import os
 
def lambda_handler(event, context):
    token = event['authorizationToken']
    try:
        decoded = jwt.decode(
            token,
            os.environ['JWT_SECRET'],
            algorithms=['HS256']
        )
 
        auth_context = {
            'x-hasura-user-id': decoded['sub'],
            'x-hasura-role': decoded.get('role', 'user')
        }
 
        return {
            'principalId': decoded['sub'],
            'policyDocument': {
                'Version': '2012-10-17',
                'Statement': [{
                    'Action': 'execute-api:Invoke',
                    'Effect': 'Allow',
                    'Resource': event['methodArn']
                }]
            },
            'context': auth_context
        }
    except jwt.InvalidTokenError:
        raise Exception('Unauthorized')

6. GraphQL クエリの例

query GetProductsWithInventory($warehouse: Int!) {
  products {
    id
    name
    price
    description
    inventory(where: { warehouse_id: { _eq: $warehouse } }) {
      quantity
      updated_at
    }
  }
}
 
subscription OnInventoryUpdate($product_id: Int!) {
  inventory(where: { product_id: { _eq: $product_id } }) {
    quantity
    warehouse_id
    updated_at
  }
}

例2: リアルタイム協働編集アプリケーション

ドキュメント管理システムで複数ユーザーによる同時編集をサポートする場合です。

アーキテクチャ:

1. クライアント更新要求
   ↓
API Gateway(REST エンドポイント)
   ↓
Hasura mutation でドキュメント更新
   ↓
データベース変更
   ↓
Hasura Event Trigger 発火
   ↓
Webhook で外部サービスに通知

2. リアルタイム購読
   ↓
API Gateway(WebSocket)
   ↓
Hasura subscription で変更を配信
   ↓
接続中の全クライアントに更新を配信

実装例:

# ドキュメント更新の mutation
mutation UpdateDocument($doc_id: Int!, $content: String!, $user_id: Int!) {
  update_documents_by_pk(
    pk_columns: { id: $doc_id }
    _set: { content: $content, updated_at: "now()" }
  ) {
    id
    updated_at
  }
  insert_document_history_one(
    object: {
      document_id: $doc_id
      user_id: $user_id
      content_delta: $content
      timestamp: "now()"
    }
  ) {
    id
  }
}
 
# リアルタイム購読
subscription OnDocumentUpdate($doc_id: Int!) {
  documents_by_pk(id: $doc_id) {
    id
    content
    updated_at
    updated_by {
      id
      name
    }
  }
  document_history(
    where: { document_id: { _eq: $doc_id } }
    order_by: { timestamp: desc }
    limit: 10
  ) {
    id
    content_delta
    user_id
    timestamp
  }
}

Event Trigger の設定:

# Webhook handler
from flask import Flask, request
import requests
 
app = Flask(__name__)
 
@app.route('/webhook/document-updated', methods=['POST'])
def document_updated():
    event = request.json['event']
    doc_id = event['data']['new']['id']
 
    # 接続中のクライアントに通知
    for client_ws in active_connections:
        asyncio.create_task(
            client_ws.send_json({
                'type': 'document.updated',
                'document_id': doc_id,
                'timestamp': event['created_at']
            })
        )
 
    return {'status': 'ok'}

ベストプラクティス

1. キャッシング戦略

┌─────────────────────────────────────────┐
│          CDN キャッシュ                 │
│     (静的コンテンツ: 1時間)            │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│     API Gateway キャッシュ              │
│     (レスポンス: 5分)                   │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│     Hasura クエリキャッシュ             │
│     (頻繁なクエリ: 1分)                 │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│     データベース                        │
└─────────────────────────────────────────┘

2. レート制限の設定

# API Gateway でのレート制限
- スタンダードユーザー: 100 req/min
- プレミアムユーザー: 1000 req/min
- API キー検証: X-API-Key ヘッダー必須
 
# Hasura でのクエリ制限
- クエリ深度: 最大 10 レベル
- クエリ複雑度: スコア制度で制御

3. セキュリティ層

┌─────────────────────────────────────────┐
│  API Gateway IAM ポリシー               │
│  (API レベルの認可)                      │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│  Hasura RBAC                            │
│  (ロールベースのアクセス制御)           │
└─────────────────────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│  Hasura Row-Level Security              │
│  (行レベルのフィルタリング)             │
└─────────────────────────────────────────┘

よくある質問

Q: Hasura だけでは不十分なのか?

A: Hasura は優れた GraphQL エンジンですが、エンタープライズレベルのトラフィック管理、複雑な認証戦略、API 分析には API Gateway が必要です。API Gateway と Hasura は相補的な役割を果たします。

Q: コストの増加は?

A: Hasura はオープンソースで無料、または クラウド版で段階的課金です。API Gateway は使用量ベースの課金となり、適切に設計すればコストを抑えられます。

Q: 既存の API Gateway から移行できるか?

A: はい。Apigee、MuleSoft などの既存 API Gateway と並行運用し、段階的に Hasura へ移行することも可能です。

まとめ

API Gateway と Hasura の組み合わせは、モダンなバックエンド API インフラを構築する強力なアプローチです:

  • API Gateway: トラフィック管理、セキュリティ、監視を担当
  • Hasura: データ統合、GraphQL 生成、リアルタイム機能を担当

この二層構造により、スケーラブルで保守性の高い、開発効率の優れた API プラットフォームを実現できます。

ぜひ自分のプロジェクトで試してみてください。

関連記事