DynamoDB JavaScript リゾルバーを使用したシンプルなポストアプリケーションの作成 - AWS AppSync

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 を作成するには

  1. AppSync コンソールを開き、[API を作成] を選択します。

  2. [最初から設計] を選択し、[次へ] を選択します。

  3. API PostTutorialAPI に名前を付け、[次へ] を選択します。残りのオプションはデフォルト設定値のまま、レビューページに移動し、Create を選択します。

AWS AppSync コンソールによって、新しい GraphQL API が作成されます。デフォルトでは、API キー認証モードを使用します。このコンソールを使用して、残りの GraphQL API をセットアップでき、このチュートリアルの残りの部分でクエリを実行できます。

基本的な Post API の定義

ここで、GraphQL API があるので、ポストデータの基本的な作成、取得、削除を許可する基本スキーマをセットアップできます。

スキーマにデータを追加するには

  1. API で [スキーマ] タブを選択します。

  2. 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! }
  3. [Save Schema] を選択します。

Amazon DynamoDB テーブルのセットアップ

AWS AppSync コンソールは、独自のリソースを Amazon DynamoDB テーブルに保存するのに必要な AWS リソースをプロビジョニングするのに役立ちます。このステップでは、投稿を保存する Amazon DynamoDB テーブルを作成します。また、後で使用するセカンダリインデックスも設定します。

Amazon DynamoDB テーブルを作成するには

  1. [スキーマ] ページで、[リソースを作成] を選択します。

  2. [既存のタイプを使用] を選択し、Post タイプを選択します。

  3. [セカンダリインデックス] セクションで、[インデックスを追加] を選択します。

  4. インデックス author-index の名前

  5. Primary keyauthor に、Sort キーを None に設定します。

  6. GraphQL の自動生成を無効にします。この例では、リゾルバーを自分で作成します。

  7. [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 型と異なる場合に便利です。ただし、この例では、両方のシェイプが同じであるため、データをそのまま渡します。

リゾルバーをセットアップするには

  1. API で [スキーマ] タブを選択します。

  2. リゾルバー ペインで、Mutation 型の下にある addPost フィールドを探し、「アタッチ」を選択します。

  3. データソースを選択し、[作成] を選択します。

  4. コードエディターで、コードを次のスニペットに置き換えます。

    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 }
  5. [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 オペレーションに変換できます。ユーザーはミューテーションを実行してテーブルに何かを入れることができるようになりました。

オペレーションを実行するには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに追加します。

    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 } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[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 } } }

次の説明は、何が起こったかを示しています。

  1. AWS AppSync が addPost ミューテーションリクエストを受け取りました。

  2. 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/' } } }
  3. AWS AppSync はこの値を使用して Amazon DynamoDB PutItem リクエストを生成して実行します。

  4. 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 }
  5. レスポンスハンドラーは結果をすぐに返します (return ctx.result)。

  6. 最終結果は GraphQL のレスポンスに表示されます。

getPost リゾルバー (Amazon DynamoDB GetItem) のセットアップ

これで、 Amazon DynamoDB テーブルにデータを追加できるようになりました。次は、からデータを取得できるように、getPost クエリを設定する必要があります。そのためには、別のリゾルバーを設定します。

リソースを追加するには、次の手順に従います。

  1. API で [スキーマ] タブを選択します。

  2. 右側の [リゾルバー] ペインで、Query タイプの getPost フィールドを見つけて [アタッチ] を選択します。

  3. データソースを選択し、[作成] を選択します。

  4. コードエディタで、コードを次のスニペットに置き換えます。

    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
  5. リゾルバーを保存します。

注記

このリゾルバーでは、レスポンスハンドラーに矢印関数式を使用しています。

投稿を取得する API の呼び出し

これで、リゾルバーがセットアップされたので、AWS AppSync は受信 getPost クエリを Amazon DynamoDB GetItem オペレーションに変換する方法を知っていることになります。次は、先ほど作成したポストを取得するクエリを実行します。

クエリを実行するには

  1. API で [クエリ] タブを選択します。

  2. クエリ ペインで次のコードを追加し、投稿を作成した後にコピーした ID を使用します。

    query getPost { getPost(id: "123") { id author title content url ups downs version } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[getPost] を選択します。新しく作成されたポストの結果が、クエリペインの右側にある結果ペインに表示されます。

  4. 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 クエリが idauthortitle のみを必要とする場合は、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

SelectionSetListgetPost と一緒に使用すると、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 を使用します。

リゾルバーを更新するには

  1. API で [スキーマ] タブを選択します。

  2. [スキーマ] ペインの 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! }
  3. [Save Schema] を選択します。

  4. 右側の [リゾルバー] ペインで、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;
  5. 加えた変更を保存します。

