パイプラインリゾルバー - AWS AppSync

パイプラインリゾルバー

AWS AppSync は GraphQL フィールドに対してリゾルバーを実行します。場合によっては、アプリケーションは 1 つの GraphQL フィールドを解決するために複数のオペレーションを実行する必要があります。パイプラインリゾルバーを使用すると、開発者はオペレーション (関数と呼ばれる) を構成して順番に実行できます。パイプラインリゾルバーは、たとえば、フィールドのデータを取得する前に承認チェックを実行する必要があるアプリケーションに便利です。

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

パイプラインリゾルバーは、Before マッピングテンプレート、After マッピングテンプレート、関数のリストで構成されています。各関数には、データソースに対して実行されるリクエストおよびレスポンスマッピングテンプレートがあります。パイプラインリゾルバーは実行をリスト内の関数に委任するため、いずれのデータソースにもリンクされていません。ユニットリゾルバーと関数は、データソースに対してオペレーションを実行するプリミティブです。詳細については、「リゾルバーのマッピングテンプレートの概要」を参照してください。

Before マッピングテンプレート

パイプラインリゾルバーのリクエストマッピングテンプレート (Before ステップとも呼ばれる) では、定義した関数を実行する前に準備ロジックを実行できます。

関数のリスト

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

After マッピングテンプレート

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

実行フロー

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

  1. パイプラインリゾルバーの Before マッピングテンプレート

  2. 関数 1: 関数のリクエストマッピングテンプレート

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

  4. 関数 1: 関数のレスポンスマッピングテンプレート

  5. 関数 2: 関数のリクエストマッピングテンプレート

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

  7. 関数 2: 関数のレスポンスマッピングテンプレート

  8. パイプラインリゾルバーの After マッピングテンプレート

パイプラインリゾルバーの実行フローは単方向であり、リゾルバーで静的に定義されています。

便利な Apache Velocity Template Language (VTL) ユーティリティ

アプリケーションの複雑さが増すにつれて、開発生産性を促進するのが、VTL ユーティリティとディレクティブの目的です。パイプラインリゾルバーでの作業には、以下のユーティリティが役立ちます。

$ctx.stash

stash は、リゾルバーと関数の各マッピングテンプレート内で使用可能になる Map です。同じ stash インスタンスは 1 つのリゾルバーの実行を通して存続します。つまり、stash を使用して、リクエストとレスポンスのマッピングテンプレート間、およびパイプラインリゾルバーの関数間で、任意のデータを渡すことができます。stash は Java Map データ構造と同じメソッドを継承しています。

$ctx.prev.result

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

#return(data: Object)

マッピングテンプレートから途中で戻る必要がある場合は、#return(data: Object) ディレクティブが便利です。#return(data: Object) は、プログラミング言語の return キーワードと似ています。これは、最も近いスコープのロジックブロックから戻るためです。つまり、リゾルバーのマッピングテンプレート内で #return を使用すると、リゾルバーから戻ることになります。リゾルバーのマッピングテンプレートで #return(data: Object) を使用すると、GraphQL フィールドに data が設定されます。さらに、関数のマッピングテンプレートから #return(data: Object) を使用すると、実行が関数から戻り、パイプライン内の次の関数に、またはリゾルバーのレスポンスマッピングテンプレートに継続されます。

#return

#return(data: Object) と同じですが、代わりに null が返されます。

$util.error

$util.error ユーティリティはフィールドエラーをスローするのに便利です。関数のマッピングテンプレート内で $util.error を使用すると、フィールドエラーがすぐにスローされ、それ以降の関数が実行されなくなります。詳細およびその他の $util.error 署名については、「リゾルバーのマッピングテンプレートのユーティリティリファレンス」を参照してください。

$util.appendError

注意: $util.appendError$util.error() と似ていますが、マッピングテンプレートの評価を中断しないという違いがあります。代わりに、フィールドにエラーがあったことを通知します。ただし、テンプレートを評価し、データを引き続き返すことも可能です。関数内で $util.appendError を使用しても、パイプラインの実行フローは中断されません。詳細およびその他の $util.error 署名については、「リゾルバーのマッピングテンプレートのユーティリティリファレンス」を参照してください。

パイプラインリゾルバーを作成する

AWS AppSync コンソールで、[スキーマ] ページに移動します。

以下のスキーマを保存します。

schema { query: Query mutation: Mutation } type Mutation { signUp(input: Signup): User } type Query { getUser(id: ID!): User } input Signup { username: String! email: String! } type User { id: ID! username: String email: AWSEmail }

