JavaScript リゾルバーの概要 - AWS AppSync

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

JavaScript リゾルバーの概要

AWS AppSync を使用すると、データソースに対してオペレーションを実行して GraphQL リクエストに応答できます。クエリ、ミューテーション、サブスクリプションなど、実行する GraphQL フィールドごとに、リゾルバーをアタッチする必要があります。

リゾルバーは GraphQL とデータソースの間のコネクタです。受信した GraphQL リクエストをバックエンドのデータソースに対する指示に変換する方法と、そのデータソースからのレスポンスを GraphQL レスポンスに戻す方法を AWS AppSync に指示します。AWS AppSyncを使用すると、JavaScriptを使用してリゾルバーを記述し、AWS AppSync (APPSYNC_JS) 環境で実行できます。

AWS AppSyncでは、複数の AWS AppSync 関数で構成されるユニットリゾルバーまたはパイプラインリゾルバーをパイプラインに記述できます。

サポートされているランタイム機能

AWS AppSync JavaScript ランタイムは、JavaScript ライブラリ、ユーティリティ、および機能のサブセットを提供します。APPSYNC_JS ランタイムでサポートされている特徴と機能の完全なリストについては、「リゾルバーと関数用の JavaScript ランタイム機能」を参照してください。

ユニットリゾルバー

ユニットリゾルバーは、データソースに対して実行されるリクエストハンドラーとレスポンスハンドラーを定義するコードで構成されています。リクエストハンドラーはコンテキストオブジェクトを引数として受け取り、データソースの呼び出しに使用されたリクエストペイロードを返します。レスポンスハンドラーは、実行されたリクエストの結果を含むペイロードをデータソースから受け取ります。レスポンスハンドラーは、payload を GraphQL レスポンスに変換して GraphQL フィールドを解決します。以下の例では、リゾルバーは DynamoDB データソースから項目を取得します。

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;

JavaScript パイプラインリゾルバーの仕組み

パイプラインリゾルバーは、リクエストとレスポンスのハンドラーと関数のリストを定義するコードで構成されています。各関数には、データソースに対して実行されるリクエストおよびレスポンスハンドラーがあります。パイプラインリゾルバーは実行をリスト内の関数に委任するため、いずれのデータソースにもリンクされていません。ユニットリゾルバーと関数は、データソースに対してオペレーションを実行するプリミティブです。

パイプラインリゾルバーリクエストハンドラー

パイプラインリゾルバーのリクエストハンドラー (before step) では、定義した関数を実行する前に準備ロジックを実行できます。

関数のリスト

パイプラインリゾルバーが順番に実行する関数のリスト。パイプラインリゾルバーのリクエストハンドラーの評価結果は、最初の関数で ctx.prev.result として使用可能になります。各関数の評価結果は、以下の関数で ctx.prev.result として使用可能です。

パイプラインリゾルバーレスポンスハンドラー

パイプラインリゾルバーのレスポンスハンドラーでは、最後の関数の出力から、想定される GraphQL フィールドタイプへの、一部の最終的なロジックを実行できます。関数のリストの最後にある関数の出力は、パイプラインリゾルバーレスポンスハンドラーで ctx.prev.result または ctx.result として使用可能です。

実行フロー

2 つの関数で構成されるパイプラインリゾルバーがあるとします。以下のリストでは、リゾルバーが呼び出されたときの実行フローを表しています。

  1. パイプラインリゾルバーリクエストハンドラー

  2. 関数 1: 関数リクエストハンドラー

  3. 関数 1: データソースの呼び出し

  4. 関数 1: 関数レスポンスハンドラー

  5. 関数 2: 関数リクエストハンドラー

  6. 関数 2: データソースの呼び出し

  7. 関数 2: 関数レスポンスハンドラー

  8. パイプラインリゾルバーレスポンスハンドラー

便利な APPSYNC_JS ランタイムビルトインユーティリティ

パイプラインリゾルバーでの作業には、以下のユーティリティが役立ちます。

ctx.stash

stash は、各リゾルバー、関数リクエスト、レスポンスハンドラー内で使用できるオブジェクトです。同じ stash インスタンスは 1 つのリゾルバーの実行を通して存続します。つまり、stash を使用して、リクエストとレスポンスのハンドラー間、およびパイプラインリゾルバーの関数間で、任意のデータを渡すことができます。stash は通常の JavaScript オブジェクトと同じようにテストできます。

