DynamoDB JavaScript リゾルバーを使用したシンプルなポストアプリケーションの作成
このチュートリアルでは、Amazon DynamoDB テーブルを AWS AppSync にインポートして接続し、独自のアプリケーションで活用できる JavaScript パイプラインリゾルバーを使用して完全に機能する GraphQL API を構築します。
AWS AppSync コンソールを使用して Amazon DynamoDB リソースをプロビジョニングし、リゾルバーを作成し、それらをデータソースに接続します。また、GraphQL ステートメントを使用して Amazon DynamoDB データベースへの読み取りと書き込みを行うことができ、リアルタイムデータをサブスクライブできます。
GraphQL ステートメントが Amazon DynamoDB オペレーションに変換され、レスポンスが GraphQL に変換されるように、特定のステップを完了しておく必要があります。このチュートリアルでは、いくつかの実際のシナリオおよびデータアクセスパターンを使用して、その設定手順の概要を示します。
GraphQL API の作成
AWS AppSync で GraphQL API を作成するには
-
AppSync コンソールを開き、[API を作成] を選択します。
-
[最初から設計] を選択し、[次へ] を選択します。
-
API
PostTutorialAPIに名前を付け、[次へ] を選択します。残りのオプションはデフォルト設定値のまま、レビューページに移動し、Createを選択します。
AWS AppSync コンソールによって、新しい GraphQL API が作成されます。デフォルトでは、API キー認証モードを使用します。このコンソールを使用して、残りの GraphQL API をセットアップでき、このチュートリアルの残りの部分でクエリを実行できます。
基本的な Post API の定義
ここで、GraphQL API があるので、ポストデータの基本的な作成、取得、削除を許可する基本スキーマをセットアップできます。
スキーマにデータを追加するには
-
API で [スキーマ] タブを選択します。
-
Postオブジェクトを追加および取得するためのPostタイプと操作addPostを定義するスキーマを作成します。[スキーマ] ペインで、内容を次のコードに置き換えます。schema { query: Query mutation: Mutation } type Query { getPost(id: ID): Post } type Mutation { addPost( id: ID! author: String! title: String! content: String! url: String! ): Post! } type Post { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! } -
[Save Schema] を選択します。
Amazon DynamoDB テーブルのセットアップ
AWS AppSync コンソールは、独自のリソースを Amazon DynamoDB テーブルに保存するのに必要な AWS リソースをプロビジョニングするのに役立ちます。このステップでは、投稿を保存する Amazon DynamoDB テーブルを作成します。また、後で使用するセカンダリインデックスも設定します。
Amazon DynamoDB テーブルを作成するには
-
[スキーマ] ページで、[リソースを作成] を選択します。
-
[既存のタイプを使用] を選択し、
Postタイプを選択します。 -
[セカンダリインデックス] セクションで、[インデックスを追加] を選択します。
-
インデックス
author-indexの名前 -
Primary keyをauthorに、SortキーをNoneに設定します。 -
GraphQL の自動生成を無効にします。この例では、リゾルバーを自分で作成します。
-
[作成] を選択します。
これで、PostTable という新しいデータソースができました。サイドタブの [データソース] にアクセスすると確認できます。このデータソースを使用して、クエリとミューテーションを Amazon DynamoDB テーブルにリンクします。
addPost リゾルバー (DAmazon DynamoDB PutItem) のセットアップ
AWS AppSync が Amazon DynamoDB テーブルを認識したら、リゾルバーを定義することで、そのテーブルを個々のクエリおよびミューテーションにリンクできます。最初に作成するリゾルバーは addPost リゾルバーです。このリゾルバーによって、ユーザーが Amazon DynamoDB テーブルにポストを作成できるようになります。パイプラインリゾルバーには以下のコンポーネントがあります。
-
リゾルバーをアタッチする、GraphQL スキーマ内の場所。この例では、
createPost型のMutationフィールドにリゾルバーをセットアップしています。このリゾルバーは、呼び出し元がミューテーション{ addPost(...){...} }を呼び出したときに呼び出されます。 -
このリゾルバーで使用するデータソース。この例では、
post-table-for-tutorialDynamoDB テーブルにエントリを追加できるように、前に定義した データソースを使用します。 -
リクエストハンドラー。リクエストハンドラーは、呼び出し元からの受信リクエストに対応して、それを DynamoDB に対して実行するための AWS AppSync 用のインストラクションに変換することです。
-
レスポンスハンドラー。レスポンスハンドラーの仕事は、DynamoDB からのレスポンスに対応して、それを GraphQL で想定されているものに変換し直すことです。これは、DynamoDB でのデータのシェイプが GraphQL での
Post型と異なる場合に便利です。ただし、この例では、両方のシェイプが同じであるため、データをそのまま渡します。
リゾルバーをセットアップするには
-
API で [スキーマ] タブを選択します。
-
リゾルバー ペインで、
Mutation型の下にあるaddPostフィールドを探し、「アタッチ」を選択します。 -
データソースを選択し、[作成] を選択します。
-
コードエディターで、コードを次のスニペットに置き換えます。
import { util } from '@aws-appsync/utils' import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { const item = { ...ctx.arguments, ups: 1, downs: 0, version: 1 } const key = { id: ctx.args.id ?? util.autoId() } return ddb.put({ key, item }) } export function response(ctx) { return ctx.result } -
[保存] を選択します。
注記
このコードでは、DynamoDB リクエストを簡単に作成できる DynamoDB モジュール utils を使用します。
AWS AppSync には util.autoId() という自動 ID 生成ユーティリティが付属しており、これを使用して新しい投稿の ID を生成します。ID を指定しないと、ユーティリティによって自動的に ID が生成されます。
const key = { id: ctx.args.id ?? util.autoId() }
JavaScript で使用できるユーティリティの詳細については、「リゾルバーと関数用の JavaScript ランタイム機能」を参照してください。
ポストを追加する API の呼び出し
リゾルバーが設定されたので、AWS AppSync は 受信した addPostミューテーションを Amazon DynamoDB PutItem オペレーションに変換できます。ユーザーはミューテーションを実行してテーブルに何かを入れることができるようになりました。
オペレーションを実行するには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに追加します。
mutation addPost { addPost( id: 123, author: "AUTHORNAME" title: "Our first post!" content: "This is our first post." url: "https://aws.amazon.com/appsync/" ) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
addPost] を選択します。新しく作成されたポストの結果が、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。{ "data": { "addPost": { "id": "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }
次の説明は、何が起こったかを示しています。
-
AWS AppSync が
addPostミューテーションリクエストを受け取りました。 -
AWS AppSync がリゾルバーのリクエストハンドラーを実行しました。
ddb.put関数は以下のようなPutItemリクエストを作成します。{ operation: 'PutItem', key: { id: { S: '123' } }, attributeValues: { downs: { N: 0 }, author: { S: 'AUTHORNAME' }, ups: { N: 1 }, title: { S: 'Our first post!' }, version: { N: 1 }, content: { S: 'This is our first post.' }, url: { S: 'https://aws.amazon.com/appsync/' } } } -
AWS AppSync はこの値を使用して Amazon DynamoDB
PutItemリクエストを生成して実行します。 -
AWS AppSync が、
PutItemリクエストの結果を取り込んで、それを GraphQL 型に変換し直しました。{ "id" : "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups" : 1, "downs" : 0, "version" : 1 } -
レスポンスハンドラーは結果をすぐに返します (
return ctx.result)。 -
最終結果は GraphQL のレスポンスに表示されます。
getPost リゾルバー (Amazon DynamoDB GetItem) のセットアップ
これで、 Amazon DynamoDB テーブルにデータを追加できるようになりました。次は、からデータを取得できるように、getPost クエリを設定する必要があります。そのためには、別のリゾルバーを設定します。
リソースを追加するには、次の手順に従います。
-
API で [スキーマ] タブを選択します。
-
右側の [リゾルバー] ペインで、
QueryタイプのgetPostフィールドを見つけて [アタッチ] を選択します。 -
データソースを選択し、[作成] を選択します。
-
コードエディタで、コードを次のスニペットに置き換えます。
import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { return ddb.get({ key: { id: ctx.args.id } }) } export const response = (ctx) => ctx.result -
リゾルバーを保存します。
注記
このリゾルバーでは、レスポンスハンドラーに矢印関数式を使用しています。
投稿を取得する API の呼び出し
これで、リゾルバーがセットアップされたので、AWS AppSync は受信 getPost クエリを Amazon DynamoDB GetItem オペレーションに変換する方法を知っていることになります。次は、先ほど作成したポストを取得するクエリを実行します。
クエリを実行するには
-
API で [クエリ] タブを選択します。
-
クエリ ペインで次のコードを追加し、投稿を作成した後にコピーした ID を使用します。
query getPost { getPost(id: "123") { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
getPost] を選択します。新しく作成されたポストの結果が、クエリペインの右側にある結果ペインに表示されます。 -
Amazon DynamoDB から取得された投稿が、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。
{ "data": { "getPost": { "id": "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }
別の方法として、次の例を指定します。
query getPost { getPost(id: "123") { id author title } }
getPost クエリが id、author、title のみを必要とする場合は、DynamoDB から AWS AppSync への不要なデータ転送を避けるため、プロジェクション式を使用して DynamoDB テーブルから必要な属性のみを指定するようにリクエスト関数を変更できます。例えば、リクエスト関数は以下のスニペットのようになっているかもしれません。
import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { return ddb.get({ key: { id: ctx.args.id }, projection: ['author', 'id', 'title'], }) } export const response = (ctx) => ctx.result
SelectionSetList を getPost と一緒に使用すると、expression を表すこともできます。
import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { const projection = ctx.info.selectionSetList.map((field) => field.replace('/', '.')) return ddb.get({ key: { id: ctx.args.id }, projection }) } export const response = (ctx) => ctx.result
updatePost ミューテーション (Amazon DynamoDB UpdateItem) の作成
これで、Amazon DynamoDB 内の Post オブジェクトを作成および取得できるようになりました。次は、オブジェクトを更新できるように、新しいミューテーションを設定します。すべてのフィールドを指定する必要がある addPost ミューテーションと比較すると、このミューテーションでは変更するフィールドのみを指定できます。また、変更するバージョンを指定できる新しい expectedVersion 引数が導入されました。オブジェクトの最新バージョンを変更していることを確認する条件を設定します。そのためには、UpdateItem Amazon DynamoDB operation.sc を使用します。
リゾルバーを更新するには
-
API で [スキーマ] タブを選択します。
-
[スキーマ] ペインの
Mutationタイプを次のように変更して、新しいupdatePostミューテーションを追加します。type Mutation { updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( id: ID author: String! title: String! content: String! url: String! ): Post! } -
[Save Schema] を選択します。
-
右側の [リゾルバー] ペインで、
Mutation型の新しく作成されたupdatePostフィールドを見つけて、[アタッチ] を選択します。以下のスニペットを使用して新しいリゾルバーを作成します。import { util } from '@aws-appsync/utils'; import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { id, expectedVersion, ...rest } = ctx.args; const values = Object.entries(rest).reduce((obj, [key, value]) => { obj[key] = value ?? ddb.operations.remove(); return obj; }, {}); return ddb.update({ key: { id }, condition: { version: { eq: expectedVersion } }, update: { ...values, version: ddb.operations.increment(1) }, }); } export function response(ctx) { const { error, result } = ctx; if (error) { util.appendError(error.message, error.type); } return result; -
加えた変更を保存します。
このリゾルバーは ddb.update を使用して Amazon DynamoDB の作成 UpdateItem を行います。Amazon DynamoDB では、項目全体が書き込まれるのではなく、特定の属性が更新されるようにします。これを行うには、Amazon DynamoDB の更新式を使用します。
ddb.update 関数はキーと更新オブジェクトを引数として取得します。次に、入力された引数の値をチェックします。値がnullに設定されたら、DynamoDB remove オペレーションを使用して、値を DynamoDB 項目から削除する必要があることを通知します。
また、新しい condition セクションがあります。条件式により、オペレーションが実行される前に、Amazon DynamoDB 内の既存のオブジェクトの状態に基づいて、そのリクエストが成功するかどうかを AWS AppSync と Amazon DynamoDB に指示できます。この例では、Amazon DynamoDB に現在ある項目の version フィールドが expectedVersion 引数と厳密に一致する場合にのみ、UpdateItem リクエストが成功するように指示しています。項目が更新されたら、version の値をインクリメントする必要があります。これは操作機能 increment を使えば簡単に行えます。
条件式の詳細については、「条件式リファレンス」ドキュメントを参照してください。
UpdateItem リクエストの詳細については、「UpdateItem」ドキュメントと「DynamoDB モジュール」のドキュメントを参照してください。
更新式の記述方法の詳細については、「DynamoDB UpdateExpressions」のドキュメントを参照してください。
API を呼び出して投稿を更新する
新しいリゾルバーで Post オブジェクトを更新してみましょう。
オブジェクトを更新するには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation updatePost { updatePost( id:123 title: "An empty story" content: null expectedVersion: 1 ) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
updatePost] を選択します。 -
Amazon DynamoDB で更新された投稿が、[クエリ] ペインの右側にある [結果] ペインに表示されます。これは次のように表示されます。
{ "data": { "updatePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 2 } } }
このリクエストでは、title と content フィールドのみを更新するように AWS AppSync と Amazon DynamoDB に指示しています。その他のフィールドは元のままです (増分する version フィールドは除く)。title 属性を新しい値に設定し、ポストから content 属性を削除しています。author、urlups、downs の各フィールドは変更されません。リクエストはまったく同じままで、このミューテーションをもう一度実行してみます。次のようなレスポンスが表示されます。
{ "data": { "updatePost": null }, "errors": [ { "path": [ "updatePost" ], "data": null, "errorType": "DynamoDB:ConditionalCheckFailedException", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 1RR3QN5F35CS8IV5VR4OQO9NNBVV4KQNSO5AEMVJF66Q9ASUAAJG)" } ] }
このリクエストは、条件式が falseと評価されるため失敗します。
-
このリクエストを最初に実行したときに、Amazon DynamoDB 内のこの投稿の
versionフィールドの値は1であり、expectedVersion引数と一致していました。このリクエストは成功し、Amazon DynamoDB でversionフィールドが2に増分されました。 -
このリクエストを 2 回目に実行したときに、Amazon DynamoDB 内のこのポストの
versionフィールドの値は2であり、expectedVersion引数と一致していませんでした。
このパターンは通常、「楽観的ロック」と呼ばれます。
投票ミューテーションの作成 (Amazon DynamoDB UpdateItem)
Post タイプには、賛成票と反対票を記録できるようにする ups と downs フィールドが含まれます。ただし、現時点では API ではこれらに対して何も実行できません。投稿に賛成および反対するための、いくつかのミューテーションを追加してみましょう。
ミューテーションを追加するには
-
API で [スキーマ] タブを選択します。
-
[スキーマ] ペインの
Mutation型を変更してDIRECTION列挙型を追加し、新しい投票ミューテーションを追加します。type Mutation { vote(id: ID!, direction: DIRECTION!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( id: ID, author: String!, title: String!, content: String!, url: String! ): Post! } enum DIRECTION { UP DOWN } -
[Save Schema] を選択します。
-
右側の [リゾルバー] ペインで、
Mutation型の新しく作成されたvoteフィールドを見つけて、[アタッチ] を選択します。コードを作成して次のスニペットに置き換えることで、新しいリゾルバーを作成します。import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const field = ctx.args.direction === 'UP' ? 'ups' : 'downs'; return ddb.update({ key: { id: ctx.args.id }, update: { [field]: ddb.operations.increment(1), version: ddb.operations.increment(1), }, }); } export const response = (ctx) => ctx.result; -
加えた変更を保存します。
ポストに賛成および反対するための API の呼び出し
これで、新しいリゾルバーがセットアップされたので、AWS AppSync AppSync は受信 upvotePost または downvoteミューテーションを Amazon DynamoDB の UpdateItem オペレーションに変換する方法を知っていることになり、これで、先ほど作成したポストに賛成または反対するミューテーションを実行できるようになりました。
ミューテーションを実行するには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation votePost { vote(id:123, direction: UP) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
votePost] を選択します。 -
Amazon DynamoDB で更新された投稿が、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。
{ "data": { "vote": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 6, "downs": 0, "version": 4 } } } -
もう何回か [実行] を選択します。このクエリを実行するたびに、
upsフィールドとversionフィールドが1つずつ増加することを確認できます。 -
クエリを変更して、別の
DIRECTIONを呼び出してください。mutation votePost { vote(id:123, direction: DOWN) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
votePost] を選択します。今度は、このクエリを実行するたびに、
downsフィールドとversionフィールドが1つずつ増加することを確認できます。
deletePost リゾルバー (Amazon DynamoDB DeleteItem) のセットアップ
次に、投稿を削除するミューテーションを作成します。そのためには、DeleteItem Amazon DynamoDB オペレーションを使用します。
ミューテーションを追加するには
-
スキーマで、「スキーマ」タブを選択します。
-
[スキーマ] ペインの
Mutation型を変更して、新しいdeletePostミューテーションを追加します。type Mutation { deletePost(id: ID!, expectedVersion: Int): Post vote(id: ID!, direction: DIRECTION!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( id: ID author: String!, title: String!, content: String!, url: String! ): Post! } -
今回は、
expectedVersionフィールドをオプションにしました。[スキーマを保存] を選択します。 -
右側の [リゾルバー] ペインで、
deleteタイプから新しく作成されたMutationフィールドを探し、[アタッチ] を選択します。次のコードを使用して新しいリゾルバーを作成します。import { util } from '@aws-appsync/utils' import { util } from '@aws-appsync/utils'; import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { let condition = null; if (ctx.args.expectedVersion) { condition = { or: [ { id: { attributeExists: false } }, { version: { eq: ctx.args.expectedVersion } }, ], }; } return ddb.remove({ key: { id: ctx.args.id }, condition }); } export function response(ctx) { const { error, result } = ctx; if (error) { util.appendError(error.message, error.type); } return result; }注記
expectedVersion引数は省略可能な引数です。呼び出し元がリクエストでexpectedVersion引数を設定していると、項目が既に削除されている場合、または Amazon DynamoDB 内の投稿のversion属性がexpectedVersionと完全に一致する場合にのみ、DeleteItemリクエストが成功することを許可する条件が、テンプレートによって追加されます。この引数が省略されている場合は、DeleteItemリクエストで条件式が指定されていません。versionの値や項目が Amazon DynamoDB に存在するかどうかに関係なく、成功します。注意: 項目を削除する場合でも、その項目がまだ削除されていなければ、削除された項目を返すことができます。
DeleteItem リクエストの詳細については、「DeleteItem」ドキュメントを参照してください。
ポストを削除する API の呼び出し
これで、リゾルバーがセットアップされたので、AWS AppSync は受信 delete ミューテーションを Amazon DynamoDB DeleteItem オペレーションに変換する方法を知っていることになります。ユーザーはミューテーションを実行してテーブル内の何かを削除できるようになりました。
ミューテーションを実行するには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation deletePost { deletePost(id:123) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
deletePost] を選択します。 -
この投稿が Amazon DynamoDB から削除されます。AWS AppSync は、Amazon DynamoDB から削除された項目の値を返すことに注意してください。その値はクエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。
{ "data": { "deletePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 6, "downs": 4, "version": 12 } } } -
この値は、この
deletePost呼び出しによって実際に Amazon DynamoDB から項目が削除された場合にのみ返されます。再度 [実行] を選択します。 -
この呼び出しは成功しますが、値は返されません。
{ "data": { "deletePost": null } } -
次は、
expectedValueを指定して、ポストを削除してみましょう。まず、これまで使用してきたポストは削除したため、まず新しいポストを作成する必要があります。 -
以下のミューテーションを [クエリ] ペインに追加します。
mutation addPost { addPost( id:123 author: "AUTHORNAME" title: "Our second post!" content: "A new post." url: "https://aws.amazon.com/appsync/" ) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
addPost] を選択します。 -
新しく作成されたポストの結果が、クエリペインの右側にある結果ペインに表示されます。新しく作成されたオブジェクトの
idを書き留めておきます。その値はすぐに必要になります。これは次のように表示されます。{ "data": { "addPost": { "id": "123", "author": "AUTHORNAME", "title": "Our second post!", "content": "A new post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } } -
それでは、expectedVersion の値が不正な投稿を削除してみましょう。以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation deletePost { deletePost( id:123 expectedVersion: 9999 ) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
deletePost] を選択します。以下のレスポンスが返されます。{ "data": { "deletePost": null }, "errors": [ { "path": [ "deletePost" ], "data": null, "errorType": "DynamoDB:ConditionalCheckFailedException", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 7083O037M1FTFRK038A4CI9H43VV4KQNSO5AEMVJF66Q9ASUAAJG)" } ] } -
このリクエストは、条件式が
falseと評価されるため失敗します。Amazon DynamoDBversionの投稿の値が、expectedValue引数で指定された値と一致しません。そのオブジェクトの現在の値が、GraphQL レスポンスのdataセクションのerrorsフィールドで返されます。expectedVersionを訂正して、このリクエストをもう一度試してみます。mutation deletePost { deletePost( id:123 expectedVersion: 1 ) { id author title content url ups downs version } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
deletePost] を選択します。今回は、リクエストが成功し、Amazon DynamoDB から削除された値が返されています。
{ "data": { "deletePost": { "id": "123", "author": "AUTHORNAME", "title": "Our second post!", "content": "A new post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } } -
再度 [実行] を選択します。この呼び出しは成功しますが、そのポストが Amazon DynamoDB で既に削除されているため、今回は値が返されません。
{ "data": { "deletePost": null } }
allPost リゾルバー (Amazon DynamoDB Scan) のセットアップ
これまでのところ、API は、見たい各投稿の id がわかっている場合にのみ便利です。テーブル内のすべてのポストを返す新しいリゾルバーを追加してみましょう。
ミューテーションを追加するには
-
API で [スキーマ] タブを選択します。
-
[スキーマ] ペインの
Queryタイプを次のように変更して、新しいallPostクエリを追加します。type Query { allPost(limit: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post } -
新しい
PaginationPosts型を追加します。type PaginatedPosts { posts: [Post!]! nextToken: String } -
[Save Schema] を選択します。
-
右側の [リゾルバー] ペインで、
allPostタイプから新しく作成されたQueryフィールドを探し、[アタッチ] を選択します。次のコードを使用して新しいリゾルバーを作成します。import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { limit = 20, nextToken } = ctx.arguments; return ddb.scan({ limit, nextToken }); } export function response(ctx) { const { items: posts = [], nextToken } = ctx.result; return { posts, nextToken }; }このリゾルバーのリクエストハンドラーには 2 つのオプション引数が必要です。
-
limit- 1 回の呼び出しで返される項目の最大数を指定します。 -
nextToken- 次の結果セットを取得するために使用されます (nextTokenの値がどこから来たのかは後で説明します)。
-
-
リゾルバーに加えた変更を保存します。
Scan リクエストの詳細については、「Scan」リファレンスドキュメントを参照してください。
API を呼び出してすべての投稿をスキャンする
これで、リゾルバーがセットアップされたので、AWS AppSync は受信 allPost クエリを Amazon DynamoDB Scan オペレーションに変換する方法を知っていることになります。ユーザーは、テーブルをスキャンしてすべてのポストを取得できるようになりました。ただし、これまで使用してきたデータはすべて削除したため、これを試す前にテーブルにデータを入力しておく必要があります。
データを追加してクエリするには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに追加します。
mutation addPost { post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } } -
[実行] (オレンジ色の再生ボタン) を選択します。
-
では、テーブルをスキャンして、一度に 5 個の結果を返しましょう。以下のクエリをクエリペインに貼り付けます。
query allPost { allPost(limit: 5) { posts { id title } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPost] を選択します。最初の 5 個のポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。
{ "data": { "allPost": { "posts": [ { "id": "5", "title": "A series of posts, Volume 5" }, { "id": "1", "title": "A series of posts, Volume 1" }, { "id": "6", "title": "A series of posts, Volume 6" }, { "id": "9", "title": "A series of posts, Volume 9" }, { "id": "7", "title": "A series of posts, Volume 7" } ], "nextToken": "<token>" } } } -
5 つの結果と
nextTokenを取得しました。このトークンを使用して、次の結果セットを取得できます。前回の結果セットからのallPostを含めるように、nextTokenクエリを更新します。query allPost { allPost( limit: 5 nextToken: "<token>" ) { posts { id author } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPost] を選択します。残りの 4 個のポストが、クエリペインの右側にある結果ペインに表示されます。9 個のポストのすべてをページ分割して、ポストは残っていないため、この結果セットに
nextTokenはありません。これは次のように表示されます。{ "data": { "allPost": { "posts": [ { "id": "2", "title": "A series of posts, Volume 2" }, { "id": "3", "title": "A series of posts, Volume 3" }, { "id": "4", "title": "A series of posts, Volume 4" }, { "id": "8", "title": "A series of posts, Volume 8" } ], "nextToken": null } } }
「allPostsByAuthor」リゾルバー (Amazon DynamoDB クエリ) のセットアップ
Amazon DynamoDB ですべてのポストをスキャンだけでなく、特定の作成者が作成したポストを取得するクエリを Amazon DynamoDB に対して実行することもできます。前の手順で作成した Amazon DynamoDB テーブルには、既に author-index という GlobalSecondaryIndex があるため、Amazon DynamoDB の Query オペレーションでそれを使用して、特定の作成者が作成したすべてのポストを取得できます
クエリを追加するには
-
API で [スキーマ] タブを選択します。
-
[スキーマ] ペインの
Queryタイプを次のように変更して、新しいallPostsByAuthorクエリを追加します。type Query { allPostsByAuthor(author: String!, limit: Int, nextToken: String): PaginatedPosts! allPost(limit: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }このクエリでは、
allPostクエリで使用したのと同じPaginatedPosts型を使用していることに注意してください。 -
[Save Schema] を選択します。
-
右側のリゾルバーペインで、
allPostsByAuthor型の新しく作成されたQueryフィールドを見つけて、[アタッチ] を選択します。以下のスニペットを使用してリゾルバーを作成します。import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { limit = 20, nextToken, author } = ctx.arguments; return ddb.query({ index: 'author-index', query: { author: { eq: author } }, limit, nextToken, }); } export function response(ctx) { const { items: posts = [], nextToken } = ctx.result; return { posts, nextToken }; }allPostリゾルバーと同様に、このリゾルバーには 2 つのオプション引数があります。-
limit- 1 回の呼び出しで返される項目の最大数を指定します。 -
nextToken- 次の結果セットを取得します (nextTokenの値は前回の呼び出しから取得できます)。
-
-
リゾルバーに加えた変更を保存します。
Query リクエストの詳細については、「クエリ」リファレンスドキュメントを参照してください。
特定の作成者によるすべてのポストをクエリする API の呼び出し
これでリゾルバーがセットアップされました。AWS AppSync は、受信 allPostsByAuthor ミューテーションを author-index インデックスに対する DynamoDB Query 操作に変換する方法を認識しています。ユーザーは、テーブルをクエリして、特定の作成者によるポストをすべて取得できます。
ただし、これまで使用していたポストはすべて同じ作成者だったため、それを行う前に、テーブルに投稿を追加しておきましょう。
データとクエリを追加するには
-
API で [クエリ] タブを選択します。
-
以下のミューテーションを [クエリ] ペインに追加します。
mutation addPost { post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title } post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title } post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
addPost] を選択します。 -
では、
Nadiaが作成したすべてのポストを返すクエリを実行しましょう。以下のクエリをクエリペインに貼り付けます。query allPostsByAuthor { allPostsByAuthor(author: "Nadia") { posts { id title } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPostsByAuthor] を選択します。Nadiaが作成したすべてのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。{ "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you know...?" } ], "nextToken": null } } } -
Queryでのページ分割はScanとまったく同じように動作します。例えば、AUTHORNAMEによるすべてのポストを検索して、一度に 5 個ずつ取得します。 -
以下のクエリをクエリペインに貼り付けます。
query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" limit: 5 ) { posts { id title } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPostsByAuthor] を選択します。AUTHORNAMEが作成したすべてのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。{ "data": { "allPostsByAuthor": { "posts": [ { "id": "6", "title": "A series of posts, Volume 6" }, { "id": "4", "title": "A series of posts, Volume 4" }, { "id": "2", "title": "A series of posts, Volume 2" }, { "id": "7", "title": "A series of posts, Volume 7" }, { "id": "1", "title": "A series of posts, Volume 1" } ], "nextToken": "<token>" } } } -
次のように、
nextToken引数を、前回のクエリで返された値に更新します。query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" limit: 5 nextToken: "<token>" ) { posts { id title } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPostsByAuthor] を選択します。AUTHORNAMEが作成した残りのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。{ "data": { "allPostsByAuthor": { "posts": [ { "id": "8", "title": "A series of posts, Volume 8" }, { "id": "5", "title": "A series of posts, Volume 5" }, { "id": "3", "title": "A series of posts, Volume 3" }, { "id": "9", "title": "A series of posts, Volume 9" } ], "nextToken": null } } }
セット型の使用
ここまでの Post 型は、フラットなキーと値のオブジェクトでした。 リゾルバーを使用して、セット型、リスト型、マップ型などの複雑なオブジェクトをモデル化することもできます。Post 型を更新して、タグを含めましょう。1 つのポストには、DynamoDB に文字列として保存されているタグを 0 個以上付けることができます。タグを追加および削除するミューテーションと、特定のタグが付いているポストをスキャンする新しいクエリもセットアップします。
データを設定するには
-
API で [スキーマ] タブを選択します。
-
[スキーマ] ペインの
Postタイプを次のように変更して、新しいtagsフィールドを追加します。type Post { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! tags: [String!] } -
[スキーマ] ペインの
Queryタイプを次のように変更して、新しいallPostsByTagクエリを追加します。type Query { allPostsByTag(tag: String!, limit: Int, nextToken: String): PaginatedPosts! allPostsByAuthor(author: String!, limit: Int, nextToken: String): PaginatedPosts! allPost(limit: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post } -
[スキーマ] ペインの
Mutationタイプを変更して、新しいaddTagとremoveTagミューテーションを次のように追加します。type Mutation { addTag(id: ID!, tag: String!): Post removeTag(id: ID!, tag: String!): Post deletePost(id: ID!, expectedVersion: Int): Post upvotePost(id: ID!): Post downvotePost(id: ID!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String!, title: String!, content: String!, url: String! ): Post! } -
[Save Schema] を選択します。
-
右側のリゾルバーペインで、
allPostsByTag型の新しく作成されたQueryフィールドを見つけて、[アタッチ] を選択します。以下のスニペットを使用してリゾルバーを作成します。import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { limit = 20, nextToken, tag } = ctx.arguments; return ddb.scan({ limit, nextToken, filter: { tags: { contains: tag } } }); } export function response(ctx) { const { items: posts = [], nextToken } = ctx.result; return { posts, nextToken }; } -
リゾルバーに加えた変更を保存します。
-
次に、以下のスニペットを使って
MutationフィールドaddTagに対して同じ操作を行います。注記
DynamoDB ユーティリティは現在、セットオペレーションをサポートしていませんが、リクエストを自分で作成してセットを操作することはできます。
import { util } from '@aws-appsync/utils' export function request(ctx) { const { id, tag } = ctx.arguments const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 }) expressionValues[':tags'] = util.dynamodb.toStringSet([tag]) return { operation: 'UpdateItem', key: util.dynamodb.toMapValues({ id }), update: { expression: `ADD tags :tags, version :plusOne`, expressionValues, }, } } export const response = (ctx) => ctx.result -
リゾルバーに加えた変更を保存します。
-
以下のスニペットを使用して、これを
Mutationフィールドでもう一度繰り返します。import { util } from '@aws-appsync/utils'; export function request(ctx) { const { id, tag } = ctx.arguments; const expressionValues = util.dynamodb.toMapValues({ ':plusOne': 1 }); expressionValues[':tags'] = util.dynamodb.toStringSet([tag]); return { operation: 'UpdateItem', key: util.dynamodb.toMapValues({ id }), update: { expression: `DELETE tags :tags ADD version :plusOne`, expressionValues, }, }; } export const response = (ctx) => ctx.resultexport -
リゾルバーに加えた変更を保存します。
タグを操作する API の呼び出し
これで、リゾルバーがセットアップされたので、AWS AppSync は受信 addTag、removeTag、および allPostsByTag リクエストを UpdateItem および Scan の DynamoDB オペレーションに変換する方法を知っていることになります。それを試すには、前のステップで作成したポストのいずれかを選択します。例えば、Nadia が作成したポストを使用しましょう。
タグを使用するには
-
API で [クエリ] タブを選択します。
-
以下のクエリをクエリペインに貼り付けます。
query allPostsByAuthor { allPostsByAuthor( author: "Nadia" ) { posts { id title } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPostsByAuthor] を選択します。 -
Nadia のすべてのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。
{ "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you known...?" } ], "nextToken": null } } } -
タイトルが「The cutest dog in the world」の投稿を使用しましょう。
idは後で使用するため書き留めておきます。では、dogタグを追加してみましょう。 -
以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation addTag { addTag(id:10 tag: "dog") { id title tags } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
addTag] を選択します。ポストが新しいタグで更新されています。{ "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } } -
タグを追加することができます。
puppyに変更するように、tag引数を更新します。mutation addTag { addTag(id:10 tag: "puppy") { id title tags } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
addTag] を選択します。投稿が新しいタグで更新されています。{ "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog", "puppy" ] } } } -
タグを削除することもできます。以下のミューテーションを [クエリ] ペインに貼り付けます。また、
id引数を、前にメモしておいた値に更新する必要があります。mutation removeTag { removeTag(id:10 tag: "puppy") { id title tags } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
removeTag] を選択します。ポストが更新され、puppyタグが削除されています。{ "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } } -
タグが付いているすべての投稿を検索することもできます。以下のクエリをクエリペインに貼り付けます。
query allPostsByTag { allPostsByTag(tag: "dog") { posts { id title tags } nextToken } } -
[実行] (オレンジ色の再生ボタン) を選択し、[
allPostsByTag] を選択します。次のようにdogタグが付いているすべての投稿が返されます。{ "data": { "allPostsByTag": { "posts": [ { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog", "puppy" ] } ], "nextToken": null } } }
結論
このチュートリアルでは、AWS AppSync と GraphQL を使用して Post オブジェクトを操作できる API を構築しました。
クリーンアップするには、AWS AppSync GraphQL API をコンソールから削除します。
DynamoDB テーブルに関連付けられているロールを削除するには、[データソース] テーブルで [データソース]を選択し、[編集] をクリックします。[既存のロールを作成または使用する] の下にあるロールの値を書き留めます。IAM コンソールに移動して、ロールを削除します。
DynamoDB テーブルを削除するには、データソースリスト内のテーブルの名前をクリックします。これにより、DynamoDB コンソールに移動して、テーブルを削除できます。