AWS AppSyncの Data API での Aurora PostgreSQL の使用 - AWS AppSync GraphQL

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

AWS AppSyncの Data API での Aurora PostgreSQL の使用

を使用して GraphQL API を Aurora PostgreSQL データベースに接続する方法について説明します AWS AppSync。この統合により、GraphQL オペレーションを介して SQL クエリとミューテーションを実行することで、スケーラブルなデータ駆動型アプリケーションを構築できます。 は、Data API で有効になっている Amazon Aurora クラスターに対して SQL ステートメントを実行するためのデータソース AWS AppSync を提供します。 AWS AppSync リゾルバーを使用して、GraphQL クエリ、ミューテーション、サブスクリプションを使用して、データ API に対して SQL ステートメントを実行できます。

このチュートリアルを開始する前に、 AWS サービスと GraphQL の概念に関する基本的な知識が必要です。

注記

このチュートリアルでは、US-EAST-1 リージョンを使用しています。

Aurora PostgreSQL データベースをセットアップする

Amazon RDS データソースを に追加する前に AWS AppSync、次の操作を行います。

  1. Aurora Serverless v2 クラスターで Data API を有効にします。

  2. を使用してシークレットを設定する AWS Secrets Manager

  3. 次の AWS CLI コマンドを使用してクラスターを作成します。

    aws rds create-db-cluster \ --db-cluster-identifier appsync-tutorial \ --engine aurora-postgresql \ --engine-version 16.6 \ --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \ --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD \ --enable-http-endpoint

これにより、クラスターの ARN が返されます。クラスターを作成したら、次の AWS CLI コマンドを使用して Serverless v2 インスタンスを追加する必要があります。

aws rds create-db-instance \ --db-cluster-identifier appsync-tutorial \ --db-instance-identifier appsync-tutorial-instance-1 \ --db-instance-class db.serverless \ --engine aurora-postgresql
注記

これらのエンドポイントがアクティブ化されるまでに時間がかかります。ステータスは、クラスターの接続とセキュリティタブの RDS コンソールで確認できます。

次の AWS CLI コマンドを使用して、クラスターのステータスを確認します。

aws rds describe-db-clusters \ --db-cluster-identifier appsync-tutorial \ --query "DBClusters[0].Status"

AWS Secrets Manager コンソールまたは を使用して、前のステップCOMPLEX_PASSWORDUSERNAMEと を使用して、次のような入力ファイル AWS CLI を使用してシークレットを作成します。

{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }

これをパラメータとして に渡します AWS CLI。

aws secretsmanager create-secret \ --name appsync-tutorial-rds-secret \ --secret-string file://creds.json

これにより、シークレットの ARN が返されます。 AWS AppSync コンソールでデータソースを作成するときは、Aurora Serverless v2 クラスターの ARN と のシークレットを書き留めます。

データベースとテーブルの作成

まず、 という名前のデータベースを作成しますTESTDB。PostgreSQL では、データベースはテーブルやその他の SQL オブジェクトを保持するコンテナです。API に追加 AWS AppSync する前に、Aurora Serverless v2 クラスターが正しく設定されていることを確認します。まず、次のように --sqlパラメータを使用して TESTDB データベースを作成します。

aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \ --sql "create DATABASE \"testdb\"" \ --database "postgres"

これがエラーなしで実行されたら、create table コマンドを使用して 2 つのテーブルを追加します。

aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \ --database "testdb" \ --sql 'create table public.todos (id serial constraint todos_pk primary key, description text not null, due date not null, "createdAt" timestamp default now());' aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333 ISN:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333 ISN:secret:appsync-tutorial-rds-secret" \ --database "testdb" \ --sql 'create table public.tasks (id serial constraint tasks_pk primary key, description varchar, "todoId" integer not null constraint tasks_todos_id_fk references public.todos);'

成功したら、クラスターを API のデータソースとして追加します。

GraphQL スキーマを作成する

Aurora Serverless v2 Data API が設定されたテーブルで実行されたので、GraphQL スキーマを作成します。API 作成ウィザードを使用して既存のデータベースからテーブル設定をインポートすることで、API をすばやく作成できます。

開始方法

  1. AWS AppSync コンソールで API の作成を選択し、Amazon Aurora クラスターから開始します。

  2. [API 名] などの API の詳細を指定し、API を生成するデータベースを選択します。

  3. データベースを選択します。必要に応じてリージョンを更新し、Aurora クラスターと TESTDB データベースを選択します。

  4. シークレットを選択し、[インポート] を選択します。

  5. テーブルが検出されたら、型名を更新します。TodosTodo に変更し、TasksTask に変更します。

  6. [スキーマをプレビュー] を選択して、生成されたスキーマをプレビューします。スキーマは以下のようになります。

    type Todo { id: Int! description: String! due: AWSDate! createdAt: String } type Task { id: Int! todoId: Int! description: String }
  7. ロールの場合、 で AWS AppSync 新しいロールを作成するか、次のようなポリシーで作成できます。

    JSON
    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-data:ExecuteStatement" ], "Resource": [ "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial", "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial:*" ] }, { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret", "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret:*" ] } ] }

    このポリシーには、ロールにアクセス許可を付与するステートメントが 2 つあることに注意してください。最初のリソースは Aurora クラスターで、2 番目のリソースは AWS Secrets Manager ARN です。

    [次へ] を選択し、設定の詳細を確認して [API の作成] を選択します。これで完全に動作する API が作成されました。API の全詳細は、[スキーマ] ページで確認できます。

