LoginSignup
1

More than 1 year has passed since last update.

AppSync+Serverless Frameworkでoperationを追加する一案

Last updated at Posted at 2020-10-13

はじめに

Serverless Framework+AppSync各種プラグインによる開発を進めているのですが、一つのoperationフィールドを追加するだけで割と多くの操作が必要になってくることに気付きました。
個人的に苦手なマッピングテンプレートに工数を割くのが嫌なので、マッピングテンプレートは可能な限り避けて、JavaScript/TypeScript側で操作できる方法をご紹介します。

必要なもの

Serverless Frameworkテンプレートとして以下のものを使わせていただきました。

[daisuke-awaji/serverless-appsync-offline-typescript-template]
解説: Effective AppSync 〜 Serverless Framework を使用した AppSync の実践的な開発方法とテスト戦略 〜

また事前に以下もインストールが必要です。

  • JDK(DynamoDB-localに必要)
  • Serverless Framework

GraphQL operation

Operationというのは、API Gatewayでいうリソースに相当する(と思う)ものです。
たとえばTodoリストを登録したり表示したりするAPIを作ろうとするとき、Todoのリスト一覧を返してくれる「listTodo」のようなQueryフィールドが必要になります。このフィールドの追加、および実際に処理を実行するLambda関数との紐付けるは、すべてServerless Frameworkの設定ファイルserverless.ymlに記述することになります

一例として以下のようになります。

custom:
  appSync:
    # ローカルではこの認証方式でないと動かない……
    authenticationType: AMAZON_COGNITO_USER_POOLS
    userPoolConfig:
      awsRegion: ap-northeast-1
      userPoolId: ap-northeast-1_xxxxx
      defaultAction: ALLOW
    mappingTemplates:
      - field: {フィールド名}
        datasource: {dataSources.nameで定義された名前}
        request: default.request.vtl
        response: default.response.vtl
    dataSources:
      - type: AWS_Lambda
        name: {dataSources名}
        description: Lambda
        config:
          functionName: {Lambda関数名}
          iamRoleStatements:
            - Effect: "Allow"
              Action:
                - "lambda:invokeFunction"
              Resource:
                - "*"
functions:
  {Lambda関数名}:
    handler: src/handler.main

以上のうち、フィールドを増やすにはmappingTemplatesという箇所と、Lambda関数のファイルであるsrc/handler.mainを変更します。

Queryフィールド追加の一例

例えばTodoのIDを指定して特定Todoだけを抽出するQueryフィールドgetTodoById(id: String)を追加したい、という場合を考えてみます。
まずスキーマを定義します。定義はschema.graphqlのQueryタイプに定義を追加します。

type Todo {
  id: ID!
  name: String!
  completed: Boolean!
}

type Query {
  getTodoById(id: String!): Todo
}

次に、serverless.ymlのmappingTemplatesに追加をします。dataSources名はds_mainとしています。

    mappingTemplates:
      - field: getTodoById
        datasource: ds_main
        request: default.request.vtl
        response: default.response.vtl
    dataSources:
      - type: AWS_LAMBDA
        name: ds_main
        description: Lambda DataSource for application
        config:
          functionName: dsMain
          iamRoleStatements:
            - Effect: "Allow"
              Action:
                - "lambda:invokeFunction"
              Resource:
                - "*"

functions:
  dsMain:
    handler: src/handler.handler

ハンドラー自体は、src/handler.tsとして保存しています。

あとはLambda関数のコードで、event.info.fieldNameでQueryフィールド名getTodoByIdを取得できるので、switch構文などで呼び出すTypeScriptコードを呼び出す、ということができます。

src/handler.ts

import getTodoById from './getTodoById';

exports.handler = async(event, _context, _callback)=> {
    switch (event.info.fieldName) {
        case "getTodoById":
            return await getTodoById(event.arguments.id);
        default:
            return null;
    }
}

上記コードで呼び出されるgetTodoById.tsの中身は例えば以下のような感じです。

const AWS = require('aws-sdk');
AWS.config.update({region:'ap-northeast-1'});
const docClient = new AWS.DynamoDB.DocumentClient();


async function getTodoById(id: String) {
    const params = {
        TableName: process.env.TODO_TABLE,
        Key: { id }
    }
    try {
        const { Item } = await docClient.get(params).promise()
        return Item
    } catch (err) {
        console.log('DynamoDB error: ', err)
    }
}

export default getTodoById

マッピングテンプレート

AppSyncではリゾルバをマッピングテンプレートとして書きます
マッピングテンプレートはVTLで書きますが、個人的にはこの言語をいじるのは避けたいので適当なファイル名default.request.vtlなどを作ってこの2つだけを使用することにします。

mapping-templates/default.request.vtl

{
    "version" : "2017-02-28",
    "operation": "Invoke",
    "payload": $utils.toJson($context)
}

またレスポンス用のマッピングテンプレートは以下の通りです。

mapping-templates/default.response.vtl

$util.toJson($context.result)

これで一つのフィールドを追加することができます。
マッピングテンプレートは使い回すので、基本的には

  • schema.graphqlにフィールドを追加
  • serverless.ymlにフィールドを追加
  • Lambda用ファイルを追加・修正

の3つを行う必要があります。

終わりに

マッピングテンプレートを書くべきかどうかは開発規模によると思うので、適宜よい方法を見つけることをおすすめします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1