🌳

【AWS】【Serverless Framework 入門】 API Gateway + Lambda + DynamoDB サンプル

2021/12/25に公開

はじめに

こちらの記事の続編になります。

前回は Serverless Framework を使って hello 関数を Lambda にデプロイしたところまで行いました。
(Lambda をただデプロイできれば前編の記事は見なくても全然大丈夫です。)

今回はそれを API Gateway 経由で API として公開し、さらに DynamoDB との疎通にも試みるものになります。

以下の Serverless Framework の公式サンプルを参考にしました。

できたこと

以下の図の環境を Serverless Framework で構築しました。

png.png

ソースは GitHub に公開したのでそちらを参照してください。
公式サンプルの「AWS | REST API With DynamoDB」 とほとんど同じように実装しました。

一気に解説すると大変なので以下の手順に分けて見ていきたいと思います。

  1. API の公開(練習)
  2. DynamoDB のテーブル作成
  3. API の公開と DynamoDB との疎通

1. API の公開(練習)

以下の公式サンプルを参考にしました。
AWS | REST API Example

hello 関数を GET メソッドの API として公開してみたいと思います。

serverless.yml の変更

serverless.yml に https リクエストのイベントを追加します。

serverless.yml
 functions:
   hello:
     handler: handler.hello
+    events:
+      - http:
+          path: hello
+          method: get

デプロイ

変更後にデプロイしてみます。

$ sls deploy -v
(省略)
Serverless: Stack update finished...
Service Information
service: hogehoge
stage: dev
region: us-east-1
stack: hogehoge-dev
resources: 11
api keys:
  None
endpoints:
  GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
  hello: hogehoge-dev-hello
layers:
  None

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:xxxxxxxxx:function:hogehoge-dev-hello:5
ServiceEndpoint: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev
ServerlessDeploymentBucketName: hogehoge-dev-serverlessdeploymentbucket-1q2qo8ezm7dcf

https リクエストのイベントを追加したことによって、こちらのエンドポイントが開放されます。

endpoints:
  GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello

API の疎通確認

curl で叩いてみます。

$ curl https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": {
    "resource": "/hello",
    (省略)
  }
}

レスポンスが返ってくることが確認できました。API の公開の成功です。

AWS コンソールの確認

AWS コンソールの API Gateway を確認してみます。
gateway.png
このように API が登録されていることがわかります。
ちなみに serverless.ymlevents.http.path を記述しないと API Gateway には何も登録されません。

以上で API を公開する方法はわかりました。

2. DynamoDB のテーブル作成

以下の公式サンプルを参考にしました。
AWS | REST API With DynamoDB

こちらのサンプルの DynamoDB のテーブル作成だけを抜き出したものになります。

serverless.yml の変更

serverless.yml
 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}"

(省略)

+resources:
+  Resources:
+    TodosDynamoDbTable:
+      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}

providerresources に分けて簡単に解説します。

provider

provider には共通で用いる環境変数の設定や Lambda から他のリソースに関する IAM ロールなどを設定します。

serverless.yml
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}"
  • opt:xxx
    • コマンドライン引数にアクセスできます。例えばコマンドラインで sls deploy --stage prd と実行した場合は ${opt:stage} = prd となります。
  • self:xxx
    • この記法で自身の serverless.yml で定義している key にアクセスできます。
  • ${opt:xxx, self:xxx}
    • opt:xxx が定義されていればその値を優先し、もし、定義されていないければ self:xxx の値を使用する書き方です。
  • environment
    • 環境変数を定義します。今回はここにデーブル名を定義します。
  • iamRoleStatements
    • 実行時に他の AWS リソースを参照する場合にここで Lambda の IAM ロールを設定します。今回は DynamoDB へのアクセス許可を追加しています。
  • Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
    • 設定した IAM ロールはここの項目で設定したリソースに対して有効になります。DynamoDB の特定のテーブルに対して有効にする場合このようになります。ちなみに ${self:provider.environment.DYNAMODB_TABLE} は上記で解説しているように environment で定義している DYNAMODB_TABLE の値になります。

resources

resources には AWS リソースの設定事項を記述します。

今回は DynamoDB のテーブル定義を記述していきます。

