Amazon Web Services ブログ

既存の MySQL と PostgreSQL データベース用の GraphQL API の作成

AWS Cloud Development Kit (CDK) を使って既存のリレーショナルデータベース上にスケーラブルでセキュアな GraphQL インタフェースを簡単に構築できる機能を発表しました。AWS Systems Manager Parameter Store に SecureString として安全に保存されたデータベースの認証情報と共に AWS Amplify GraphQL API CDK コンストラクトを提供し、SQL ステートメントを実行する GraphQL API の構築を開始します。この新機能は、Amazon Relational Database Service (Amazon RDS) 上の MySQL および PostgreSQL データベース、または外部でホストされている MySQL および PostgreSQL データベースで動作します。

2023 年 10 月に、DynamoDB データベースの作成と接続をサポートする Amplify GraphQL API の新しい L3 CDKコンストラクトを発表しました。L3 CDK コンストラクトにより、お客様は AWS Lambda 関数を作成し、追加のデータソースに接続することができます。お客様は AWS Lambda を使用する柔軟性を楽しんでいるが、データベース接続、SQL 文の実行、ネットワーク設定を管理するためのカスタムコードを手作業で構築するための価値を生みづらい重労働を取り除くために、MySQL と PostgreSQL データベースに対するよりファーストクラスのサポートを求めていました。

既存の MySQL および PostgreSQL データベース用の新しい GraphQL API を 5 つのステップで作成してみましょう。

前提条件

始めるには以下が必要です。

  • npm 経由の CDK CLI がインストールされていること
  • このガイドでは、Amazon RDS で デプロイされた MySQL と PostgreSQL データベースがあることを前提に説明しますが、この機能は一般にアクセス可能などのデータベースでも動作します。

Step1 – データベースの認証情報を Systems Manager に保存

データベース接続情報 (ホスト名、ユーザ名、パスワード、ポート、データベース名) を、Systems Manager に SecureString として格納します。

Systems Manager コンソールに移動し、Parameter Store に移動して、Create Parameter をクリックします。データベースサーバのホスト名、接続するユーザ名とパスワード、データベースポート、およびデータベース名にそれぞれ 1 つずつ、合計 5 つの異なる SecureString を作成します。

Systems Manager 構成は以下のようになります。

Step 2 – 新しい AWS CDK プロジェクトをセットアップし、Amplify GraphQL コンストラクトをインストール

新しい AWS CDK アプリケーションを作成し、ターミナルで以下の AWS CDK CLI コマンドを実行して、AWS Amplify GraphQL コンストラクトの依存関係をインストールします。

mkdir sql-api
cd sql-api
cdk init app --language=typescript

次にプロジェクトを開き、npm install @aws-amplify/graphql-api-construct を実行して、GraphQL API コンストラクトを依存関係に追加します。

Step 3 – GraphQL スキーマで Query と Mutation を定義

AWS CDK アプリの lib/ フォルダ内に、公開したい API を含む新しい schema.graphql ファイルを作成します。公開したい API に合わせて、GraphQL オブジェクト型、Query、Mutation を定義します。例えば、データベーステーブルのオブジェクト型、それらのテーブルからデータを取得する Query、それらのテーブルを変更する Mutation を定義します。

type Meal {
  id: Int!
  name: String!
}

type Query {
  listMeals(contains: String!): [Meal]
    @sql(statement: "SELECT * FROM Meals WHERE name LIKE CONCAT('%', :contains, '%');")
    @auth(rules: [{ allow: public }])
}

Queryリクエストから引数を参照するには、:notation を使用できます。Amplify の GraphQL API は、デフォルトで拒否ベースで動作します。{ allow: public } ルールでは、API キーを使用している人であれば誰でもこの Queryを呼び出せるように指定しています。API キー、Amazon Cognito User Pool、OpenID Connect、AWS Identity and Access Management (IAM)、またはカスタム Lambda 関数に基づいて、これらの Query や Mutation へのアクセスを制限するために Authorization ルールを確認してください。