ctx.prev.result

ctx.prev.result は、パイプラインで実行された前のオペレーションの結果を表します。前のオペレーションがパイプラインリゾルバーリクエストハンドラーであった場合、ctx.prev.result はチェーン内の最初の関数に使用可能になります。前のオペレーションが最初の関数であった場合、ctx.prev.result は最初の関数の出力を表し、パイプライン内の 2 番目の関数に使用可能になります。前のオペレーションが最後の関数であった場合、ctx.prev.result は最後の関数の出力を表し、パイプラインリゾルバーレスポンスハンドラーに使用可能になります。

util.error

util.error ユーティリティはフィールドエラーをスローするのに便利です。関数リクエストまたはレスポンスハンドラー内で util.error を使用すると、フィールドエラーがすぐにスローされ、それ以降の関数が実行されなくなります。詳細やその他の util.error シグネチャについては、「リゾルバーと関数の JavaScript ランタイム機能」をご覧ください。

util.appendError

util.appendErrorutil.error() と似ていますが、ハンドラーの評価を中断しないという大きな違いがあります。代わりに、フィールドにエラーがあったことを通知します。ただし、ハンドラーを評価し、データを引き続き返すことも可能です。関数内で util.appendError を使用しても、パイプラインの実行フローは中断されません。詳細やその他の util.error シグネチャについては、「リゾルバーと関数の JavaScript ランタイム機能」をご覧ください。

Runtime.earlyReturn

runtime.earlyReturn 関数を使うと、どのリクエスト関数からでも途中で戻ることができます。リゾルバーのリクエストハンドラー内で runtime.earlyReturn を使用すると、リゾルバーから返されます。AWS AppSync 関数リクエストハンドラーから呼び出すと、関数から戻り、パイプライン内の次の関数またはリゾルバー応答ハンドラーのどちらかまで実行が続行されます。

パイプラインリゾルバーの記述

パイプラインリゾルバーには、パイプライン内の関数の実行を囲むリクエストハンドラーとレスポンスハンドラーもあります。リクエストハンドラーは最初の関数のリクエストの前に実行され、レスポンスハンドラーは最後の関数の応答の後に実行されます。リゾルバーリクエストハンドラーは、パイプライン内の関数が使用するデータを設定できます。リゾルバーレスポンスハンドラーは GraphQL フィールド出力タイプにマッピングするデータを返す役割を果たします。以下の例では、リゾルバーリクエストハンドラーが allowedGroups を定義しています。返されるデータはこれらのグループのいずれかに属している必要があります。この値は、リゾルバーの関数がデータをリクエストする際に使用できます。リゾルバーのレスポンスハンドラーは最終チェックを行い、結果をフィルタリングして、許可されたグループに属するアイテムだけが返されるようにします。

import { util } from '@aws-appsync/utils'; /** * Called before the request function of the first AppSync function in the pipeline. * @param ctx the context object holds contextual information about the function invocation. */ export function request(ctx) { ctx.stash.allowedGroups = ['admin']; ctx.stash.startedAt = util.time.nowISO8601(); return {}; } /** * Called after the response function of the last AppSync function in the pipeline. * @param ctx the context object holds contextual information about the function invocation. */ export function response(ctx) { const result = []; for (const item of ctx.prev.result) { if (ctx.stash.allowedGroups.indexOf(item.group) > -1) result.push(item); } return result; }

AWS AppSync 関数の記述

AWS AppSync 関数では、スキーマ内の複数のリゾルバーにまたがって再利用できる共通のロジックを作成できます。例えば、Amazon DynamoDB データソースの項目をクエリする QUERY_ITEMS という AWS AppSync 関数を 1 つ指定できます。項目をクエリするリゾルバーについては、リゾルバーのパイプラインに関数を追加し、使用するクエリインデックスを指定するだけです。ロジックを再実装する必要はありません。

コードの記述

次の GraphQL クエリを使用して、Amazon DynamoDB データソースから Post 型を返す getPost(id:ID!) という名前のフィールドにパイプラインリゾルバーをアタッチするとします。

