翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
での Aurora Serverless v2 の使用 AWS AppSync
を使用して GraphQL API を Aurora Serverless データベースに接続します AWS AppSync。この統合により、GraphQL クエリ、ミューテーション、サブスクリプションを通じて SQL ステートメントを実行できます。これにより、リレーショナルデータを柔軟に操作できます。
注記
このチュートリアルでは、US-EAST-1 リージョンを使用しています。
利点
GraphQL とリレーショナルデータベースのシームレスな統合
GraphQL インターフェイスを使用して SQL オペレーションを実行する機能
Aurora Serverless v2 によるサーバーレスのスケーラビリティ
AWS Secrets Manager によるデータアクセスの保護
入力のサニタイズによる SQL インジェクションに対する保護
フィルタリングや範囲操作などの柔軟なクエリ機能
一般的なユースケース
リレーショナルデータ要件を使用したスケーラブルなアプリケーションの構築
GraphQL の柔軟性と SQL データベース機能の両方を必要とする API の作成
GraphQL ミューテーションとクエリによるデータオペレーションの管理
セキュアなデータベースアクセスパターンの実装
このチュートリアルでは、以下について説明します。
Aurora Serverless v2 クラスターを設定する
Data API 機能を有効にする
データベース構造の作成と設定
データベースオペレーションの GraphQL スキーマを定義する
クエリとミューテーションのリゾルバーを実装する
適切な入力サニタイズによりデータアクセスを保護する
GraphQL インターフェイスを使用してさまざまなデータベースオペレーションを実行する
トピック
データベースクラスターを設定する
Amazon RDS データソースを に追加する前に AWS AppSync、まず Aurora Serverless v2 クラスターで Data API を有効にし、 を使用してシークレットを設定する必要がありますAWS Secrets Manager。 AWS CLIを使用して Aurora Serverless v2 クラスターを作成することができます。
aws rds create-db-cluster \ --db-cluster-identifier appsync-tutorial \ --engine aurora-mysql \ --engine-version 8.0 \ --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \ --master-username USERNAME \ --master-user-password COMPLEX_PASSWORD \ --enable-http-endpoint
これにより、クラスターの ARN が返されます。
クラスターを作成したら、次のコマンドを使用して Aurora 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-mysql
注記
これらのエンドポイントのアクティブ化には時間がかかります。ステータスは、クラスターの [接続とセキュリティ] タブの Amazon RDS コンソールで確認できます。次の AWS CLI コマンドを使用して、クラスターのステータスを確認することもできます。
aws rds describe-db-clusters \ --db-cluster-identifier appsync-tutorial \ --query "DBClusters[0].Status"
AWS Secrets Manager コンソールまたは AWS CLI を使用してシークレットを作成し、前のステップCOMPLEX_PASSWORDの USERNAMEと を使用して次のような入力ファイルを作成できます。
{ "username": "USERNAME", "password": "COMPLEX_PASSWORD" }
これをパラメータとして に渡します AWS CLI。
aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1
これにより、シークレットの ARN が返されます。
Aurora Serverless クラスターとシークレットの ARN をメモしておいてください。これらの ARN は後でデータソースを作成するときに AppSync コンソールで使用します。
Data API を有効にする
RDS のドキュメントの指示に従うことで、クラスターで Data API を有効にできます。Data API は AppSync データソースとして追加する前に有効にする必要があります。
データベースとテーブルを作成する
Data API を有効にしたら、 AWS CLIの aws
rds-data execute-statement コマンドで使用できるようになります。これにより、Aurora Serverless クラスターが正しく設定されていることを AppSync API への追加前に確認できます。まず、--sql のようなパラメータで TESTDB というデータベースを作成します。
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 --sql "create DATABASE TESTDB"
これがエラーなしで実行されたら、create table コマンドを使用してテーブルを追加します。
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"
すべてが問題なく実行されたら、AppSync API のデータソースとしてクラスターを追加する手順に進むことができます。
GraphQL スキーマ
Aurora Serverless Data API がテーブルで起動されて実行中になったところで、次は GraphQL スキーマを作成し、ミューテーションとサブスクリプションを実行するためのリゾルバーをアタッチします。 AWS AppSync コンソールで新しい API を作成し、スキーマページに移動して、次のように入力します。
type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }
スキーマを保存し、[データソース] ページに移動して、新しいデータソースを作成します。データソースタイプとして [Relational database (リレーショナルデータベース)] を選択し、データソース名としてわかりやすい名前を入力します。前回の手順で作成したデータベースの名前と、そのデータベースを作成したクラスターのクラスター ARN を使用します。[Role (ロール)] では、AppSync により新しいロールを作成するか、以下のようなポリシーによりロールを作成できます。
このポリシーには、ロールにアクセス許可を付与する 2 つのステートメントがあります。最初のリソースは Aurora Serverless クラスターで、2 番目のリソースは AWS Secrets Manager ARN です。作成をクリックする前に AppSync データソース構成内の両方の ARN を指定する必要があります。
これをパラメータとして に渡します AWS CLI。
aws secretsmanager create-secret \ --name HttpRDSSecret \ --secret-string file://creds.json \ --region us-east-1
これにより、シークレットの ARN が返されます。 AWS AppSync コンソールでデータソースを作成するときは、Aurora Serverless クラスターの ARN と、後で のシークレットを書き留めます。
データベース構造を構築する
Data API を有効にしたら、 AWS CLIの aws
rds-data execute-statement コマンドで使用できるようになります。これにより、API に追加する前に Aurora Serverless v2 クラスターが正しく設定されます AWS AppSync 。まず、次のように --sql パラメータを使用して TESTDB というデータベースを作成します。
aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret" \ --region us-east-1 \ --sql "create DATABASE TESTDB"
これがエラーなしで実行されたら、次の create table コマンドを使用してテーブルを追加します。
aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:http-endpoint-test" \ --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:testHttp2-AmNvc1" \ --region us-east-1 \ --sql "create table Pets(id varchar(200), type varchar(200), price float)" \ --database "TESTDB"
API インターフェイスを設計する
Aurora Serverless v2 Data API がテーブルを使用して実行中になった後、GraphQL スキーマを作成し、ミューテーションとサブスクリプションを実行するためのリゾルバーをアタッチします。 AWS AppSync コンソールで新しい API を作成し、コンソールのスキーマページに移動して、次のように入力します。
type Mutation { createPet(input: CreatePetInput!): Pet updatePet(input: UpdatePetInput!): Pet deletePet(input: DeletePetInput!): Pet } input CreatePetInput { type: PetType price: Float! } input UpdatePetInput { id: ID! type: PetType price: Float! } input DeletePetInput { id: ID! } type Pet { id: ID! type: PetType price: Float } enum PetType { dog cat fish bird gecko } type Query { getPet(id: ID!): Pet listPets: [Pet] listPetsByPriceRange(min: Float, max: Float): [Pet] } schema { query: Query mutation: Mutation }
スキーマを保存し、[データソース] ページに移動して、新しいデータソースを作成します。[データソース] タイプとして [リレーショナルデータベース] を選択し、わかりやすい名前を入力します。前回の手順で作成したデータベースの名前と、そのデータベースを作成したクラスターのクラスター ARN を使用します。ロールの場合、 に AWS AppSync 新しいロールを作成するか、次のようなポリシーで作成させることができます。
このポリシーには、ロールにアクセス許可を付与する 2 つのステートメントがあります。最初のリソースは Aurora Serverless v2 クラスターで、2 つ目は AWS Secrets Manager ARN です。Create をクリックする前に、 AWS AppSync データソース設定で両方の ARNs を指定する必要があります。
API をデータベースオペレーションに接続する
有効な GraphQL スキーマと RDS データソースを用意できたところで、リゾルバーをスキーマへの GraphQL フィールドにアタッチできます。この API は以下の機能を提供します。
-
Mutation.createPet フィールドを使用してペットを作成する
-
Mutation.updatePet フィールドを使用してペットを更新する
-
Mutation.deletePet フィールドを使用してペットを削除する
-
Query.getPet フィールドを使用して 1 つのペットを取得する
-
Query.listPets フィールドを使用してすべてのペットを一覧表示する
-
Query.listPetsByPriceRange フィールドを使用してペットを価格帯別に一覧表示する
Mutation.createPet
AWS AppSync コンソールのスキーマエディタから、右側の「 のリゾルバーのアタッチ」を選択しますcreatePet(input: CreatePetInput!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
#set($id=$utils.autoId()) { "version": "2018-05-29", "statements": [ "insert into Pets VALUES (:ID, :TYPE, :PRICE)", "select * from Pets WHERE id = :ID" ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }
SQL ステートメントは、statements 配列内での順序に基づいて順番に実行されます。結果はその同じ順序で返されます。これはミューテーションなので、挿入の後に選択ステートメントを実行して、GraphQL レスポンスマッピングテンプレートに入力するための、コミットされた値を取得します。
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
ステートメントには 2 つの SQL クエリがあるため、データベースから返される行列の 2 番目の結果を $utils.rds.toJsonString($ctx.result))[1][0]) で指定する必要があります。
Mutation.updatePet
AWS AppSync コンソールのスキーマエディタから、 のリゾルバーをアタッチを選択しますupdatePet(input: UpdatePetInput!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
{ "version": "2018-05-29", "statements": [ $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"), $util.toJson("select * from Pets WHERE id = :ID") ], "variableMap": { ":ID": "$ctx.args.input.id", ":TYPE": $util.toJson($ctx.args.input.type), ":PRICE": $util.toJson($ctx.args.input.price) } }
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
Mutation.deletePet
AWS AppSync コンソールのスキーマエディタから、 のリゾルバーをアタッチを選択しますdeletePet(input: DeletePetInput!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID"), $util.toJson("delete from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.input.id" } }
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
Query.getPet
スキーマに対してミューテーションが作成されたところで、3 つのクエリを接続して、個々の項目、リストを取得し、SQL フィルタを適用する方法を紹介します。 AWS AppSync コンソールのスキーマエディタから、 のリゾルバーをアタッチを選択しますgetPet(id: ID!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
{ "version": "2018-05-29", "statements": [ $util.toJson("select * from Pets WHERE id=:ID") ], "variableMap": { ":ID": "$ctx.args.id" } }
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
Query.listPets
AWS AppSync コンソールのスキーマエディタから、右側の「 のリゾルバーのアタッチ」を選択しますgetPet(id: ID!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
{ "version": "2018-05-29", "statements": [ "select * from Pets" ] }
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
Query.listPetsByPriceRange
AWS AppSync コンソールのスキーマエディタから、右側の「 のリゾルバーのアタッチ」を選択しますgetPet(id: ID!): Pet。[RDS データソース] を選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。
{ "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.max), ":MIN": $util.toJson($ctx.args.min) } }
[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
API を使用してデータを変更する
すべてのリゾルバーを SQL ステートメントで設定し、GraphQL API を Serverless Aurora Data API に接続したところで、ミューテーションとクエリの実行を開始できます。 AWS AppSync コンソールで、クエリタブを選択し、次のように入力して Pet を作成します。
mutation add { createPet(input : { type:fish, price:10.0 }){ id type price } }
レスポンスには、id、type、price が含まれています。
{ "data": { "createPet": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "type": "fish", "price": "10.0" } } }
この項目は updatePet ミューテーションを実行することで変更できます。
mutation update { updatePet(input : { id: ID_PLACEHOLDER, type:bird, price:50.0 }){ id type price } }
事前のcreatePetオペレーションから返されたidを使用したことに注意してください。リゾルバーが $util.autoId() を利用したため、これがレコードに固有の値になります。同様の方法でレコードを削除できます。
mutation delete { deletePet(input : {id:ID_PLACEHOLDER}){ id type price } }
最初のミューテーションで price に異なる値を使用してレコードをいくつか作成したら、クエリをいくつか実行します。
データを取得する
引き続きコンソールの [クエリ] タブで、以下のステートメントを使用して、作成したすべてのレコードを一覧表示します。
query allpets { listPets { id type price } }
次の GraphQL クエリを使用して、Query.listPetsByPriceRange のマッピングテンプレートに where price > :MIN and price <
:MAX があった、SQL の WHERE 述語を活用します。
query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }
price が 1 ドル以上、10 ドル未満のレコードのみが表示されます。最後に、以下のようにクエリを実行して個々のレコードを取得できます。
query onePet { getPet(id:ID_PLACEHOLDER){ id type price } }
セキュアなデータアクセス
SQL インジェクションは、データベースアプリケーションのセキュリティ脆弱性を利用して行われます。これは、攻撃者がユーザー入力フィールドを通じて悪意のある SQL コードを挿入した場合に発生します。これにより、データベースデータへの不正アクセスが可能になります。variableMap を使用して SQL インジェクション攻撃から保護する前に、すべてのユーザー入力を慎重に検証してサニタイズすることをお勧めします。変数マップを使用しない場合、GraphQL 操作の引数をサニタイズする責任があります。そのための 1 つの方法は、Data API に対して SQL ステートメントを実行する前に、リクエストマッピングテンプレートに入力固有の検証手順を提供することです。listPetsByPriceRange 例のリクエストマッピングテンプレートを変更する方法を見てみましょう。ユーザー入力だけに頼るのではなく、以下のことが可能です。
#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice)) #set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice)) #if (!$validMaxPrice || !$validMinPrice) $util.error("Provided price input is not valid.") #end { "version": "2018-05-29", "statements": [ "select * from Pets where price > :MIN and price < :MAX" ], "variableMap": { ":MAX": $util.toJson($ctx.args.maxPrice), ":MIN": $util.toJson($ctx.args.minPrice) } }
Data API に対してリゾルバーを実行するときに不正な入力から保護するもう 1 つの方法は、プリペアードステートメントをストアドプロシージャおよびパラメータ化された入力と共に使用することです。例えば、listPets のリゾルバーで、select をプリペアードステートメントとして実行する以下のプロシージャを定義します。
CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END
これは、Aurora Serverless v2 インスタンスで作成します。
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \ --schema "mysql" --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx" \ --region us-east-1 --database "DB_NAME" \ --sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"
その結果、listPets のリゾルバーコードは、ストアドプロシージャを呼び出すシンプルなものになりました。少なくとも、文字列入力では一重引用符をエスケープする必要があります。
#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type)) #if (!$validType) $util.error("Input for 'type' is not valid.", "ValidationError") #end { "version": "2018-05-29", "statements": [ "CALL listPets(:type)" ] "variableMap": { ":type": $util.toJson($ctx.args.type.replace("'", "''")) } }
エスケープ文字列の使用
'some string value' のように、一重引用符を使用して、SQL ステートメントの文字列リテラルの開始と終了を表します。1 つ以上の一重引用符 (') を含む文字列値を文字列内で使用するには、それぞれを 2 つの一重引用符 ('') に置き換える必要があります。例えば、入力文字列が Nadia's dog の場合、SQL ステートメントでは次のようにエスケープします。
update Pets set type='Nadia''s dog' WHERE id='1'