serverless.yml
resources:
  Resources:
    TodosDynamoDbTable:
      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}
  • TodosDynamoDbTable
    • ここはリソースの論理 ID です。一意であれば任意の値で大丈夫です。
  • DeletionPolicy: Retain
    • リソースの削除ポリシーの設定ができます。Delete(デフォルト), Retain, Snapshot のいずれか 3 つの設定が可能です。スタックが削除されてもテーブルを保持する場合は Retain を採用しますが、今回はスタックが削除されたタイミング(sls remove -v)でテーブルも削除してほしいので、何も記述せずにデフォルトの Delete 設定とします。(公式ドキュメント
  • AttributeDefinitions, KeySchema, ProvisionedThroughput
    • このあたりの設定は DynamoDB そのものに関わる設定のため今回は説明を省略いたします。ここではテーブルには id という項目を設けましたよ、スループット(1 秒あたりの読み込み/書き込み数)は 1 にしますよ、ということぐらいで十分です。(AWS DynamoDB スループットの設定が料金に跳ねるので注意)
  • TableName
    • テーブル名です。今回は provider の environment で DYNAMODB_TABLE${self:service}-${opt:stage, self:provider.stage} = hogehoge-dev と設定していることになります。詳しくは provider の設定を確認してください。

デプロイ

serverless.yml を更新したらデプロイします。

$ sls deploy -v
(省略)

AWS コンソールの確認

テーブルが作成されているはずなので AWS コンソールで DynamoDB のサービスを開いてみます。
Dynamo.png
hogehoge-dev テーブルが作成されていることがわかります。

また、provider.iamRoleStatements で IAM ロールを設定しているので、CWL も確認してみます。
IAM.png
hogehoge-dev テーブルに読み込み/書き込み権限がついていることがわかります。

これで DynamoDB のテーブル作成ができるようになりました。

3. API の公開と DynamoDB との疎通

こちらの環境を構築していきます。

png.png

インターフェース設計

以下の API を作成したいと思います。

やりたいこと path method data(request body parameter) DynamoDB の操作 用意する関数名
全件取得 /todos GET なし Scan list
単一取得 /todos/{id} GET なし GetItem get
追加 /todos POST あり PutItem create
更新 /todos/{id} PUT あり UpdateItem update
削除 /todos/{id} DELETE なし DeleteItem delete

serverless.yml の変更

設計したインターフェースを serverless.yml に反映させます。

pathmethod をこれから実装するであろう JavaScript ファイルとそこに定義する関数(handler)に結びつけるように書くだけです。

serverless.yml
functions:
       - http:
           path: hello
           method: get
+
+  list:
+    handler: todos/list.list
+    events:
+      - http:
+          path: todos
+          method: get
+          cors: true
+
+  get:
+    handler: todos/get.get
+    events:
+      - http:
+          path: todos/{id}
+          method: get
+          cors: true
+
+  create:
+    handler: todos/create.create
+    events:
+      - http:
+          path: todos
+          method: post
+          cors: true
+
+  update:
+    handler: todos/update.update
+    events:
+      - http:
+          path: todos/{id}
+          method: put
+          cors: true
+
+  delete:
+    handler: todos/delete.delete
+    events:
+      - http:
+          path: todos/{id}
+          method: delete
+          cors: true

必要な npm パッケージのインストール

テーブルへのアイテムの追加の処理時にユニークな id を払い出すのに npm の uuid というパッケージを使うので packeage.json を作成して登録します。

$ npm init -y
$ npm install uuid
package.json
{
  (省略)
  "dependencies": {
    "uuid": "^8.3.1"
  }
}

内部処理の実装

詳しい実装は GitHub に公開したのでそちらを参照してください。
公式サンプルの「AWS | REST API With DynamoDB」 と全く同じように実装しました。

デプロイ

デプロイしてみます。

$ sls deploy -v     
(省略)
Serverless: Stack update finished...
Service Information
service: hogehoge
stage: dev
region: us-east-1
stack: hogehoge-dev
resources: 41
api keys:
  None
endpoints:
  GET - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
  GET - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos
  GET - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  POST - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos
  PUT - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  DELETE - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
functions:
  hello: hogehoge-dev-hello
  list: hogehoge-dev-list
  get: hogehoge-dev-get
  create: hogehoge-dev-create
  update: hogehoge-dev-update
  delete: hogehoge-dev-delete
layers:
  None

(省略)

以下のようにインターフェース設計どおりに API が公開されたようです。

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

API の疎通確認

curl コマンドを叩いて API を呼び出してもいいのですが、Postman の方がいろいろわかりやすいので、Postman から確認してみます。

アイテムを POST メソッドを呼んで追加してみます。
2 2.png
うまく行っているみたいですね。

今度は GET で Scan してみます。
9 2.png
ちゃんと取得できました。

AWS コンソールの確認

この結果を AWS の DynamoDB からも確認してみます。
10 2.png
ちゃんと反映されているみたいですね。

これでやりたいことができました。

リソースの削除

リソースを削除するまでが Serverless Framework です。

$ sls remove -v
(省略)

一応、念のため AWS コンソールを見に行って削除されていることは確認した方がいいです。

リソースを削除してもすぐにデプロイできるので、本当に気軽に削除できますね。

まとめ

Serverless Framework を使って、以下のことができるようになりました。

  • API の公開
  • 環境変数の設定
  • IAM ロールの設定
  • DynamoDB のテーブル定義の設定
  • インターフェースの定義の設定
  • API と DynamoDB の疎通

その結果、以下の図のような環境を Serverless Framework で構築することができました。

png.png

【続編はこちら】
【Serverless Framework】カスタムドメイン設定方法(開発環境と本番環境の分離も考慮)

GitHubで編集を提案

Discussion