getPost(id:1){ id title content }

まず、以下のコードで簡単なリゾルバーを Query.getPost にアタッチします。これは単純なリゾルバーコードの例です。リクエストハンドラーにはロジックは定義されておらず、レスポンスハンドラーは最後の関数の結果を返すだけです。

/** * Invoked **before** the request handler of the first AppSync function in the pipeline. * The resolver `request` handler allows to perform some preparation logic * before executing the defined functions in your pipeline. * @param ctx the context object holds contextual information about the function invocation. */ export function request(ctx) { return {} } /** * Invoked **after** the response handler of the last AppSync function in the pipeline. * The resolver `response` handler allows to perform some final evaluation logic * from the output of the last function to the expected GraphQL field type. * @param ctx the context object holds contextual information about the function invocation. */ export function response(ctx) { return ctx.prev.result }

次に、データソースから投稿項目を取得する関数 GET_ITEM を定義します。

import { util } from '@aws-appsync/utils' import * as ddb from '@aws-appsync/utils/dynamodb' /** * Request a single item from the attached DynamoDB table datasource * @param ctx the context object holds contextual information about the function invocation. */ export function request(ctx) { const { id } = ctx.args return ddb.get({ key: { id } }) } /** * Returns the result * @param ctx the context object holds contextual information about the function invocation. */ export function response(ctx) { const { error, result } = ctx if (error) { return util.appendError(error.message, error.type, result) } return ctx.result }

リクエスト中にエラーが発生した場合、関数のレスポンスハンドラーは、呼び出し元のクライアントに返されるエラーを GraphQL レスポンスに追加します。GET_ITEM 関数をリゾルバー関数リストに追加します。クエリを実行すると、GET_ITEM 関数のリクエストハンドラーは、AWS AppSync の DynamoDB モジュールによって提供される utils を使用して、id をキーとして使用する DynamoDBGetItem リクエストを作成します。ddb.get({ key: { id } }) は適切な GetItem オペレーションを生成します。

{ "operation" : "GetItem", "key" : { "id" : { "S" : "1" } } }

AWS AppSync はリクエストを使用して Amazon DynamoDB からデータを取得します。データが返されると、GET_ITEM 関数のレスポンスハンドラーによって処理されます。レスポンスハンドラーはエラーをチェックして結果を返します。

{ "result" : { "id": 1, "title": "hello world", "content": "<long story>" } }

最後に、リゾルバーのレスポンスハンドラーは結果を直接返します。

エラーの使用

リクエスト中に関数でエラーが発生した場合、そのエラーは ctx.error の関数レスポンスハンドラーで利用できるようになります。util.appendError ユーティリティを使用して GraphQL レスポンスにエラーを追加できます。stash を使用すると、パイプライン内の他の関数がエラーを利用できるようになります。次の例を参照してください。

/** * Returns the result * @param ctx the context object holds contextual information about the function invocation. */ export function response(ctx) { const { error, result } = ctx; if (error) { if (!ctx.stash.errors) ctx.stash.errors = [] ctx.stash.errors.push(ctx.error) return util.appendError(error.message, error.type, result); } return ctx.result; }

ユーティリティ

AWS AppSync には、APPSYNC_JS ランタイムによるリゾルバーの開発に役立つ 2 つのライブラリが用意されています。

  • @aws-appsync/eslint-plugin - 開発中に問題を迅速に発見して修正します。

  • @aws-appsync/utils - コードエディターで型検証とオートコンプリートを行います。

eslint プラグインの設定

ESLint は、コードを静的に分析して問題をすばやく見つけるツールです。ESLint は継続的インテグレーションパイプラインの一部として実行できます。@aws-appsync/eslint-pluginは、APPSYNC_JS ランタイムを利用する際にコード内の無効な構文を検出する ESLint プラグインです。このプラグインを使用すると、変更をクラウドにプッシュしなくても、開発中にコードに関するフィードバックを迅速に得ることができます。

@aws-appsync/eslint-plugin には、開発中に使用できる 2 つのルールセットが用意されています。

"plugin:@aws-appsync/base" は、プロジェクトで活用できる基本ルールセットを設定します。

