LoginSignup
0
2

More than 3 years have passed since last update.

API Gateway ↔ Lambda ↔ Dynamo

Last updated at Posted at 2020-12-16

はじめに

前回のものに API Gateway を繋いでみます。こんな感じがゴール

image.png

API Gateway

基本

GUI がかなり直感的でなので頭使わずに使える感じです

  1. エンドポイントのリソース /project 作って
  2. リソースにメソッド GET PUT POST DELETE 作って
  3. メソッド毎にLambda のコード project にマッピングするだけ

image.png

忘れがちなのが、APIの変更は「デプロイ」を押してデプロイしないといけないこと。これでとりあえず動くはず

image.png

APIキー

APIキーを使うときはちょっと面倒

  1. リソースの各メソッド毎にAPIキーでの認証を必須にする
  2. 「使用料プラン」を作成する(ここでAPIに対するレートリミットなどを設定)
  3. 「ステージ」を作成する(開発用とか商用とかそういう意味のステージのことで、ここでは dev_1 とした)
  4. 「APIキー」「使用料プラン」「ステージ」を関連づける

image.png

変更の反映に数分かかることもあるようですが、これでAPIキーを付けてコールできるはず

curl -s -X GET -H "x-api-key: your-api-key-here" https://<project-id>.execute-api.us-east-2.amazonaws.com/dev_1/projects | jq

Lambda 統合プロキシ

  • Lambda 統合プロキシ にチェックを入れる
  • メソッド(GET/PUT/POST/DELETE)をラムダ関数に紐づける1

することで、HTTPリクエストに含まれる様々なパラメターがそのまま Lambda に渡される

image.png

例えば、HTTPメソッドが POST なのか GET なのかが引き渡されるので Lambda 側でわかる.

こんな風に読んだ時に

curl -s -X GET -H "x-api-key:your-api-key-here" https://<api-id>.execute-api.us-east-2.amazonaws.com/dev_1/projects?say=ciao -d '{"say": "hello"}'

Lambda に event として渡されるパラメータはこんな感じ

event
{
  "resource": "/projects",
  "path": "/projects",
  "httpMethod": "GET",
  "headers": {
    "accept": "*/*",
    "content-type": "application/x-www-form-urlencoded",
    "Host": "<project-id>.execute-api.us-east-2.amazonaws.com",
    "User-Agent": "curl/7.64.1",
    "X-Amzn-Trace-Id": "Root=1-5fd44390-5f78ee5a289e7e3a0355bec2",
    "x-api-key": "your-api-key",
    "X-Forwarded-For": "***.**.**.**",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "multiValueHeaders": {
    "accept": [
      "*/*"
    ],
    "content-type": [
      "application/x-www-form-urlencoded"
    ],
    "Host": [
      "<project-id>.execute-api.us-east-2.amazonaws.com"
    ],
    "User-Agent": [
      "curl/7.64.1"
    ],
    "X-Amzn-Trace-Id": [
      "Root=1-5fd44390-5f78ee5a289e7e3a0355bec2"
    ],
    "x-api-key": [
      "<your-api-key>"
    ],
    "X-Forwarded-For": [
      "***.**.**.**"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ]
  },
  "queryStringParameters": {
    "say": "ciao"
  },
  "multiValueQueryStringParameters": {
    "say": [
      "ciao"
    ]
  },
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "<resource-id>",
    "resourcePath": "/projects",
    "httpMethod": "GET",
    "extendedRequestId": "Xa9-iG9kCYcFU8Q=",
    "requestTime": "12/Dec/2020:04:14:08 +0000",
    "path": "/dev_1/projects",
    "accountId": "<account-id>",
    "protocol": "HTTP/1.1",
    "stage": "dev_1",
    "domainPrefix": "<project-id>",
    "requestTimeEpoch": 1607746448145,
    "requestId": "06499aaf-9168-49d7-b4da-2a0a3ad5d4ad",
    "identity": {
      "cognitoIdentityPoolId": null,
      "cognitoIdentityId": null,
      "apiKey": "<your-api-key>",
      "principalOrgId": null,
      "cognitoAuthenticationType": null,
      "userArn": null,
      "apiKeyId": "<api-key-id>",
      "userAgent": "curl/7.64.1",
      "accountId": null,
      "caller": null,
      "sourceIp": "***.**.**.**",
      "accessKey": null,
      "cognitoAuthenticationProvider": null,
      "user": null
    }, 
    "domainName": "<project-id>.execute-api.us-east-2.amazonaws.com",
    "apiId": "<api-id>"
  },
  "body": "{\"say\": \"hello\"}",
  "isBase64Encoded": false
}

クライアントからのデータ bodyevent['body'] でアクセスできるので Lambda 側の CRUD するコードを前回から少し変えて、こんな感じにしときます


require 'json'
require 'aws-sdk-dynamodb'

def add_project(table, body)
  table.put_item({ item: body })  
end

def delete_project(table, body)
  params = { table_name: table, key: { 'project-id': body['project-id'] } }
  begin
    table.delete_item(params)
  rescue Aws::DynamoDB::Errors::ServiceError => error
    puts error.message
  end
end

def update_project(table, body)
  params = {
    table_name: table,
    key: { 'project-id': body['project-id'] },
    attribute_updates: {
      name: {
        value: body['name'],
        action: "PUT"
      }
    }
  }
  table.update_item(params)  
end

def list_project(table)
  scan_output = table.scan({ limit: 50, select: "ALL_ATTRIBUTES" })

  scan_output.items.each do |item|
    keys = item.keys
    keys.each do |k|
      puts "#{k}: #{item[k]}"
    end
  end
end

def lambda_handler(event:, context:)

  http_method = event['httpMethod'] # このへんを追加
  body = JSON.parse(event['body'])  # このへんを追加

  dynamodb = Aws::DynamoDB::Resource.new(region: 'us-east-2')
  table = dynamodb.table('project')

  case http_method
    when 'GET'    then list_project(table)
    when 'PUT'    then update_project(table, body)
    when 'POST'   then add_project(table, body)
    when 'DELETE' then delete_project(table, body)
    else 0
  end

  { statusCode: 200, body: JSON.generate(list_project(table)) }

end

例えばこんな感じでリクエスト投げると、HTTPのメソッドをみて CRUD 処理をする。ちなみに jq しとくと見やすいのでおすすめ

curl -s -X POST -d '{ "project-id":101, "name":"Cycling", "is-default":false }' -H "x-api-key: <your-api-key-here>" https://<project-id>.execute-api.us-east-2.amazonaws.com/dev_1/projects | jq

うむ、動いたぞ

シリーズ


  1. 今回のようなケースでは、実はメソッドとしてANYを使うことでメソッド個別にラムダ関数に紐づける必要はなくなる 

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2