AppSyncのパイプラインリゾルバーを解説!気になる仕組みを見てみよう💡

AppSyncのパイプラインリゾルバーを解説!気になる仕組みを見てみよう💡

こんにちは!

今日も AppSync について元気に投稿していきます!今回はパイプラインリゾルバーについて解説していきます。気になっているけどまだ使ったことない方が多いのではないでしょうか。

AppSync で GraphQL 開発をされている方、AppSync に興味のある方はぜひこの記事をご活用ください。

想定する読者

  • AppSync のパイプラインリゾルバーが気になっているヒト
  • AppSync の機能について詳しく知りたいヒト

はじめに

AppSync のパイプラインリゾルバー、皆様すでに利用されておりますでしょうか?

要件によってはとても便利な機能となりますので、まだ触れていない方はこの記事でイメージをつかんでいただければと思います。ご存知の方も、CloudFormation のテンプレートなども記載しておりますので、ぜひ最後までご一読ください!

パイプラインリゾルバーとは?

パイプラインリゾルバーとは、オペレーション(関数)を順番に実行できるAppSync の機能です。

まず始めに理解を高めるために、AppSync の GUI コンソール画面を見てみましょう。パイプラインリゾルバーがどんなものかイメージするのに役立ちます。

こちらはパイプラインリゾルバーの編集画面です。各セクションを確認してみると、以下のような流れになっています。

  • テンプレートをマッピングする前(Before マッピングテンプレート)
  • Functions
  • Functions – AppSyncDemoFuncConf1
  • Functions – AppSyncDemoFuncConf2
  • テンプレートをマッピングした後(After マッピングテンプレート)

動作としては、パイプラインリゾルバーを実行する前にまず、Before マッピングテンプレートが実行され、そのあとに指定した順序で関数(Functions)が実行されます(画像でいうと、AppSyncDemoFuncConf1、AppSyncDemoFuncConf2 のところになります)。そして最後に、After マッピングテンプレートが実行され結果が返されます。

この一連の動きがパイプラインリゾルバーの仕組みとなります。

ここでの注意点としては、Before と After マッピングテンプレートはあくまで関数の前後に何かしらのロジックを実行する際に使われているだけになりますので、データソースへのアクセスはできません。データソースに対する操作などは、必ず関数(Functions)内で実行しましょう。

それでは次に、各セクションについて解説していきます。

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

実行前に何かしらのロジックを実行するところです。

一般的には、再利用することの多い情報の文字列をセットします。その際は、$ctx.stash を使って以下のように記述します。

$util.qr($ctx.stash.put("email", $ctx.args.input.email))
{}

上記は、実行されたクエリ内にある “email” の値を $ctx.args.input.email で取得し、$ctx.stash で使えるようにセットしています。セットされた値は、$ctx.stash.email などの変数名で利用することができます。

2つ以上の関数で共通の値がある場合などは、Before マッピングテンプレートの段階で $ctx.stash を利用してみると便利です。

CloudFormation では、以下のように記述します。

Resources:
 PipelineResolver:
    Type: AWS::AppSync::Resolver
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId #required
      TypeName: "Query" #required
      FieldName: "getUserAndGroup" #required
      Kind: PIPELINE
      PipelineConfig:
        Functions: 
          - !GetAtt FunctionConfigurationStep1.FunctionId
          - !GetAtt FunctionConfigurationStep2.FunctionId
      RequestMappingTemplate: | # Before マッピングテンプレート
        $util.qr($ctx.stash.put("email", $ctx.args.input.email))
        {}
      ResponseMappingTemplate: | # After マッピングテンプレート
        $util.toJson($context.result)

RequestMappingTemplate の部分が Before マッピングテンプレートの部分になります。なお、ResponseMappingTemplate の部分には After マッピングテンプレートを記述します。

パラメータ「Functions」は実行したい順番で、FunctionId を列挙しましょう。

関数(Functions)

ここは通常のリゾルバーと同じようにリクエストとレスポンスマッピングテンプレートを記述していきましょう。前の関数の結果は $ctx.prev.result で取得できますので、2番目以降の関数で必要な時に活用しましょう。

CloudFormation では、以下のように記述します。

Resources:
  FunctionConfigurationStep1:
    Type: AWS::AppSync::FunctionConfiguration
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId #required
      DataSourceName: !GetAtt DataSource1.Name #required
      Description: This is demo.
      FunctionVersion: '2018-05-29' #required
      Name: AppSyncDemoFuncConf1 #required
      RequestMappingTemplate: |
        {
          "version": "2017-02-28",
          "operation": "GetItem",
          "key": {
              "id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
          }
        }
      ResponseMappingTemplate: |
        $util.toJson($context.result)

  FunctionConfigurationStep2:
    Type: AWS::AppSync::FunctionConfiguration
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId #required
      DataSourceName: !GetAtt DataSource2.Name #required
      Description: This is demo.
      FunctionVersion: '2018-05-29' #required
      Name: AppSyncDemoFuncConf2 #required
      RequestMappingTemplate: |
        {
          "version": "2017-02-28",
          "operation": "GetItem",
          "key": {
              "id": $util.dynamodb.toDynamoDBJson($ctx.prev.result.groupId),
          }
        }
      ResponseMappingTemplate: |
        #if($ctx.error)
            $util.error($ctx.error.message, $ctx.error.type)
        #end
        $util.qr($ctx.prev.result.put("groupName", $ctx.result.groupName))
        $util.toJson($ctx.prev.result)

上記の例を解説すると、1番目の関数で DynamoDB から id を取得したあと、1番目で取得した id と同じ値の groupId を2番目の関数で取得し、1番目の結果と2番目の結果の中にある「groupName」の値をマージしたものを最終的な結果として返しています。

なお2番目の関数のレスポンスマッピングテンプレートのところでは、$ctx.prev.result.put を実行することで、2つの関数の結果をマージしています。

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

こちらは最終的なレスポンスを返すところになります。

関数のほうでデータ操作を十分に行っている場合がほとんどですので、単純にパイプラインの最後の関数の出力を返すように記述します。複雑な操作はとくに必要のないところになります。

$util.toJson($context.result)

パイプラインリゾルバーのメリット

パイプラインリゾルバーは、1つのクエリに対して複数のデータソースを扱う場合にメリットを発揮します。

データソースが DynamoDB の場合は、2つ以上のテーブルに対して操作が必要なときなどに利用する際に便利です。また Cognito においては複数のリゾルバーを実行することにより複雑な認可制御を実現できるでしょう。

ぜひこの機会に触れてみて、試してみてください!

参考記事

まとめ

パイプラインリゾルバーの仕組みがなんとなくでも分かりましたでしょうか?一見、複雑にみえてもリゾルバーを複数順番に実行しているだけですので、紐解いていけば理解は早くなるかと思います。このブログを機に理解を深めていただければ幸いです。

このブログでは、GraphQL や GraphQL のマネージドサービスである AWS AppSync の記事をどんどん公開しておりますので、ご興味のある方はほかの記事もご覧いただければと思います。

AppSync に関する開発は、お気軽にお問い合わせください。