AppSync ハンズオン: DynamoDB を使用した GraphQL APIの作成

technologies

おはようございます!ベンジャミンの木村です!

今回は、AppSyncを理解するために、基本操作であるDynamoDBとの連携をコンソール上で試し、それをハンズオン形式でまとめました。

AppSync初心者の方におすすめの内容となっておりますので、ご確認いただけますと幸いです!

また、「AppSyncを以前にGraphQLがわからない…」という方は、GraphQLについて解説した記事もありますので、ぜひそちらをご覧ください。

目次

ハンズオンの内容

作業としてはAWS AppSyncを使用してDynamoDBと連携し、GraphQL APIを介して以下の基本操作を実現する内容です。

  • データの取得 (Get)
  • データの一覧表示 (List)
  • データの作成 (Put)
  • データの削除 (Delete)

これらの操作を、GraphQLスキーマの定義から始め、AppSync上でのデータソース設定、リゾルバ作成、そしてクエリ・ミューテーションの実行までを通して解説しています。

1. DynamoDB テーブルの作成

DynamoDBを作成し、項目を作成する手順になります。

1-1. 下記URLより、DynamoDBのコンソール画面へ移動する

https://ap-northeast-1.console.aws.amazon.com/dynamodbv2/home?region=ap-northeast-1#service

1-2. 左ペインの「テーブル」→「テーブルの作成」ボタンをクリックする

1-3. 以下の設定を入力し、『テーブルの作成』ボタンをクリックする

  • テーブル名 MenuItems
  • パーティションキー: id (Type: 文字列)

※その他の設定はデフォルトのままで構いません。

1-4. 作成したテーブルをクリックする

1-5. 「アクション」→「項目を作成」をクリックする

1-6. 以下の通り項目を入力し、「項目を作成」ボタンをクリックする

  • id: 1 (Type: 文字列)
  • name: マルゲリータピザ (Type: 文字列)
  • description: トマトソースとバジル、モッツァレラチーズのピザ (Type: 文字列)
  • category: メインディッシュ (Type: 文字列)
  • price: 1200 (Type: 数値)
  • isVegetarian: true (Type: ブール式)

1-7. 項目が作成されていることが確認できたら完了

2. AppSync API の作成

AppSyncAPIを作成し、スキーマを定義する手順になります。

2-1. 下記URLからAppSyncのコンソール画面へ移動する

https://ap-northeast-1.console.aws.amazon.com/appsync/home?region=ap-northeast-1#

2-2. 「APIを作成」→「GraphQL API」をクリックする

2-3. 「Design from scratch」を選択し、「次へ」ボタンをクリックする

2-4. 任意のAPI名を入力し、「次へ」ボタンをクリックする

2-5. 「後でGraphQLリソースを作成」を選択し、「次へ」ボタンをクリックする

2-6. 入力内容を確認し、「APIを作成」ボタンをクリックする

2-7. 左ペインの「スキーマ」をクリックし、開いた画面で、画像のように以下のスキーマを貼り付けて「スキーマの保存」ボタンをクリックする。

type MenuItem {
	id: ID!
	name: String!
	description: String!
	category: String!
	price: Int!
	isVegetarian: Boolean!
}

type Mutation {
	createMenuItem(
		id: ID!,
		name: String!,
		description: String!,
		category: String!,
		price: Int!,
		isVegetarian: Boolean!
	): MenuItem
	deleteMenuItem(id: ID!): MenuItem
}

type Query {
	getMenuItem(id: ID!): MenuItem
	listMenuItems: [MenuItem]
}

スキーマの書き方については、GraphQLとほぼほぼ一緒ですので、書き方の解説は、冒頭のGraphQLの記事を参考にしていただけますと幸いです。

3. DynamoDB データソースの作成

項番2で作成したAppSyncAPIのデータソースとして、項番1で作成したDynamoDBを指定するまでの手順です。

3-1. 左ペインより「データソース」→「データソースを作成」ボタンをクリックする

3-2. 以下の通り設定し、「作成」ボタンをクリックする

  • データソース名: DynamoDBMenuItems
  • データソースタイプ: AMAZON_DYNAMODB
  • リージョン: ap-northeast-1
  • テーブル名: 項番1で作成した テーブル名

※他はデフォルトでOK

4. リゾルバーの設定

リゾルバーを設定することで、AppSyncからDynamoDBに対してget、list、put、deleteの操作ができるようにします。

4-1.「Schema」タブに戻り、それぞれのフィールド(getMenuItem, listMenuItems, createMenuItem, deleteMenuItem)の「アタッチ」ボタンをクリックする

