Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@IMdev

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

はじめに

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つを行う必要があります。

終わりに

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

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
IMdev
主にIoT関連、Web系のエンジニア。JavaScript、Vueなど。 最近はTypeScriptに興味があります。 Mind Sphere/IoT/Raspberry Pi/PHP/Python/IoT
ferix
R&Dを中心に主に受託開発でソフトウェアを開発するエンジニア集団

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?