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 の自動生成を無効にします。この例では、リゾルバーを自分で作成します。
-
[Create] (作成) を選択します。
これで、PostTable
という新しいデータソースができました。サイドタブの [データソース] にアクセスすると確認できます。このデータソースを使用して、クエリとミューテーションを Amazon DynamoDB テーブルにリンクします。
addPost リゾルバー (DAmazon DynamoDB PutItem) のセットアップ
AWS AppSync が Amazon DynamoDB テーブルを認識したら、リゾルバーを定義することで、そのテーブルを個々のクエリおよびミューテーションにリンクできます。最初に作成するリゾルバーは addPost
リゾルバーです。このリゾルバーによって、ユーザーが Amazon DynamoDB テーブルにポストを作成できるようになります。パイプラインリゾルバーには以下のコンポーネントがあります。
-
リゾルバーをアタッチする、GraphQL スキーマ内の場所。この例では、
createPost
型のMutation
フィールドにリゾルバーをセットアップしています。このリゾルバーは、呼び出し元がミューテーション{ addPost(...){...} }
を呼び出したときに呼び出されます。 -
このリゾルバーで使用するデータソース。この例では、
post-table-for-tutorial
DynamoDB テーブルにエントリを追加できるように、前に定義した データソースを使用します。 -
リクエストハンドラー。リクエストハンドラーは、呼び出し元からの受信リクエストに対応して、それを 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 }
-
[Save] を選択します。
注記
このコードでは、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
、url
ups
、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 コンソールに移動して、テーブルを削除できます。