Step 4 – GraphQL API コンストラクトと、VPC 設定を構成

データベース内の SSM パスを参照する、AWS CDK スタック内の GraphQL API CDK コンストラクトの新しいインスタンスを作成します。このコンストラクトを sql-api-stack.ts ファイルにインポートします。

import { AmplifyGraphqlApi, AmplifyGraphqlDefinition } from '@aws-amplify/graphql-api-construct';

AWS CDK スタックコードで、GraphqlApi のインスタンスを作成します。データベースの認証情報への SSM パラメータパスを props として渡します。また、.graphql スキーマファイルを指すように schema プロパティを設定します。

export class RdsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    const amplifyApi = new AmplifyGraphqlApi(this, "AmplifyApi", {
      definition: AmplifyGraphqlDefinition.fromFilesAndStrategy(
        path.join(__dirname, "schema.graphql"),
        {
          dbType: 'MYSQL',
          name: 'MySQLSchemaDefinition',
          dbConnectionConfig: {
            databaseNameSsmPath: '/rds-test/database',
            hostnameSsmPath: '/rds-test/host',
            passwordSsmPath: '/rds-test/password',
            portSsmPath: '/rds-test/port',
            usernameSsmPath: '/rds-test/username',
          },
        },
      ),
      authorizationModes: {
        // We recommend to only use API key authorization for development purposes.
        defaultAuthorizationMode: 'API_KEY',
        apiKeyConfig: {
          expires: cdk.Duration.days(30)
        }
      }
    })
  }
}

今回は、MySQL データベースは Amazon Virtual Private Cloud (Amazon VPC) 内の Amazon RDS 上にデプロイされているため、データベースの VPC 情報も GraphQL API に提供する必要があります。

GraphqlApi コンストラクトで、Amazon VPC、アベイラビリティゾーン、セキュリティグループ ID を指すようにvpcConfiguration プロパティを構成します。

const amplifyApi = new AmplifyGraphqlApi(this, "AmplifyApi", {
      definition: AmplifyGraphqlDefinition.fromFilesAndStrategy(
        path.join(__dirname, "schema.graphql"), {
        dbType: 'MYSQL',
        name: 'MySQLSchemaDefinition',
        dbConnectionConfig: { ... },
        // Place all VPC Configuration in here. If your database is 
        // publicly reachable over the Internet, you can skip this config.
        vpcConfiguration: {
          // (1) Set the security group ID
          securityGroupIds: ['sg-08XXXXXXX'],
          // (2) Set the VPC ID
          vpcId: 'vpc-c3XXXXXX',
          // (3) Set the available subnet + availability zone config
          subnetAvailabilityZoneConfig: [{
            availabilityZone: 'us-east-1b',
            subnetId: 'subnet-5bXXXXXXX'
          }]
        },
      }),
      authorizationModes: { ... }
    })

Step 5 – GraphQL API のデプロイ

次に AWS CDK アプリをデプロイします。cdk deploy を実行して、スキーマから接続されたデータベースで GraphQL API スタックを起動します。これで、AWS AppSync コンソールに移動し、GraphQL Query と Mutation の実行を開始できます。

ボーナス:インライン SQL ステートメントをファイル参照にリファクタリング

SQL ステートメントを GraphQL API と一緒にインラインで記述することは、特に API が大きくなるにつれて管理が難しくなる可能性があります。これらの Query や Mutation を管理しやすくするには、SQL ステートメントを別の .sqlファイルで作成し、GraphQL スキーマに参照として追加します。まず、新しい lib/sql-statements フォルダーを作成し、SQL ステートメントを含む listMeals.sql ファイルを追加してみましょう。lib/sql-statments/listMeals.sql ファイルは以下のようになります。

SELECT * FROM Meals;

