Chapter 03

AWS AppSync + Lambda + DynamoDBの環境を作成する

shitakemura
shitakemura
2022.02.13に更新

はじめに

本チャプターではAWS AppSync + Lambda + DynamoDBの構成を AWS CDK を使って作成します。
Lambda 関数の作成は、GraphQL のスキーマ定義を作成 -> TypeScript の型定義を自動生成 -> 実装の流れでおこないます。Todo アプリでは複数の Lambda 関数を作成しますが、ここではまず Todo 一覧を取得する関数を実装します。

GraphQL のスキーマを定義する

GraphQL API のスキーマ定義を作成します。
schema.graphqlを作成し、Todo の型 Todoと Todo 一覧を取得する クエリgetTodosを定義します。

./infra-backend/graphql/schema.graphql
scalar AWSTimestamp

type Todo {
  id: ID!
  title: String!
  completed: Boolean!
  createdAt: AWSTimestamp!
}

type Query {
  getTodos: [Todo!]!
}

GraphQL スキーマから TypeScript の型定義ファイルを自動生成する

型定義ファイルの作成にはGraphQL Code Generatorを使用します。

まず必要なパッケージをインストールします。

./infra-backend
$ npm i graphql @graphql-codegen/cli @graphql-codegen/typescript

続いて、GraphQL Code Generator の設定ファイルcodegen.ymlを作成します。

./infra-backend/codegen.yml
overwrite: true
schema:
  - graphql/schema.graphql
generates:
  ./graphql/generated/generated-types.ts:
    plugins:
      - typescript

この設定でスキーマ定義ファイルgraphql/schema.graphqlが読み込まれて、
./graphql/generated/generated-types.tsに型定義が出力されます。

npm コマンドから型情報の生成が行えるようpackage.jsonを更新します。

./infra-backend/package.json
  "scripts": {
    ...
+    "codegen": "graphql-codegen"
  }

npm コマンドを実行します。

./infra-backend
$ npm run codegen

generated-types.tsに型定義が生成されているのを確認します。

Todo 一覧を取得する Lambda 関数を実装する

必要なパッケージをインストールします。

./infra-backend
$ npm i aws-sdk
$ npm i -D @types/aws-lambda

続いて、Todo 一覧を取得する Lambda 関数を作成します。

./infra-backend/lambda/getTodos.ts
import { AppSyncResolverHandler } from "aws-lambda";
import { DynamoDB } from "aws-sdk";
import { Todo } from "../graphql/generated/generated-types";

const docClient = new DynamoDB.DocumentClient();

export const handler: AppSyncResolverHandler<
  null,
  Todo[] | null
> = async () => {
  try {
    if (!process.env.TODO_TABLE) {
      console.log("TODO_TABLE was not specified");
      return null;
    }

    const data = await docClient
      .scan({ TableName: process.env.TODO_TABLE })
      .promise();

    return data.Items as Todo[];
  } catch (err) {
    console.error(`DynamoDB error: ${err}`);
    return null;
  }
};

import { Todo } from "../graphql/generated/generated-types";の部分で自動生成した型定義を使用しています。

AWS CDK で AWS AppSync + Lambda + DynamoDB の環境を作成する

必要なパッケージをインストールします。

./infra-backend
$ npm i -D @aws-cdk/aws-appsync-alpha@2.10.0-alpha.0

AWS CDK でAWS AppSync + Lambda + DynamoDBの構成を実装します。
AWS AppSync の認証はAPI-KEY認証とし、データソースに lambda 関数getTodosを設定し、リゾルバーを作成します。

./infra-backend/lib/infra-backend-stack.ts
import * as cdk from "aws-cdk-lib";
import * as lambdaNodeJs from "aws-cdk-lib/aws-lambda-nodejs";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as appsync from "@aws-cdk/aws-appsync-alpha";
import { Construct } from "constructs";
import * as path from "path";

export class InfraBackendStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // DynamoDB
    const todoTable = new dynamodb.Table(this, "TodoTable", {
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      partitionKey: {
        name: "id",
        type: dynamodb.AttributeType.STRING,
      }
    });

    // AppSync
    const todoApi = new appsync.GraphqlApi(this, "TodoGraphqlApi", {
      name: "todo-graphql-api",
      schema: appsync.Schema.fromAsset("graphql/schema.graphql"),
      authorizationConfig: {
        defaultAuthorization: {
          authorizationType: appsync.AuthorizationType.API_KEY,
          apiKeyConfig: {
            expires: cdk.Expiration.after(cdk.Duration.days(365)),
          },
        },
      },
    });

    // Lambda function
    const getTodosLambda = new lambdaNodeJs.NodejsFunction(
      this,
      "getTodosHandler",
      {
        entry: path.join(__dirname, "../lambda/getTodos.ts"),
        handler: "handler",
        environment: {
          TODO_TABLE: todoTable.tableName,
        },
      }
    );

    todoTable.grantReadData(getTodosLambda);

    // DataSource
    const getTodosDataSource = todoApi.addLambdaDataSource(
      "getTodosDataSource",
      getTodosLambda
    );

    // Resolver
    getTodosDataSource.createResolver({
      typeName: "Query",
      fieldName: "getTodos",
    });

    // CfnOutput
    new cdk.CfnOutput(this, "GraphQlApiUrl", {
      value: todoApi.graphqlUrl,
    });

    new cdk.CfnOutput(this, "GraphQlApiKey", {
      value: todoApi.apiKey || "",
    });
  }
}

cdk diffcdk deployを実行してデプロイを行います。

./infra-backend
$ cdk diff
$ cdk deploy

AWS 管理画面で動作を確認する

AWS 管理画面でデプロイした GraphQL クエリgetTodosの動作確認をします。

DynamoDB に Todo Item を作成する

DynamoDB > 項目 > InfraBackendServiceStack-TodoTableXXXX > 項目の作成より の TodoItem を複数個作成します。

AWS AppSync で クエリ getTodos の動作確認をする

AWS AppSync > Todo-graph-api > QueriesでクエリgetTodosの動作確認をします。

作成したデータが取得できるのを確認できました。

以上で AWS AppSync + Lambda + DynamoDB の環境作成は完了です。

参考情報