🎢

Serverless Framework + TypeScriptでAppSync環境を構築する

2021/10/29に公開

はじめに

Serverless FrameworkでAppSyncの環境を構築します。
AppSyncのプラグインが提供されているので、それを利用すればできるのですが、設定する内容が多く、かつ初見では難しいので最初から構築する内容をまとめてみました。

今回のソースは以下にありますので、全体像を見たい方はそちらも参照ください。
https://github.com/merutin/serverless-appsync-lambda-test/tree/sample-create

構成図

便宜上、PCにしていますが、ブラウザからGraphqlのリクエストすると、AppSyncを通じてlambdaを実行します。
構成図
シンプルにするために、以下の点を割り切っています

  • AppSyncはAPI_KEYによる認証にする
  • AppSyncのデータソースはlambdaのみとする
    • vtlを書かなくてよくなる
  • lambdaもOKを返すのみで処理は特に書かない

テンプレートの作成・修正

serverlessのテンプレートを利用して作成します。

npx serverless create -t aws-nodejs-typescript

個人的に上記で作成されるテンプレートは好きではないので、色々修正します。
参考:Serverless Frameworkのaws-nodejs-typescriptのテンプレートを詳しく見る

  • handler.tsの修正
    src下を全部削除して、以下のソースに置き換えします。
src/handler.ts
import 'source-map-support/register';

import { AppSyncResolverHandler } from 'aws-lambda';

export const sample: AppSyncResolverHandler<any, any> = async (event) => {
  console.log("event", event);

  return "OK";
}
  • package.jsonの修正
    まだnpm installをしていないので、package.jsonから不要なものをそのまま削除します。
    ちなみに、最新のtemplateでは、webpackからesbuildに変更になっているみたいです。おそらく次のリリースなのかな。
{
  "name": "serverless-appsync-lambda-test",
  ...
  "dependencies": {
-    "@middy/core": "^1.5.2",
-    "@middy/http-json-body-parser": "^1.5.2",
    "source-map-support": "^0.5.19"
  },
  "devDependencies": {
    "@serverless/typescript": "^2.23.0",
    "@types/aws-lambda": "^8.10.71",
    "@types/node": "^14.14.25",
-    "json-schema-to-ts": "^1.5.0",
    "serverless": "^2.23.0",
    "serverless-webpack": "^5.3.5",
    "ts-loader": "^8.0.15",
    "ts-node": "^9.1.1",
    "tsconfig-paths": "^3.9.0",
    "tsconfig-paths-webpack-plugin": "^3.3.0",
    "typescript": "^4.1.3",
    "webpack": "^5.20.2",
    "webpack-node-externals": "^2.5.2"
  },
  "author": "The serverless webpack authors (https://github.com/elastic-coders/serverless-webpack)",
  "license": "MIT"
}
  • serverless.tsの修正
    不要なものを削除して、regionを指定しておきます。