ルール 説明
非同期 非同期のプロセスと Promise はサポートされていません。
no-await 非同期のプロセスと Promise はサポートされていません。
no-classes ルールはサポートされていません。
no-for for はサポートされていない (サポートされている for-in および for-of は除く)
no-continue continue はサポートされていません。
no-generators ジェネレータはサポートされていません。
no-yield yield はサポートされていません。
no-labels ラベルはサポートされていません。
no-this this キーワードはサポートされていません。
no-try try/catch 構造はサポートされていません。
no-while while ループはサポートされていません。
no-disallowed-unary-operators ++-- および ~ 単項演算子は使用できません。
no-disallowed-binary-operators instanceof 演算子は使用できません。
no-promise 非同期のプロセスと Promise はサポートされていません。

"plugin:@aws-appsync/recommended" にはいくつかの追加のルールがありますが、プロジェクトにTypeScript構成を追加する必要もあります。

ルール 説明
no-recursion 再帰的な関数呼び出しは許可されません。
no-disallowed-methods 使用できないメソッドもあります。サポートされている組み込み関数の全セットについては、「リファレンス」をご覧ください。
no-function-passing 関数を関数の引数として関数に渡すことはできません。
no-function-reassign 関数は再割り当てできません。
no-function-return 関数を関数の戻り値にすることはできません。

プラグインをプロジェクトに追加するには、「ESLint 入門」のインストールと使用手順に従ってください。次に、プロジェクトパッケージマネージャー (npm、yarn、pnpm など) を使用してプロジェクトにプラグインをインストールします。

$ npm install @aws-appsync/eslint-plugin

.eslintrc.{js,yml,json} ファイルで "plugin:@aws-appsync/base"または "plugin:@aws-appsync/recommended"extends プロパティに追加します。以下のスニペットは JavaScript の基本的なサンプル .eslintrc 設定です。

{ "extends": ["plugin:@aws-appsync/base"] }

"plugin:@aws-appsync/recommended" ルールセットを使用するには、必要な依存関係をインストールします。

$ npm install -D @typescript-eslint/parser

.eslintrc.js ファイルを作成するには