RDS のリゾルバー

API 作成フローでは、型とやり取りするリゾルバーが自動的に作成されました。スキーマページを見ると、リゾルバーの一部が表示されます。

  • Mutation.createTodo フィールドで todo を作成する。

  • Mutation.updateTodo フィールドで todo を更新する。

  • Mutation.deleteTodo フィールドで todo を削除する。

  • Query.getTodo フィールドで 単一の todo を取得する。

  • Query.listTodos フィールドで todos をすべて一覧表示する。

Task 型にアタッチされた類似のフィールドとリゾルバーがあります。いくつかのリゾルバーを詳しく見ていきましょう。

Mutation.createTodo

AWS AppSync コンソールのスキーマエディタの右側で、 testdbの横にある を選択しますcreateTodo(...): Todo。リゾルバーコードは、rds モジュールの insert 関数を使用して、todos テーブルにデータを追加する挿入ステートメントを動的に作成します。ここでは Postgres を使用しているため、挿入されたデータをreturning ステートメントを活用して返すことができます。

次のリゾルバーを更新して、 dueフィールドDATEのタイプを適切に指定します。

import { util } from '@aws-appsync/utils'; import { insert, createPgStatement, toJsonObject, typeHint } from '@aws-appsync/utils/rds'; export function request(ctx) { const { input } = ctx.args; // if a due date is provided, cast is as `DATE` if (input.due) { input.due = typeHint.DATE(input.due) } const insertStatement = insert({ table: 'todos', values: input, returning: '*', }); return createPgStatement(insertStatement) } export function response(ctx) { const { error, result } = ctx; if (error) { return util.appendError( error.message, error.type, result ) } return toJsonObject(result)[0][0] }

リゾルバーを保存します。型ヒントは、入力オブジェクト内の dueDATE 型として適切にマークします。これにより、Postgres エンジンが値を適切に解釈できるようになります。次に、スキーマを更新して CreateTodo 入力から id を削除します。Postgres データベースは生成された ID を返すことができるため、次のように 1 回のリクエストで作成して返すことができます。

input CreateTodoInput { due: AWSDate! createdAt: String description: String! }

変更を加え、スキーマを更新します。クエリエディタに移動して、次のようにデータベースに項目を追加します。

mutation CreateTodo { createTodo(input: {description: "Hello World!", due: "2023-12-31"}) { id due description createdAt } }

次の結果が得られます。

{ "data": { "createTodo": { "id": 1, "due": "2023-12-31", "description": "Hello World!", "createdAt": "2023-11-14 20:47:11.875428" } } }

Query.listTodos

コンソールのスキーマエディタの右側で、listTodos(id: ID!): Todo の横にある testdb を選択します。リクエストハンドラーは select ユーティリティ関数を使用して、実行時にリクエストを動的に構築します。

export function request(ctx) { const { filter = {}, limit = 100, nextToken } = ctx.args; const offset = nextToken ? +util.base64Decode(nextToken) : 0; const statement = select({ table: 'todos', columns: '*', limit, offset, where: filter, }); return createPgStatement(statement) }

due の日付に基づいて todos をフィルタリングしたいとします。due の値を DATE にキャストするようにリゾルバーを更新しましょう。インポートのリストとリクエストハンドラーを次のように更新します。

import { util } from '@aws-appsync/utils'; import * as rds from '@aws-appsync/utils/rds'; export function request(ctx) { const { filter: where = {}, limit = 100, nextToken } = ctx.args; const offset = nextToken ? +util.base64Decode(nextToken) : 0; // if `due` is used in a filter, CAST the values to DATE. if (where.due) { Object.entries(where.due).forEach(([k, v]) => { if (k === 'between') { where.due[k] = v.map((d) => rds.typeHint.DATE(d)); } else { where.due[k] = rds.typeHint.DATE(v); } }); } const statement = rds.select({ table: 'todos', columns: '*', limit, offset, where, }); return rds.createPgStatement(statement); } export function response(ctx) { const { args: { limit = 100, nextToken }, error, result, } = ctx; if (error) { return util.appendError(error.message, error.type, result); } const offset = nextToken ? +util.base64Decode(nextToken) : 0; const items = rds.toJsonObject(result)[0]; const endOfResults = items?.length < limit; const token = endOfResults ? null : util.base64Encode(`${offset + limit}`); return { items, nextToken: token }; }

クエリエディタで、次の操作を行います。

query LIST { listTodos(limit: 10, filter: {due: {between: ["2021-01-01", "2025-01-02"]}}) { items { id due description } } }

Mutation.updateTodo

Todoupdate することも可能です。[クエリ] エディタから、id 1 の最初の Todo 項目を更新しましょう。

mutation UPDATE { updateTodo(input: {id: 1, description: "edits"}) { description due id } }

更新する項目の id を指定する必要があることに注意してください。条件を指定して、特定の条件を満たす項目のみを更新することもできます。たとえば、説明がedits次のように で始まる場合にのみ項目を編集できます。

mutation UPDATE { updateTodo(input: {id: 1, description: "edits: make a change"}, condition: {description: {beginsWith: "edits"}}) { description due id } }

create オペレーションと list オペレーションを処理したのと同じように、リゾルバーを更新して due フィールドを DATE にキャストできます。これらの変更をupdateTodo次のように に保存します。

import { util } from '@aws-appsync/utils'; import * as rds from '@aws-appsync/utils/rds'; export function request(ctx) { const { input: { id, ...values }, condition = {}, } = ctx.args; const where = { ...condition, id: { eq: id } }; // if `due` is used in a condition, CAST the values to DATE. if (condition.due) { Object.entries(condition.due).forEach(([k, v]) => { if (k === 'between') { condition.due[k] = v.map((d) => rds.typeHint.DATE(d)); } else { condition.due[k] = rds.typeHint.DATE(v); } }); } // if a due date is provided, cast is as `DATE` if (values.due) { values.due = rds.typeHint.DATE(values.due); } const updateStatement = rds.update({ table: 'todos', values, where, returning: '*', }); return rds.createPgStatement(updateStatement); } export function response(ctx) { const { error, result } = ctx; if (error) { return util.appendError(error.message, error.type, result); } return rds.toJsonObject(result)[0][0]; }

次に、以下の条件で更新を試してください。

mutation UPDATE { updateTodo( input: { id: 1, description: "edits: make a change", due: "2023-12-12"}, condition: { description: {beginsWith: "edits"}, due: {ge: "2023-11-08"}}) { description due id } }

Mutation.deleteTodo

deleteTodo ミューテーションで Tododelete できます。これはミューupdateTodoテーションと同様に機能し、次のように削除する項目の id を指定する必要があります。

mutation DELETE { deleteTodo(input: {id: 1}) { description due id } }

カスタムクエリの記述

rds モジュールユーティリティを使用して SQL ステートメントを作成しました。独自でカスタムした静的ステートメントを記述して、データベースとやり取りすることもできます。まず、スキーマを更新して CreateTask 入力から id フィールドを削除します。

input CreateTaskInput { todoId: Int! description: String }

次に、いくつかのタスクを作成します。タスクには、Todo次のように との外部キー関係があります。

mutation TASKS { a: createTask(input: {todoId: 2, description: "my first sub task"}) { id } b:createTask(input: {todoId: 2, description: "another sub task"}) { id } c: createTask(input: {todoId: 2, description: "a final sub task"}) { id } }

getTodoAndTasks次のように、 Queryというタイプに新しいフィールドを作成します。

getTodoAndTasks(id: Int!): Todo

次のように Todoタイプにtasksフィールドを追加します。

type Todo { due: AWSDate! id: Int! createdAt: String description: String! tasks:TaskConnection }

スキーマを保存します。コンソールのスキーマエディタの右側で、getTodosAndTasks(id: Int!): Todo に対して [リゾルバーをアタッチ] を選択します。Amazon RDS データソースを選択します。次のコードでリゾルバーを更新します。

import { sql, createPgStatement,toJsonObject } from '@aws-appsync/utils/rds'; export function request(ctx) { return createPgStatement( sql`SELECT * from todos where id = ${ctx.args.id}`, sql`SELECT * from tasks where "todoId" = ${ctx.args.id}`); } export function response(ctx) { const result = toJsonObject(ctx.result); const todo = result[0][0]; if (!todo) { return null; } todo.tasks = { items: result[1] }; return todo; }

このコードでは、sql タグテンプレートを使用して、実行時に動的な値を安全に渡すことができる SQL ステートメントを記述します。createPgStatement は、一度に最大 2 つの SQL リクエストを受け入れることができます。これを利用して、todo に対して 1 つのクエリを送信し、tasks に対して別のクエリを送信します。これは、JOIN ステートメントやその他の方法を使用して行うこともできます。つまり、独自の SQL ステートメントを記述してビジネスロジックを実装できるということです。クエリエディタでクエリを使用するには、次の手順を実行します。

query TodoAndTasks { getTodosAndTasks(id: 2) { id due description tasks { items { id description } } } }

クラスターの削除

重要

クラスターは完全に削除されます。このアクションを実行する前に、プロジェクトを徹底的に確認してください。

クラスターを削除するには

$ aws rds delete-db-cluster \ --db-cluster-identifier appsync-tutorial \ --skip-final-snapshot