const serverlessConfiguration: AWS = {
---
  provider: {
    name: 'aws',
    runtime: 'nodejs14.x',
+    region: "ap-northeast-1",
-    apiGateway: {
-      minimumCompressionSize: 1024,
-      shouldStartNameWithService: true,
-    },
-----
-  // import the function via paths
-  functions: { hello },
+  functions: {
+    sample: {
+      handler: "src/handler.sample",
+    },
+  },
};
  • dependencyのインストール
    一通り修正ができたので、npm installしておきます。
npm install

graphqlファイルの作成

Queryのsampleはhandler.tsのsampleを実行するように設定していきます。

graphql/schema.graphql
type Query {
  sample: String!
}

以降に出てくるキャプチャーにはdummyという引数が出てくる場合がりますが、 sample(dummy: Boolean): String! と記載していたためなので無視してください。

AppSync環境の構築

serverless frameworkのプラグインをインストールします。
AppSyncのプラグインに加えて、ローカルである程度動作確認をしたいので、offlineで動作するプラグインを入れます。

  • serverless-offline ローカルでの動作確認用
  • serverless-appsync-plugin AppSyncの開発に必要なプラグイン
  • serverless-appsync-simulator AppSyncのローカル開発用のシミュレーター
npm install -D serverless-offline serverless-appsync-plugin serverless-appsync-simulator

serverless.tsの中身を修正します。
AppSyncの設定周りは色々書くことがあるので大変ですが、pluginのReadmeなど見ながらやっていきます。
ここの設定が一番大変です。

  custom: {
    webpack: {
      webpackConfig: "./webpack.config.js",
      includeModules: true,
    },
+    appSync: {
+      // AppSyncにデプロイするときの名前。${opt:stage}つけてステージごとに違う名称にしておいた方がわかりやすい
+      name: "appsync-sample", 
+      // 認証タイプ。今回は簡単なAPI_KEYで設定する
+      authenticationType: "API_KEY", // API_KEY or AWS_IAM or AMAZON_COGNITO_USER_POOLS or OPENID_CONNECT or AWS_LAMBDA
+      schema: "./graphql/schema.graphql", // schemaファイルのパス。複数ファイルや正規表現(glob)もOK
//     認証タイプにAPI_KEYを設定した場合のみ必要な情報
+      apiKeys: [
+        {
+          name: "test-api-key", // apiKeyの名前
+          description: "AppSync test", // 説明
+          expiresAfter: "30d", // 有効期限(デプロイしてから、どのくらいで期限切れになるか)。最大365d。
+          // expiresAt: '2021-03-09T16:00:00+00:00' で指定すれば年月で指定できる
+        },
+      ],
+      // falseにしておくと、lambdaの呼び出しがデフォルトになる
+      // dynamoDBや他のものがメインの場合は基本となるvtlを指定しておくと楽かも
+      defaultMappingTemplates: {
+        request: false,
+        response: false,
+      }, 
+      mappingTemplates: [
+        // queryとdataSourceを結びつける部分
+        {
+          dataSource: "sample",
+          type: "Query",
+          field: "sample",
+        },
+      ],
+      dataSources: [
+        // lambdaをdataSourceに設定する部分。この設定をすることでAppSyncからlambdaを呼び出せる
+        {
+          type: "AWS_LAMBDA",
+          name: "sample",
+          config: {
+            functionName: "sample",
+          },
+        },
+      ],
+    },
+    "appsync-simulator": {
+    // localで動作するときに利用するシミュレーターの設定
+      location: ".webpack/service",
+      apiKey: "da2-fakeApiId123456",
+      watch: false, // この設定しないと、watchmanが入っていない場合に動作しなくなる
+    },
},
  • serverless.tsは以下のようになっているかと思います。
serverless.tsの全体
serverless.ts
import type { AWS } from "@serverless/typescript";

const serverlessConfiguration: AWS = {
  service: "serverless-appsync-lambda-test",
  frameworkVersion: "2",
  custom: {
    webpack: {
      webpackConfig: "./webpack.config.js",
      includeModules: true,
    },
    appSync: {
      name: "appsync-sample-${opt:stage}", 
      authenticationType: "API_KEY", 
      schema: "./graphql/schema.graphql", 
      apiKeys: [
        {
          name: "test-api-key", 
          description: "AppSync test", 
          expiresAfter: "30d", 
        },
      ],
      defaultMappingTemplates: {
        request: false,
        response: false,
      },
      mappingTemplates: [
        {
          dataSource: "sample",
          type: "Query",
          field: "sample",
        },
      ],
      dataSources: [
        {
          type: "AWS_LAMBDA",
          name: "sample",
          config: {
            functionName: "sample",
          },
        },
      ],
    },
    "appsync-simulator": {
      location: ".webpack/service",
      apiKey: "da2-fakeApiId123456",
      watch: false,
    },
  },
  plugins: [
    "serverless-webpack",
    "serverless-appsync-simulator",
    "serverless-appsync-plugin",
    "serverless-offline",
  ],
  provider: {
    name: "aws",
    runtime: "nodejs14.x",
    // stageはあったほうが何かと便利なので追加
    stage: '${opt:stage, "local"}',
    region: "ap-northeast-1",
    environment: {
      AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1",
    },
    lambdaHashingVersion: "20201221",
  },
  functions: {
    sample: {
      handler: "src/handler.sample",
    },
  },
};

module.exports = serverlessConfiguration;

AppSyncの起動(ローカル)

まずはテストとして、ローカルでの動作を確認していきます。
serverless offlineのコマンドを実行して起動します。

npx serverless offline start --stage local

起動すると、http://localhost:20002 でGraphiQlが起動するのでアクセスします。

GraphiQl

左側からsampleにチェックを入れて実行すると結果が右側に表示されます。
これでローカルでの起動の確認が出来ました。

AppSyncの起動(AWS)

serverlessのコマンドを実行してデプロイします
何度も実行することになるので、package.jsonに書くがいいのですが、検証なので直接実行します。

npx serverless deploy --stage test

AppSyncのAPIの一覧 に今回デプロイした「appsync-sample-test」が増えています。

「appsync-sample-test」を選択して、左のペインからクエリを選択します。
ローカルで起動したときのGraphiQlと同じような画面が表示されるので、同様にsampleにチェックを入れて実行してみます。

{
  "data": {
    "sample": "OK"
  }
}

が返ってきたら成功です。

最後に

今回は環境を1から作成するところをできる限りシンプルに紹介してみました。
実際に利用する場合には、認証がAPI_KEYではなかったり、lambda以外にdynamoDBを利用したりすると思います。
次回はDynamoDBを追加して動作できるようにする予定です。

作成したものを削除する場合は以下のコマンドを実行してください。

npx serverless remove --stage test 

Discussion