{ "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2018, "project": "./tsconfig.json" }, "extends": ["plugin:@aws-appsync/recommended"] }

バンドル、TypeScript、ソースマップ

ライブラリの活用とコードのバンドル

リゾルバーと関数コードでは、APPSYNC_JS 要件を満たす限り、カスタムライブラリと外部ライブラリの両方を活用できます。これにより、アプリケーション内の既存のコードを再利用できます。複数のファイルで定義されているライブラリを利用するには、esbuild などのバンドルツールを使用して、コードを 1 つのファイルにまとめ、AWS AppSyncリゾルバーまたは関数に保存する必要があります。

コードをバンドルするときは、次の点に留意してください。

  • APPSYNC_JS は、ECMAScript モジュール (ESM) のみをサポートします。

  • @aws-appsync/* モジュールは APPSYNC_JS に統合されており、バンドルすべきではありません。

  • APPSYNC_JS ランタイム環境は、コードがブラウザ環境では実行されないという点で NodeJS に似ています。

  • オプションでソースマップを含めることができます。ただし、ソースコンテンツを含めないでください。

    ソースマップの詳細については、「ソースマップの使用」を参照してください。

例えば、src/appsync/getPost.resolver.js にあるリゾルバーコードをバンドルするには、次の esbuild CLI コマンドを使用することができます。

$ esbuild --bundle \ --sourcemap=inline \ --sources-content=false \ --target=esnext \ --platform=node \ --format=esm \ --external:@aws-appsync/utils \ --outdir=out/appsync \ src/appsync/getPost.resolver.js

コードのビルドとTypeScript での作業

TypeScript はMicrosoft が開発したプログラミング言語で、TypeScript タイピングシステムとともに JavaScript のすべての機能を備えています。TypeScript を使用すると、タイプセーフなコードを記述し、ビルド時にコードを AWS AppSync に保存する前にエラーやバグcatch できます。@aws-appsync/utils パッケージは完全に型指定されています。

APPSYNC_JS ランタイムは TypeScript を直接サポートしていません。コードを AWS AppSync に保存する前に、まず TypeScript コードを APPSYNC_JS ランタイムがサポートする JavaScript コードにトランスパイルする必要があります。TypeScript を使用してローカルの統合開発環境 (IDE) でコードを記述できますが、AWS AppSync コンソールでは TypeScript コードを作成できないことに注意してください。

開始する前に、プロジェクトに TypeScript がインストールされていることを確認してください。次に、TSConfigを使用して APPSYNC_JS ランタイムと連携するようにTypeScriptのトランスコンパイル設定を構成します。使用できる基本 tsconfig.json ファイルの例を次に示します。

// tsconfig.json { "compilerOptions": { "target": "esnext", "module": "esnext", "noEmit": true, "moduleResolution": "node", } }

その後、esbuild などのバンドルツールを使用して、コードをコンパイルしてバンドルできます。例えば、AWS AppSync コードが置かれているプロジェクトが src/appsync にある場合、以下のコマンドを使用してコードをコンパイルしてバンドルできます。

$ esbuild --bundle \ --sourcemap=inline \ --sources-content=false \ --target=esnext \ --platform=node \ --format=esm \ --external:@aws-appsync/utils \ --outdir=out/appsync \ src/appsync/**/*.ts

Amplify Codegen を使用する

Amplify CLI を使用してスキーマのタイプを生成できます。schema.graphql ファイルが置かれているディレクトリから以下のコマンドを実行し、プロンプトを確認して codegen を設定します。

$ npx @aws-amplify/cli codegen add

特定の状況 (スキーマの更新時など) で codegen を再生成するには、以下のコマンドを実行します。

$ npx @aws-amplify/cli codegen

その後、生成された型をリゾルバーコードで使用できます。例として、次のスキーマがあるとします。

type Todo { id: ID! title: String! description: String } type Mutation { createTodo(title: String!, description: String): Todo } type Query { listTodos: Todo }

生成された型は、次のサンプル AWS AppSync 関数で使用できます。

import { Context, util } from '@aws-appsync/utils' import * as ddb from '@aws-appsync/utils/dynamodb' import { CreateTodoMutationVariables, Todo } from './API' // codegen export function request(ctx: Context<CreateTodoMutationVariables>) { ctx.args.description = ctx.args.description ?? 'created on ' + util.time.nowISO8601() return ddb.put<Todo>({ key: { id: util.autoId() }, item: ctx.args }) } export function response(ctx) { return ctx.result as Todo }

TypeScript でのジェネリックの使用

ジェネリックスは用意されているいくつかの型で使用できます。例えば、以下のスニペットは Todo タイプです。

export type Todo = { __typename: "Todo", id: string, title: string, description?: string | null, };

Todo を利用するサブスクリプション用のリゾルバーを書くことができます。お使いの IDE では、型定義とオートコンプリートのヒントを参考にして、toSubscriptionFilter 変換ユーティリティを適切に使用してください。

import { util, Context, extensions } from '@aws-appsync/utils' import { Todo } from './API' export function request(ctx: Context) { return {} } export function response(ctx: Context) { const filter = util.transform.toSubscriptionFilter<Todo>({ title: { beginsWith: 'hello' }, description: { contains: 'created' }, }) extensions.setSubscriptionFilter(filter) return null }

バンドルのリンティング

esbuild-plugin-eslint プラグインをインポートすることで、バンドルを自動的にリントできます。その後、eslint 機能を有効にする plugins 値を指定することで有効化できます。以下は、esbuild JavaScript API を build.mjs というファイルで使用しているスニペットです。

/* eslint-disable */ import { build } from 'esbuild' import eslint from 'esbuild-plugin-eslint' import glob from 'glob' const files = await glob('src/**/*.ts') await build({ format: 'esm', target: 'esnext', platform: 'node', external: ['@aws-appsync/utils'], outdir: 'dist/', entryPoints: files, bundle: true, plugins: [eslint({ useEslintrc: true })], })

ソースマップの使用

JavaScript コードでインラインソースマップ (sourcemap) を提供できます。ソースマップは、JavaScript または TypeScript コードをバンドルしていて、入力ソースファイルへの参照をログやランタイム JavaScript エラーメッセージに表示したい場合に便利です。

sourcemap はコードの最後に表示する必要があります。以下の形式の 1 行のコメントで定義されます。

//# sourceMappingURL=data:application/json;base64,<base64 encoded string>

例を示します。

//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibGliLmpzIiwgImNvZGUuanMiXSwKICAibWFwcGluZ3MiOiAiO0FBQU8sU0FBUyxRQUFRO0FBQ3RCLFNBQU87QUFDVDs7O0FDRE8sU0FBUyxRQUFRLEtBQUs7QUFDM0IsU0FBTyxNQUFNO0FBQ2Y7IiwKICAibmFtZXMiOiBbXQp9Cg==

ソースマップは esbuild で作成できます。以下の例は、コードをビルドしてバンドルするときに esbuild JavaScript API を使用してインラインソースマップを組み込む方法を示しています。

/* eslint-disable */ import { build } from 'esbuild' import eslint from 'esbuild-plugin-eslint' import glob from 'glob' const files = await glob('src/**/*.ts') await build({ sourcemap: 'inline', sourcesContent: false, format: 'esm', target: 'esnext', platform: 'node', external: ['@aws-appsync/utils'], outdir: 'dist/', entryPoints: files, bundle: true, plugins: [eslint({ useEslintrc: true })], })

特に、sourcemap および sourcesContent オプションでは、各ビルドの最後にソースマップをインラインで追加し、ソースコンテンツは含めないように指定しています。慣例として、ソースコンテンツは sourcemap には含めないことをお勧めします。これを無効にするには、sources-contentfalse に設定します。

ソースマップの仕組みを説明するために、リゾルバーコードがヘルパーライブラリのヘルパー関数を参照する次の例を確認してください。このコードには、リゾルバーコードとヘルパーライブラリーのログステートメントが含まれています。

./src/default.resolver.ts (自身のリゾルバー)

import { Context } from '@aws-appsync/utils' import { hello, logit } from './helper' export function request(ctx: Context) { console.log('start >') logit('hello world', 42, true) console.log('< end') return 'test' } export function response(ctx: Context): boolean { hello() return ctx.prev.result }

.src/helper.ts (ヘルパーファイル)

export const logit = (...rest: any[]) => { // a special logger console.log('[logger]', ...rest.map((r) => `<${r}>`)) } export const hello = () => { // This just returns a simple sentence, but it could do more. console.log('i just say hello..') }

リゾルバーファイルをビルドしてバンドルすると、リゾルバーコードにはインラインソースマップが含まれます。リゾルバーを実行すると、CloudWatch ログに次のエントリが表示されます。

CloudWatch ログのエントリを見ると、2 つのファイルの機能がバンドルされ、同時に実行されていることがわかります。各ファイルの元のファイル名もログにはっきりと反映されています。

テスト

EvaluateCode API コマンドを使用すると、コードをリゾルバーまたは関数に保存する前に、モックデータを使用してリゾルバーと関数ハンドラーをリモートでテストできます。コマンドを使い始めるには、ポリシーに appsync:evaluatecode アクセス許可を追加していることを確認してください。例:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "appsync:evaluateCode", "Resource": "arn:aws:appsync:<region>:<account>:*" } ] }