※選択する「アタッチ」ボタンは、一旦getMenuItemを選んでください。

4-2. データソースに DynamoDBMenuItems を選択し、「作成」ボタンをクリック

4-3. 「コードサンプルを使用」→「 Get an Item 」で選択し、「保存」ボタンをクリックする

※テンプレートを使用するとJSDoc の型注釈に対して、エラー文が出力されます。コードの機能自体には問題は与えませんので、開発環境での型チェックでは無視しても構いません。

こちらで生成された、リゾルバーコードの内容ですが、GraphQLの時とだいぶ様変わりしていますので、下記に解説させていただきます。

テンプレートコード全体
import { util } from '@aws-appsync/utils';

/**
 * Sends a request to get an item with id `ctx.args.id`
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {import('@aws-appsync/utils').DynamoDBGetItemRequest} the request
 */
export function request(ctx) {
    return {
        operation: 'GetItem',
        key: util.dynamodb.toMapValues({ id: ctx.args.id }),
    };
}

/**
 * Returns the fetched DynamoDB item
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the DynamoDB item
 */
export function response(ctx) {
    return ctx.result;
}

テンプレートコードの解説

GraphQLでのリゾルバーは、QueryMutation のなどの役割ごとのエントリポイントに分けて記述するのに対し、AppSyncのリゾルバーは、リクエストの作成部分レスポンスの整形部分が明確に分かれています。これにより、何をリクエストし、どんな結果を返すのかが簡単に把握できる仕組みになっています。それぞれについて詳しく説明します。

1. request関数とは?

request関数は、外部システム(ここではDynamoDB)に対してどんな操作を実行するのかを決める役割を持っています。

export function request(ctx) {
return {
operation: 'GetItem', // DynamoDBで1件のデータを取得する操作を指定
key: util.dynamodb.toMapValues({ id: ctx.args.id }), // クエリで渡されたidをキーに変換
};
}
  • operation: DynamoDBの操作内容を指定します。この例では1件取得するGetItemを使っています。
  • key: DynamoDBで検索するためのキーです。クエリの引数idを使って設定しています。
    • ctx.args.idで、クエリで渡されたidの値を取得しています。
    • util.dynamodb.toMapValuesを使うことで、DynamoDBが理解できる形式に変換します(例えば、"id": "1""id": { "S": "1" }に)。

2. response関数とは?

response関数は、DynamoDBなどから返ってきたデータをそのまま、または整形してクライアントに返す役割を持っています。

export function response(ctx) {
return ctx.result; // DynamoDBから返された結果をそのまま返す
}
  • ctx.result: DynamoDBから返ってきた結果が自動的にここに入ります。
    • この例では特に加工せず、そのまま返しています。
    • 必要であれば、ここでフィールドを追加・削除したり、形式を変えることもできます。

※JSDoc(/** **/の部分)については、本記事では解説を控えています。JSDocはコードの内容を説明するためのものであり、直接コードの動作に影響を与えるものではありません。

以下より項番4-1~4-3と同様の操作を、list, put, deleteの場合で値を変えて行なってください。

Query.listMenuItemsの場合

  1. Query.listMenuItems の横にある「アタッチ」をクリックする。
  2. データソースに DynamoDBMenuItems を選択する。
  3. 「コードサンプルを使用」で 「List items」 を選択し、「保存」ボタンをクリックする。
テンプレートコード全体
/**
 * Scans the DynamoDB datasource. Scans up to the provided `limit` and stards from the provided `NextToken` (optional).
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {import('@aws-appsync/utils').DynamoDBScanRequest} the request
 */
export function request(ctx) {
    const { limit = 10, nextToken } = ctx.args;
    return { operation: 'Scan', limit, nextToken };
}

/**
 * Returns the scanned items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} a flat list of results from the Scan operation
 */
export function response(ctx) {
    return ctx.result.items;
}

Mutation.createMenuItemの場合

  1. Mutation.createMenuItem の横にある「アタッチ」をクリックする。
  2. データソースに DynamoDBMenuItems を選択する。
  3. 「コードサンプルを使用」で 「Create an item」 を選択し、「保存」ボタンをクリックする。
テンプレートコード全体
import { util } from '@aws-appsync/utils';

/**
 * Puts an item into the DynamoDB table using an auto-generated ID.
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {import('@aws-appsync/utils').DynamoDBPutItemRequest} the request
 */
