Serverless FrameworkでREST APIを作成してみた (Lambda, API Gateway, DynamoDB, Node.js)


はじめに

前回の記事でServerless FrameworkでLambdaの開発環境を構築しました。

引き続きServerless Frameworkの勉強ということで、Lambda(Node.js), API Gateway, DynamoDBの構成でREST APIを作成してみることにしました。

実装

Lambda関数の中身をjsファイル、構築するリソースの設定をserverless.ymlに記述します。

Lambda関数の作成

REST APIを実装するにあたり、データ作成(Create), 全データ取得(List), データ取得(Get), データ更新(Update), データ削除(Delete)の機能が必要です。
そのため、各機能ごとにLambda関数を作成する必要があります。

それぞれの関数を説明すると長くなるので、データ更新用の関数(create.js)のみ説明します。

create.js
'use strict';

const uuid = require('uuid');
const AWS = require('aws-sdk');

const dynamoDb = new AWS.DynamoDB.DocumentClient(); //ドキュメントクライアントを使用してNode.jsでDynamoDBにアクセス

module.exports.create = (event, context, callback) => {
  const timestamp = new Date().getTime();
  const data = JSON.parse(event.body); //JSONをJSのオブジェクトに変換

  //textがstringじゃないときにエラーを返す(バリデーション)
  if (typeof data.text !== 'string') {
    console.error('Validation Failed');
    callback(null, {
      statusCode: 400,
      headers: { 'Content-Type': 'text/plain' },
      body: 'Couldn\'t create the todo item.',
    });
    return;
  }

  //テーブル名(TableName)と各カラム(Item)のオブジェクト
  const params = { 
    TableName: process.env.DYNAMODB_TABLE,
    Item: {
      id: uuid.v1(),
      text: data.text,
      checked: false,
      createdAt: timestamp,
      updatedAt: timestamp,
    },
  };

  //DynamoDBへのデータ保存
  dynamoDb.put(params, (error) => {
    //エラー処理
    if (error) {
      console.error(error);
      callback(null, {
        statusCode: error.statusCode || 501,
        headers: { 'Content-Type': 'text/plain' },
        body: 'Couldn\'t create the todo item.',
      });
      return;
    }

    //処理成功時のレスポンス
    const response = {
      statusCode: 200,
      body: JSON.stringify(params.Item), //JSオブジェクトをJSONに変換
    };
    callback(null, response);
  });
};

Node.jsからDynamoDBにアクセスするために、ドキュメントクライアントを利用します。

const dynamoDb = new AWS.DynamoDB.DocumentClient();と定義することで、dynamoDb.get(データ取得)、dynamoDb.put(データ保存)のようにJSでDynamoDBのデータの基本操作を行うことができます。

データ保存を行うためには、テーブル名(TableName)とカラム(Item)のオブジェクト(params)を用意してdynamoDb.put(params, (error)のように引数で渡してあげればOKです。

  const params = { 
    TableName: process.env.DYNAMODB_TABLE,
    Item: {
      id: uuid.v1(),
      text: data.text,
      checked: false,
      createdAt: timestamp,
      updatedAt: timestamp,
    },
  };

dynamoDb.put(params, (error)の中身には、データ保存の処理が失敗したときと成功したときのcallbackを記述します。
処理が成功していれば、ステータスコード(statusCode)と保存内容(body)を返します。

    //処理成功時のレスポンス
    const response = {
      statusCode: 200,
      body: JSON.stringify(params.Item), //JSオブジェクトをJSONに変換
    };
    callback(null, response);

serverless.ymlの作成

API GatewayやDynamoDBのリソースを構築するために、serverless.ymlを記述します。

API Gatewayの構築

公式ドキュメントを参考に、Lambda関数のトリガーイベントとしてAPIGatewayを作成します。

functions: #Lambdaの構築
  create: #関数名を指定
    handler: todos/create.create #handler関数の指定(ディレクトリパス.モジュール名)
    events: #Lambdaのイベントトリガーを指定
      - http: #httpイベントをトリガとしてAPI Gatewayを構築
          path: todos #リソースの指定
          method: post #メソッドの指定
          cors: true #cors有効/無効の指定

DynamoDBの構築

DynamoDBの構築についても、公式ドキュメントを参考にします。

resources: #リソースの構築
  Resources:
    TodosDynamoDbTable: #DynamoDBの構築
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.DYNAMODB_TABLE}

IAM設定

LambdaからDynamoDBにアクセスするための権限を付与するために、ProvideriamRoleStatementsを追記します。

provider:
  name: aws
  runtime: nodejs12.x
  environment:
    DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: 'arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}'

デプロイ

以下のコマンドでデプロイを行います。

sls deploy -v

AWSコンソールのLambdaを開くと、作成した関数がデプロイされているのを確認できます。

API Gatewayを開くとdev-serverless-rest-api-with-dynamodbが作成されています。

また、DynamoDBにもテーブルが作成されます。

動作確認

insomniaを使って挙動をみてみます。

エンドポイントはデプロイの際にターミナルに表示されたものを使います。

endpoints:
  POST - https://zb6e0z65ka.execute-api.us-east-1.amazonaws.com/dev/todos
  GET - https://zb6e0z65ka.execute-api.us-east-1.amazonaws.com/dev/todos
  GET - https://zb6e0z65ka.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  PUT - https://zb6e0z65ka.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  DELETE - https://zb6e0z65ka.execute-api.us-east-1.amazonaws.com/dev/todos/{id}

試しにPOSTリクエスト(Create)を飛ばしてみると、200 OKとレスポンスが返ってきます。

また、DynamoDBのテーブルにはデータが追加されています。

おわりに

Serverless Framework超便利だね。

参考資料