このリゾルバーは 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 オブジェクトを更新してみましょう。

オブジェクトを更新するには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation updatePost { updatePost( id:123 title: "An empty story" content: null expectedVersion: 1 ) { id author title content url ups downs version } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[updatePost] を選択します。

  4. 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 } } }

このリクエストでは、titlecontent フィールドのみを更新するように AWS AppSync と Amazon DynamoDB に指示しています。その他のフィールドは元のままです (増分する version フィールドは除く)。title 属性を新しい値に設定し、ポストから content 属性を削除しています。authorurlupsdowns の各フィールドは変更されません。リクエストはまったく同じままで、このミューテーションをもう一度実行してみます。次のようなレスポンスが表示されます。

{ "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と評価されるため失敗します。

  1. このリクエストを最初に実行したときに、Amazon DynamoDB 内のこの投稿の version フィールドの値は 1 であり、expectedVersion 引数と一致していました。このリクエストは成功し、Amazon DynamoDB で version フィールドが 2 に増分されました。

  2. このリクエストを 2 回目に実行したときに、Amazon DynamoDB 内のこのポストの version フィールドの値は 2 であり、expectedVersion 引数と一致していませんでした。

このパターンは通常、「楽観的ロック」と呼ばれます。

投票ミューテーションの作成 (Amazon DynamoDB UpdateItem)

Post タイプには、賛成票と反対票を記録できるようにする upsdowns フィールドが含まれます。ただし、現時点では API ではこれらに対して何も実行できません。投稿に賛成および反対するための、いくつかのミューテーションを追加してみましょう。

ミューテーションを追加するには

  1. API で [スキーマ] タブを選択します。

  2. [スキーマ] ペインの 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 }
  3. [Save Schema] を選択します。

  4. 右側の [リゾルバー] ペインで、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;
  5. 加えた変更を保存します。

ポストに賛成および反対するための API の呼び出し

これで、新しいリゾルバーがセットアップされたので、AWS AppSync AppSync は受信 upvotePost または downvoteミューテーションを Amazon DynamoDB の UpdateItem オペレーションに変換する方法を知っていることになり、これで、先ほど作成したポストに賛成または反対するミューテーションを実行できるようになりました。

ミューテーションを実行するには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation votePost { vote(id:123, direction: UP) { id author title content url ups downs version } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[votePost] を選択します。

  4. 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 } } }
  5. もう何回か [実行] を選択します。このクエリを実行するたびに、ups フィールドと version フィールドが 1 つずつ増加することを確認できます。

  6. クエリを変更して、別の DIRECTION を呼び出してください。

    mutation votePost { vote(id:123, direction: DOWN) { id author title content url ups downs version } }
  7. [実行] (オレンジ色の再生ボタン) を選択し、[votePost] を選択します。

    今度は、このクエリを実行するたびに、downs フィールドと version フィールドが 1 つずつ増加することを確認できます。

deletePost リゾルバー (Amazon DynamoDB DeleteItem) のセットアップ

次に、投稿を削除するミューテーションを作成します。そのためには、DeleteItem Amazon DynamoDB オペレーションを使用します。

ミューテーションを追加するには

  1. スキーマで、「スキーマ」タブを選択します。

  2. [スキーマ] ペインの 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! }
  3. 今回は、expectedVersion フィールドをオプションにしました。[スキーマを保存] を選択します。

  4. 右側の [リゾルバー] ペインで、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 オペレーションに変換する方法を知っていることになります。ユーザーはミューテーションを実行してテーブル内の何かを削除できるようになりました。

