翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
での 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 データベース機能の両方を必要とする APIs の作成
GraphQL ミューテーションとクエリによるデータオペレーションの管理
安全なデータベースアクセスパターンの実装
このチュートリアルでは、以下について説明します。
Aurora Serverless v2 クラスターをセットアップする
Data API 機能を有効にする
データベース構造の作成と設定
データベースオペレーションの GraphQL スキーマを定義する
クエリとミューテーションのリゾルバーを実装する
適切な入力サニタイズによるデータアクセスの保護
GraphQL インターフェイスを使用してさまざまなデータベースオペレーションを実行する
トピック
データベースクラスターのセットアップ
Amazon RDS データソースを に追加する前に AWS AppSync、まず Aurora Serverless v2 クラスターで Data API を有効にし、 を使用してシークレットを設定する必要がありますAWS Secrets Manager。Aurora Serverless v2 クラスターは、以下を使用して作成できます AWS CLI。
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 ステートメントを順番に実行します。結果はその同じ順序で返されます。これはミューテーションであるため、挿入後に select ステートメントを実行し、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 } }
Query.listPetsByPriceRange のマッピングテンプレートwhere price > :MIN and price < :MAX
に があった SQL WHERE 述語を、次の GraphQL クエリで活用します。
query petsByPriceRange { listPetsByPriceRange(min:1, max:11) { id type price } }
price が 1 ドル以上、10 ドル未満のレコードのみが表示されます。最後に、以下のようにクエリを実行して個々のレコードを取得できます。
query onePet { getPet(id:ID_PLACEHOLDER){ id type price } }
データアクセスを保護する
SQL インジェクションは、データベースアプリケーションのセキュリティ上の脆弱性です。これは、攻撃者がユーザー入力フィールドを通じて悪意のある SQL コードを挿入した場合に発生します。これにより、データベースデータへの不正アクセスが可能になります。を使用して SQL インジェクション攻撃から保護する前に、すべてのユーザー入力variableMap
を慎重に検証してサニタイズすることをお勧めします。変数マップが使用されていない場合は、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("'", "''")) } }
エスケープ文字列の使用
一重引用符を使用して、 などの SQL ステートメントの文字列リテラルの開始と終了をマークします'some string value'
。1 つ以上の一重引用符 ('
) を含む文字列値を文字列内で使用するには、それぞれを 2 つの一重引用符 (''
) に置き換える必要があります。例えば、入力文字列が Nadia's dog
の場合、SQL ステートメントでは次のようにエスケープします。
update Pets set type='Nadia''s dog' WHERE id='1'