パイプラインリゾルバーを Mutation 型の signUp フィールドにつなげます。右側にある [Mutation] 型で、signUp フィールドの横にある [Attach resolver (リゾルバーのアタッチ)] を選択します。[Create Resolver (リゾルバーの作成)] ページで、[Switch to Pipeline (パイプラインの切り替え)] ボタンをクリックします。このページには、Before マッピングテンプレートテキスト領域、関数セクション、After マッピングテンプレートテキスト領域の 3 つのセクションが表示されています。

パイプラインリゾルバーは、最初に E メールアドレスの入力を検証してからシステムにユーザーを保存することで、ユーザーをサインアップします。E メールの検証を validateEmail 関数内にカプセル化し、ユーザーの保存を saveUser 関数内にカプセル化します。validateEmail 関数が最初に実行され、E メールが有効であれば、saveUser 関数が実行されます。

実行フローは以下のようになります。

  1. Mutation.signUp リゾルバーのリクエストマッピングテンプレート

  2. validateEmail 関数

  3. saveUser 関数

  4. Mutation.signUp リゾルバーのレスポンスマッピングテンプレート

API の他のリゾルバーで validateEmail 関数を再利用することが多いため、GraphQL フィールド間でアクセス先の $ctx.args が変わるのを避けるとします。この場合、代わりに $ctx.stash を使用して、signUp(input: Signup) 入力フィールド引数からの email 属性を保存できます。

Before マッピングテンプレート:

## store email input field into a generic email key $util.qr($ctx.stash.put("email", $ctx.args.input.email)) {}

コンソールから、デフォルトのパススルー After マッピングテンプレートを使用できます。

$util.toJson($ctx.result)

関数を作成する

パイプラインリゾルバーページの [関数] セクションで、[関数の作成] をクリックします。リゾルバーページを通らずに関数を作成することもできます。そのためには、AWS AppSync コンソールで [関数] ページに進みます。[関数の作成] ボタンを選択します。E メールが有効であり特定のドメインからのものかどうかをチェックする関数を作成しましょう。E メールが無効な場合、この関数はエラーを発生させます。それ以外の場合、渡された入力をすべて転送します。

関数ページで none データソースを選択し、validateEmail リクエストマッピングテンプレートに入力します。

#set($valid = $util.matches("^[a-zA-Z0-9_.+-]+@(?:(?:[a-zA-Z0-9-]+\.)?[a-zA-Z]+\.)?(myvaliddomain)\.com", $ctx.stash.email)) #if (!$valid) $util.error("$ctx.stash.email is not a valid email.") #end { "payload": { "email": $util.toJson(${ctx.stash.email}) } }

レスポンスマッピングテンプレート:

$util.toJson($ctx.result)

これで、validateEmail 関数が作成されました。これらの手順を繰り返して、以下のリクエストとレスポンスのマッピングテンプレートで使用する saveUser 関数を作成します。シンプルになるように、none データソースを使用し、関数が実行された後にユーザーがシステムに保存されているように見せかけています。

リクエストマッピングテンプレート:

## $ctx.prev.result contains the signup input values. We could have also ## used $ctx.args.input. { "payload": $util.toJson($ctx.prev.result) }

レスポンスマッピングテンプレート:

## an id is required so let's add a unique random identifier to the output $util.qr($ctx.result.put("id", $util.autoId())) $util.toJson($ctx.result)

これで、saveUser 関数が作成されました。

パイプラインリゾルバーへの関数の追加

先ほど作成したパイプラインリゾルバーには関数が自動的に追加されています。コンソールの [関数] ページで関数を作成した場合は、リゾルバーページの [Add Function (関数の追加)] をクリックして、それらの関数をアタッチできます。validateEmailsaveUser の両方の関数をリゾルバーに追加します。validateEmail 関数は saveUser 関数の前に配置する必要があります。関数を追加するときには、上下の矢印を使用して関数の実行順序を並べ替えることができます。

クエリの実行

AWS AppSync コンソールで、[クエリ] ページに移動します。以下のクエリを入力します。

mutation { signUp(input: { email: "nadia@myvaliddomain.com" username: "nadia" }) { id username } }

以下のように返されます。

{ "data": { "signUp": { "id": "256b6cc2-4694-46f4-a55e-8cb14cc5d7fc", "username": "nadia" } } }

これで、パイプラインリゾルバーを使用して、ユーザーをサインアップし、入力 E メールを検証できました。パイプラインリゾルバーに焦点を当てたより完全なチュートリアルについては、「チュートリアル: パイプラインリゾルバー」を参照してください。