AWSCLI または AWSSDK を使用してコマンドを活用できます。例えば、CLI を使用してコードをテストするには、ファイルを指定し、コンテキストを指定し、評価するハンドラーを指定するだけです。

aws appsync evaluate-code \ --code file://code.js \ --function request \ --context file://context.json \ --runtime name=APPSYNC_JS,runtimeVersion=1.0.0

レスポンスには、ハンドラーから返されたペイロードを含む evaluationResult が含まれます。また、評価中にハンドラーによって生成されたログのリストを保持する logs オブジェクトも含まれています。これにより、コード実行のデバッグが容易になり、評価に関する情報を確認してトラブルシューティングに役立てることができます。例:

{ "evaluationResult": "{\"operation\":\"PutItem\",\"key\":{\"id\":{\"S\":\"record-id\"}},\"attributeValues\":{\"owner\":{\"S\":\"John doe\"},\"expectedVersion\":{\"N\":2},\"authorId\":{\"S\":\"Sammy Davis\"}}}", "logs": [ "INFO - code.js:5:3: \"current id\" \"record-id\"", "INFO - code.js:9:3: \"request evaluated\"" ] }

評価結果は JSON として解析でき、以下のようになります。

{ "operation": "PutItem", "key": { "id": { "S": "record-id" } }, "attributeValues": { "owner": { "S": "John doe" }, "expectedVersion": { "N": 2 }, "authorId": { "S": "Sammy Davis" } } }