export function request(ctx) {
    return {
        operation: 'PutItem',
        key: util.dynamodb.toMapValues({ id: util.autoId() }),
        attributeValues: util.dynamodb.toMapValues(ctx.args),
    };
}

/**
 * Returns the item or throws an error if the operation failed
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the inserted item
 */
export function response(ctx) {
    if (ctx.error) {
        util.error(ctx.error.message, ctx.error.type);
    }
    return ctx.result;
}

Mutation.deleteMenuItemの場合

  1. Mutation.deleteMenuItem の横にある「アタッチ」をクリックする。
  2. データソースに DynamoDBMenuItems を選択する。
  3. 「コードサンプルを使用」で 「Delete an item」 を選択し、「保存」ボタンをクリックする。
テンプレートコード全体
import { util } from '@aws-appsync/utils';

/**
 * Deletes an item with id `ctx.args.id` from the DynamoDB table
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {import('@aws-appsync/utils').DynamoDBDeleteItemRequest} the request
 */
export function request(ctx) {
    return {
        operation: 'DeleteItem',
        key: util.dynamodb.toMapValues({ id: ctx.args.id }),
    };
}

/**
 * Returns the deleted item. Throws an error if the operation failed
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {*} the deleted item
 */
export function response(ctx) {
    if (ctx.error) {
        util.error(ctx.error.message, ctx.error.type);
    }
    return ctx.result;
}

5. クエリとミューテーションのテスト

5-1. GraphQL クエリの実行テスト(単一データ取得(get))

5-1-1. 左ペインより「クエリ」→「Add new operation」→「Query」を選択する

5-1-2. getMenuItemで取得したいデータの項目を選択し、「実行する」→「MyQuery」を選択する

※idは1で実行する。項番1でidを1で作成したので。

成功すれば右枠に挿入データが表示される。

以下は実行するクエリの例です。「MyQuery」で項目を選択すると、次のようなクエリが生成されます。

query MyQuery {
  getMenuItem(id: "1") {
    category
    description
    id
  }
}

5-2. GraphQL クエリの実行テスト(全データ取得(list))

5-2-1. listMenuItemで取得したいデータの項目を選択し、「実行する」→「MyQuery」を選択する

※今回は、全データ取得なので、idの選択は不要。

成功すれば右枠に挿入データが表示される。

実行するクエリの例

query MyQuery {
  listMenuItems {
    category
    description
    id
    isVegetarian
    name
    price
  }
}

5-3. GraphQL ミューテーションを実行(データの作成put)

5-3-1. 「Add new operation」→「Mutation」をクリックする

5-3-2. 各項目にDynamoDBに挿入したい値を入力し、「MyMutation」をクリックする

成功すれば右枠に挿入データが表示される。

実行するクエリの例

mutation MyMutation($id: ID = "") {
  createMenuItem(id: $id, name: "カルボナーラ", category: "メインディッシュ", description: "クリーミーなソースのパスタ", isVegetarian: false, price: 1500) {
    category
    description
    id
    isVegetarian
    name
    price
  }
}

5-3-3. ソースに指定しているDynamoDBのコンソール画面へ移動し、 「テーブルアイテムの探索」をクリックする

5-3-4. クエリした値が挿入されていることを確認できたらOK

※IDがハッシュ値になっているのは、項番4-3のcreateMenuItemのリゾルバでid: util.autoId()でid値を指定しているからになります。連番などで自分で指定したい場合は、こちらのリゾルバのrequestを修正するといいでしょう!

5-4. GraphQL ミューテーションを実行(データの削除delete)

5-4-1. 各項目にDynamoDBの削除したいid値を入力し、「MyMutation」をクリックする

実行するクエリの例

mutation MyMutation {
  deleteMenuItem(id: "1") {
    category
    description
    id
    isVegetarian
    name
    price
  }
}

5-4-2. DynamoDBの項目画面へ移動し、クエリした値が削除されていることを確認できたらOK

まとめ

いかがでしたでしょうか?

今回は前回記載したGraphQLの続きでAppSyncの構築方法を記載しました!

これを読んだあなたは「AppSyncって何?」って状態から抜け出せていただけましたら幸いです。

また今回は、AppSyncの構造をより深く理解していただくため、手順をできるだけ手作業で進められるよう工夫して記事を作成しました。手順を簡略化したい場合は、項番2-3で「DynamoDBテーブルから始める」を選択すると、スキーマやリゾルバーが自動で作成されます。こちらの手順もぜひお試しください。

次回は、Amplify Gen2を使ったAppSyncの構築方法をご紹介する予定です。乞うご期待!

Related posts