はじめに
前回のものに API Gateway を繋いでみます。こんな感じがゴール
API Gateway
基本
GUI がかなり直感的でなので頭使わずに使える感じです
- エンドポイントのリソース
/project
作って - リソースにメソッド
GET
PUT
POST
DELETE
作って - メソッド毎にLambda のコード
project
にマッピングするだけ
忘れがちなのが、APIの変更は「デプロイ」を押してデプロイしないといけないこと。これでとりあえず動くはず
APIキー
APIキーを使うときはちょっと面倒
- リソースの各メソッド毎にAPIキーでの認証を必須にする
- 「使用料プラン」を作成する(ここでAPIに対するレートリミットなどを設定)
- 「ステージ」を作成する(開発用とか商用とかそういう意味のステージのことで、ここでは
dev_1
とした) - 「APIキー」「使用料プラン」「ステージ」を関連づける
変更の反映に数分かかることもあるようですが、これで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 に渡される
例えば、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
}
クライアントからのデータ body
は event['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
うむ、動いたぞ
シリーズ
- Lambda ↔ DynamoDB
- API Gateway ↔ Lambda ↔ DynamoDB
- Cognito ↔ API Gateway ↔ Lambda ↔ Dynamo
- Amplify [Hosting] ↔ API Gateway [REST] ↔ Lambda
- API GatewayでCORSを有効にするためにやったこと
- AmplifyのJavascript SDKでCognito認証からデータ取得まで
-
今回のようなケースでは、実はメソッドとして
ANY
を使うことでメソッド個別にラムダ関数に紐づける必要はなくなる ↩