SDK を使用すると、テストスイートのテストを簡単に組み込んで、コードの動作を検証できます。この例では Jest テストフレームワークを使用していますが、どのテストスイートでも機能します。次のスニペットは、仮説検証の実行を示しています。評価レスポンスは有効な JSON であることを想定しているので、JSON.parseを使用して文字列レスポンスから JSON を取得することに注意してください。

const AWS = require('aws-sdk') const fs = require('fs') const client = new AWS.AppSync({ region: 'us-east-2' }) const runtime = {name:'APPSYNC_JS',runtimeVersion:'1.0.0') test('request correctly calls DynamoDB', async () => { const code = fs.readFileSync('./code.js', 'utf8') const context = fs.readFileSync('./context.json', 'utf8') const contextJSON = JSON.parse(context) const response = await client.evaluateCode({ code, context, runtime, function: 'request' }).promise() const result = JSON.parse(response.evaluationResult) expect(result.key.id.S).toBeDefined() expect(result.attributeValues.firstname.S).toEqual(contextJSON.arguments.firstname) })

これにより、次のような結果になります。

Ran all test suites. > jest PASS ./index.test.js ✓ request correctly calls DynamoDB (543 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 totalTime: 1.511 s, estimated 2 s

VTL から JavaScript への移行

AWS AppSyncを使用すると、VTLまたはJavaScriptを使用してリゾルバーと関数のビジネスロジックを記述できます。どちらの言語でも、データソースとのやり取り方法を AWS AppSyncサービスに指示するロジックを記述します。VTL では、有効な JSON エンコードされた文字列に評価される必要があるマッピングテンプレートを作成します。JavaScript では、オブジェクトを返すリクエストハンドラーとレスポンスハンドラーを記述します。JSON エンコードされた文字列を返すことはありません。

例えば、次の VTL マッピングテンプレートを使用して Amazon DynamoDB アイテムを取得します。

{ "operation": "GetItem", "key": { "id": $util.dynamodb.toDynamoDBJson($ctx.args.id), } }

このユーティリティ $util.dynamodb.toDynamoDBJson は、JSON エンコードされた文字列を返します。$ctx.args.id<id> に設定すると、テンプレートは有効な JSON エンコードされた文字列と評価されます。

{ "operation": "GetItem", "key": { "id": {"S": "<id>"}, } }

JavaScript を使用する場合、コード内に JSON でエンコードされた生の文字列を出力する必要はなく、toDynamoDBJsonのようなユーティリティを使用する必要もありません。上記のマッピングテンプレートと同等の例を以下に示します。

import { util } from '@aws-appsync/utils'; export function request(ctx) { return { operation: 'GetItem', key: {id: util.dynamodb.toDynamoDB(ctx.args.id)} }; }

別の方法としては、util.dynamodb.toMapValues を使用する方法があります。これは、値のオブジェクトを処理する場合に推奨される方法です。

import { util } from '@aws-appsync/utils'; export function request(ctx) { return { operation: 'GetItem', key: util.dynamodb.toMapValues({ id: ctx.args.id }), }; }

これは以下のように評価されます。

{ "operation": "GetItem", "key": { "id": { "S": "<id>" } } }
注記

DynamoDB モジュールを DynamoDB データソースで使用することをお勧めします。

import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { ddb.get({ key: { id: ctx.args.id } }) }

別の例として、次のマッピングテンプレートを使用して Amazon DynamoDB データソースに項目を配置します。

{ "operation" : "PutItem", "key" : { "id": $util.dynamodb.toDynamoDBJson($util.autoId()), }, "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args) }

このマッピングテンプレート文字列を評価すると、有効な JSON でエンコードされた文字列が生成される必要があります。JavaScript を使用する場合、コードはリクエストオブジェクトを直接返します。

import { util } from '@aws-appsync/utils'; export function request(ctx) { const { id = util.autoId(), ...values } = ctx.args; return { operation: 'PutItem', key: util.dynamodb.toMapValues({ id }), attributeValues: util.dynamodb.toMapValues(values), }; }

以下として評価されます。

{ "operation": "PutItem", "key": { "id": { "S": "2bff3f05-ff8c-4ed8-92b4-767e29fc4e63" } }, "attributeValues": { "firstname": { "S": "Shaggy" }, "age": { "N": 4 } } }
注記

DynamoDB モジュールを DynamoDB データソースで使用することをお勧めします。

import { util } from '@aws-appsync/utils' import * as ddb from '@aws-appsync/utils/dynamodb' export function request(ctx) { const { id = util.autoId(), ...item } = ctx.args return ddb.put({ key: { id }, item }) }

データソースへの直接アクセスと Lambda データソース経由のプロキシのどちらかを選択する

AWS AppSyncと APPSYNC_JS ランタイムでは、AWS AppSync関数を使用してデータソースにアクセスすることで、カスタムビジネスロジックを実装する独自のコードを記述できます。これにより、追加の計算サービスやインフラストラクチャをデプロイしなくても、Amazon DynamoDB、Aurora Serverless、OpenSearch Service、HTTP API、その他の AWS サービスなどのデータソースと簡単に直接やり取りできます。 AWSAppSync では、Lambda データソースを設定することで AWS Lambda 関数を簡単に操作することもできます。Lambda データソースを使用すると、AWS Lambda のフルセット機能を使用して複雑なビジネスロジックを実行して GraphQL リクエストを解決できます。ほとんどの場合、ターゲットデータソースに直接接続された AWS AppSync 関数は、必要な機能をすべて提供します。APPSYNC_JS ランタイムでサポートされていない複雑なビジネスロジックを実装する必要がある状況では、Lambda データソースをプロキシとして使用してターゲットデータソースとやり取りできます。

データソースの直接統合 プロキシとしての Lambda データソース
ユースケース AWS AppSync functions interact directly with API data sources. AWS AppSync functions call Lambdas that interact with API data sources.
Runtime APPSYNC_JS (JavaScript) サポートされる Lambda ランタイム
Maximum size of code AWS AppSync 関数あたり 32,000 文字 50 MB (zip 圧縮済み、直接アップロード)
External modules 制限あり-APPSYNC_JS がサポートしている機能のみ Yes
Call any AWS service Yes - AWS AppSync HTTP データソースを使用中 Yes - AWS SDK を使用中
Access to the request header Yes Yes
Network access いいえ はい
File system access いいえ はい
Logging and metrics Yes Yes
Build and test entirely within AppSync Yes No
Cold start No No - プロビジョニング済み同時実行
Auto-scaling Yes - AWS AppSync による透過的な処理 Yes - Lambda で設定されているとおり
Pricing 追加料金なし Lambda の使用に対して課金

ターゲットデータソースと直接統合する AWS AppSync 関数は、次のような場合に理想的です。

  • Amazon DynamoDB、Aurora サーバーレス、OpenSearch サービスとのやり取り

  • HTTP API とのやりとりと受信ヘッダーの受け渡し

  • HTTP データソースを使用する AWS サービスとのやり取り (提供されたデータソースロールでリクエストに自動的に署名する AWS AppSync)

  • データソースにアクセスする前のアクセス制御の実装

  • リクエストに応答する前に、取得したデータをフィルター処理する。

  • リゾルバーパイプラインで AWS AppSync 関数を順次実行するシンプルなオーケストレーションの実装

  • クエリとミューテーションにおけるキャッシュ接続とサブスクリプション接続の制御。

Lambda データソースをプロキシとして使用する AWS AppSync 関数は、次のような場合に理想的です。

  • JavaScript または Velocity TL (VTL) 以外の言語を使用する

  • CPU またはメモリの調整と制御によるパフォーマンスの最適化

  • サードパーティ製ライブラリのインポートまたは APPSYNC_JS でサポートされていない機能の使用が必要

  • 複数のネットワークリクエストを行ったり、クエリを実行するためにファイルシステムにアクセスしたりする

  • バッチ設定を使用してリクエストをバッチ処理する。