ミューテーションを実行するには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation deletePost { deletePost(id:123) { id author title content url ups downs version } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[deletePost] を選択します。

  4. この投稿が 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 } } }
  5. この値は、この deletePost 呼び出しによって実際に Amazon DynamoDB から項目が削除された場合にのみ返されます。再度 [実行] を選択します。

  6. この呼び出しは成功しますが、値は返されません。

    { "data": { "deletePost": null } }
  7. 次は、expectedValue を指定して、ポストを削除してみましょう。まず、これまで使用してきたポストは削除したため、まず新しいポストを作成する必要があります。

  8. 以下のミューテーションを [クエリ] ペインに追加します。

    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 } }
  9. [実行] (オレンジ色の再生ボタン) を選択し、[addPost] を選択します。

  10. 新しく作成されたポストの結果が、クエリペインの右側にある結果ペインに表示されます。新しく作成されたオブジェクトの 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 } } }
  11. それでは、expectedVersion の値が不正な投稿を削除してみましょう。以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation deletePost { deletePost( id:123 expectedVersion: 9999 ) { id author title content url ups downs version } }
  12. [実行] (オレンジ色の再生ボタン) を選択し、[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)" } ] }
  13. このリクエストは、条件式が false と評価されるため失敗します。Amazon DynamoDB version の投稿の値が、expectedValue引数で指定された値と一致しません。そのオブジェクトの現在の値が、GraphQL レスポンスの data セクションの errors フィールドで返されます。expectedVersion を訂正して、このリクエストをもう一度試してみます。

    mutation deletePost { deletePost( id:123 expectedVersion: 1 ) { id author title content url ups downs version } }
  14. [実行] (オレンジ色の再生ボタン) を選択し、[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 } } }
  15. 再度 [実行] を選択します。この呼び出しは成功しますが、そのポストが Amazon DynamoDB で既に削除されているため、今回は値が返されません。

    { "data": { "deletePost": null } }

allPost リゾルバー (Amazon DynamoDB Scan) のセットアップ

これまでのところ、API は、見たい各投稿の id がわかっている場合にのみ便利です。テーブル内のすべてのポストを返す新しいリゾルバーを追加してみましょう。

ミューテーションを追加するには

  1. API で [スキーマ] タブを選択します。

  2. [スキーマ] ペインの Query タイプを次のように変更して、新しい allPost クエリを追加します。

    type Query { allPost(limit: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }
  3. 新しい PaginationPosts 型を追加します。

    type PaginatedPosts { posts: [Post!]! nextToken: String }
  4. [Save Schema] を選択します。

  5. 右側の [リゾルバー] ペインで、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の値がどこから来たのかは後で説明します)。

  6. リゾルバーに加えた変更を保存します。

Scan リクエストの詳細については、「Scan」リファレンスドキュメントを参照してください。

API を呼び出してすべての投稿をスキャンする

これで、リゾルバーがセットアップされたので、AWS AppSync は受信 allPost クエリを Amazon DynamoDB Scan オペレーションに変換する方法を知っていることになります。ユーザーは、テーブルをスキャンしてすべてのポストを取得できるようになりました。ただし、これまで使用してきたデータはすべて削除したため、これを試す前にテーブルにデータを入力しておく必要があります。