lib/sql-api-stack.ts ファイルで、sql-statements/ フォルダーから読み取り、カスタム SQL ステートメントとして Amplify GraphQL API に追加します。

// ...Other imports
import * as fs from 'fs';
import * as path from 'path';
import { AmplifyGraphqlApi, AmplifyGraphqlDefinition, SQLLambdaModelDataSourceStrategyFactory } from '@aws-amplify/graphql-api-construct';

export class RdsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Define custom SQL statements folder path
    const sqlStatementsPath = path.join(__dirname, 'sql-statements')
    
    // Use the Factory to define the SQL data source strategy
    const sqlStrategy = SQLLambdaModelDataSourceStrategyFactory.fromCustomSqlFiles(
      // File paths to all SQL statements
      fs.readdirSync(sqlStatementsPath).map(file => path.join(sqlStatementsPath, file)),
       // Move your connection information and VPC config into here
      {
        dbType: 'MYSQL',
        name: 'MySQLSchemaDefinition',
        dbConnectionConfig: { ... },
        vpcConfiguration: { ... },
      }
    )

    const amplifyApi = new AmplifyGraphqlApi(this, "AmplifyApi", {
      definition: AmplifyGraphqlDefinition.fromFilesAndStrategy(
        path.join(__dirname, "schema.graphql"),
        sqlStrategy
      ),
      authorizationModes: {
        defaultAuthorizationMode: 'API_KEY',
        apiKeyConfig: {
          expires: cdk.Duration.days(30)
        }
      }
    })
  }
}

次に、カスタム SQL ステートメントを参照するように GraphQL スキーマを更新します。カスタム SQL ステートメントは、ファイルのベースネーム (拡張子「.sql」を除いたファイル名) に基づいて参照できます。

type Meal {
  id: Int!
  name: String!
}

type Query {
#- listMeals(contains: String!): [Meal] @sql(statement: "SELECT * FROM Meals WHERE name LIKE :contains") @auth(rules: [{ allow: public }])
  listMeals(contains: String!): [Meal] @sql(reference: "listMeals") @auth(rules: [{ allow: public }])
}

これで、API を反復処理するスピードが格段に速くなりました。例えば、GraphQL スキーマに以下の内容を追加して、食事を作成する新しい Mutation を追加してみましょう。

type Mutation {
   createMeal(id: Int!, name: String!): AWSJSON @sql(reference: "createMeal") @auth(rules: [{ allow: public }])
}

注:MySQL から生のレスポンスを取得する手っ取り早い方法として、AWSJSON 型を返します。これは、オプトインできる追加の型安全性を提供しません。同じリクエスト内で変更または作成されたアイテムを返す方法については、ドキュメントを参照してください。

次に、新しい lib/sql-statements/createMeal.sql ファイルを以下の内容で作成します。

INSERT INTO Meals (id, name)
VALUES ( :id, :name );

これで、AWS CDK アプリを再度デプロイすることができ、新しい Mutation を持つ API が利用可能になります。

cdk deploy

クリーンアップ

生成されたリソースをすべてクリーンアップします。ターミナルで以下の AWS CDK CLI コマンドを実行します。

cdk destroy

まとめ

このガイドでは、AWS CDK を使用して新しい GraphQL API を作成し、SQL ステートメントを実行する Query とMutation を作成し、それらを認可ルールで保護しました。Amplify GraphQL API の MySQL や PostgreSQL データベースとの統合の詳細については、既存の MySQL や PostgreSQL データベースへの API の接続に関するドキュメントを確認してください。

本記事は「Create a GraphQL API for any existing MySQL and PostgreSQL database」を翻訳したものです。

翻訳者について

稲田 大陸

AWS Japan で働く筋トレが趣味のソリューションアーキテクト。普段は製造業のお客様を中心に技術支援を行っています。好きな AWS サービスは Amazon Location Service と AWS Amplify で、日本のお客様向けに Amazon Location Service の解説ブログなどを執筆しています。