データを追加してクエリするには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに追加します。

    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 } }
  3. [実行] (オレンジ色の再生ボタン) を選択します。

  4. では、テーブルをスキャンして、一度に 5 個の結果を返しましょう。以下のクエリをクエリペインに貼り付けます。

    query allPost { allPost(limit: 5) { posts { id title } nextToken } }
  5. [実行] (オレンジ色の再生ボタン) を選択し、[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>" } } }
  6. 5 つの結果と nextToken を取得しました。このトークンを使用して、次の結果セットを取得できます。前回の結果セットからの allPost を含めるように、nextToken クエリを更新します。

    query allPost { allPost( limit: 5 nextToken: "<token>" ) { posts { id author } nextToken } }
  7. [実行] (オレンジ色の再生ボタン) を選択し、[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 オペレーションでそれを使用して、特定の作成者が作成したすべてのポストを取得できます

クエリを追加するには

  1. API で [スキーマ] タブを選択します。

  2. [スキーマ] ペインの Query タイプを次のように変更して、新しい allPostsByAuthor クエリを追加します。

    type Query { allPostsByAuthor(author: String!, limit: Int, nextToken: String): PaginatedPosts! allPost(limit: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }

    このクエリでは、allPost クエリで使用したのと同じ PaginatedPosts 型を使用していることに注意してください。

  3. [Save Schema] を選択します。

  4. 右側のリゾルバーペインで、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の値は前回の呼び出しから取得できます)。

  5. リゾルバーに加えた変更を保存します。

Query リクエストの詳細については、「クエリ」リファレンスドキュメントを参照してください。

特定の作成者によるすべてのポストをクエリする API の呼び出し

これでリゾルバーがセットアップされました。AWS AppSync は、受信 allPostsByAuthor ミューテーションを author-index インデックスに対する DynamoDB Query 操作に変換する方法を認識しています。ユーザーは、テーブルをクエリして、特定の作成者によるポストをすべて取得できます。

ただし、これまで使用していたポストはすべて同じ作成者だったため、それを行う前に、テーブルに投稿を追加しておきましょう。

データとクエリを追加するには

  1. API で [クエリ] タブを選択します。

  2. 以下のミューテーションを [クエリ] ペインに追加します。

    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 } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[addPost] を選択します。

  4. では、Nadia が作成したすべてのポストを返すクエリを実行しましょう。以下のクエリをクエリペインに貼り付けます。

    query allPostsByAuthor { allPostsByAuthor(author: "Nadia") { posts { id title } nextToken } }
  5. [実行] (オレンジ色の再生ボタン) を選択し、[allPostsByAuthor] を選択します。Nadia が作成したすべてのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you know...?" } ], "nextToken": null } } }
  6. Query でのページ分割は Scan とまったく同じように動作します。例えば、AUTHORNAME によるすべてのポストを検索して、一度に 5 個ずつ取得します。

  7. 以下のクエリをクエリペインに貼り付けます。

    query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" limit: 5 ) { posts { id title } nextToken } }
  8. [実行] (オレンジ色の再生ボタン) を選択し、[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>" } } }
  9. 次のように、nextToken 引数を、前回のクエリで返された値に更新します。

    query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" limit: 5 nextToken: "<token>" ) { posts { id title } nextToken } }
  10. [実行] (オレンジ色の再生ボタン) を選択し、[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 個以上付けることができます。タグを追加および削除するミューテーションと、特定のタグが付いているポストをスキャンする新しいクエリもセットアップします。

データを設定するには

  1. API で [スキーマ] タブを選択します。

  2. [スキーマ] ペインの Post タイプを次のように変更して、新しい tags フィールドを追加します。

    type Post { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! tags: [String!] }
  3. [スキーマ] ペインの 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 }
  4. [スキーマ] ペインの Mutation タイプを変更して、新しい addTagremoveTag ミューテーションを次のように追加します。

    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! }
  5. [Save Schema] を選択します。

  6. 右側のリゾルバーペインで、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 }; }
  7. リゾルバーに加えた変更を保存します。

  8. 次に、以下のスニペットを使って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
  9. リゾルバーに加えた変更を保存します。

  10. 以下のスニペットを使用して、これを 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
  11. リゾルバーに加えた変更を保存します。

タグを操作する API の呼び出し

これで、リゾルバーがセットアップされたので、AWS AppSync は受信 addTagremoveTag、および allPostsByTag リクエストを UpdateItem および Scan の DynamoDB オペレーションに変換する方法を知っていることになります。それを試すには、前のステップで作成したポストのいずれかを選択します。例えば、Nadia が作成したポストを使用しましょう。

タグを使用するには

  1. API で [クエリ] タブを選択します。

  2. 以下のクエリをクエリペインに貼り付けます。

    query allPostsByAuthor { allPostsByAuthor( author: "Nadia" ) { posts { id title } nextToken } }
  3. [実行] (オレンジ色の再生ボタン) を選択し、[allPostsByAuthor] を選択します。

  4. Nadia のすべてのポストが、クエリペインの右側にある結果ペインに表示されます。これは次のように表示されます。

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you known...?" } ], "nextToken": null } } }
  5. タイトルが「The cutest dog in the world」の投稿を使用しましょう。id は後で使用するため書き留めておきます。では、dog タグを追加してみましょう。

  6. 以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation addTag { addTag(id:10 tag: "dog") { id title tags } }
  7. [実行] (オレンジ色の再生ボタン) を選択し、[addTag] を選択します。ポストが新しいタグで更新されています。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } }
  8. タグを追加することができます。puppy に変更するように、tag 引数を更新します。

    mutation addTag { addTag(id:10 tag: "puppy") { id title tags } }
  9. [実行] (オレンジ色の再生ボタン) を選択し、[addTag] を選択します。投稿が新しいタグで更新されています。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog", "puppy" ] } } }
  10. タグを削除することもできます。以下のミューテーションを [クエリ] ペインに貼り付けます。また、id 引数を、前にメモしておいた値に更新する必要があります。

    mutation removeTag { removeTag(id:10 tag: "puppy") { id title tags } }
  11. [実行] (オレンジ色の再生ボタン) を選択し、[removeTag] を選択します。ポストが更新され、puppy タグが削除されています。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } }
  12. タグが付いているすべての投稿を検索することもできます。以下のクエリをクエリペインに貼り付けます。

    query allPostsByTag { allPostsByTag(tag: "dog") { posts { id title tags } nextToken } }
  13. [実行] (オレンジ色の再生ボタン) を選択し、[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 コンソールに移動して、テーブルを削除できます。