🎃
APIGatewayからLambdaを非同期起動する
APIGateway+LambdaでLambdaを非同期コールする構成をCDKで構築する。
どういう時に使うのか
APIGateway+Lambdaで同期レスポンスする場合、タイムアウト設定は最大29秒なので、それより時間のかかる処理であれば非同期化を検討する必要がある。
また、Slackボットでは3秒、LINEボットでは1秒以内でレスポンスしなければタイムアウトエラー扱いになるため、非同期化を考えることが多い。
環境
- node 18.14.2
- aws-cdk-lib 2.70.0
- constructs 10.1.289
- typescript 5.0.2
コード
CDKコード:
import { Construct } from "constructs";
import * as cdk from "aws-cdk-lib";
export class ApigatewayStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// APIGW Lambda関数
const apiFn = new cdk.aws_lambda_nodejs.NodejsFunction(this, "apiFn", {
runtime: cdk.aws_lambda.Runtime.NODEJS_18_X,
entry: "src/lambda/async-api-handler.ts",
bundling: {
sourceMap: true,
},
timeout: cdk.Duration.seconds(29),
});
// APIGW
const api = new cdk.aws_apigateway.RestApi(this, "api", {
deployOptions: {
stageName: "api",
loggingLevel: cdk.aws_apigateway.MethodLoggingLevel.INFO,
},
});
api.root.addMethod(
"POST",
new cdk.aws_apigateway.LambdaIntegration(apiFn, {
proxy: false,
requestParameters: {
"integration.request.header.X-Amz-Invocation-Type": "'Event'",
},
passthroughBehavior:
cdk.aws_apigateway.PassthroughBehavior.WHEN_NO_TEMPLATES,
requestTemplates: {
// AWSマネコンのAPIGW>統合リクエスト>マッピングテンプレート>application/json>テンプレートの生成>メソッドリクエストのパススルーをベースに記述
"application/json": `
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"bodyJson" : $input.json('$'),
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
`.trim(),
},
integrationResponses: [
{
statusCode: "202",
},
],
}),
{
methodResponses: [
{
statusCode: "202",
},
],
}
);
}
}
Lambdaコード:
import { APIGatewayEvent, Callback, Context } from "aws-lambda";
export const handler = async (
event: APIGatewayEvent,
_context: Context,
_callback: Callback
): Promise<void> => {
console.log(event);
};
実行する
デプロイ:
npx cdk deploy
リクエストコマンドを実行:
curl -X POST -H "Content-Type: application/json" https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/ -d '{"key":"value"}'
CloudWatch Logsへの出力結果:
INFO {
bodyJson: { key: 'value' },
params: {
path: {},
querystring: {},
header: {
Accept: '*/*',
'CloudFront-Forwarded-Proto': 'https',
'CloudFront-Is-Desktop-Viewer': 'true',
'CloudFront-Is-Mobile-Viewer': 'false',
'CloudFront-Is-SmartTV-Viewer': 'false',
'CloudFront-Is-Tablet-Viewer': 'false',
'CloudFront-Viewer-ASN': 'xxxxx',
'CloudFront-Viewer-Country': 'JP',
'content-type': 'application/json',
Host: 'xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com',
'User-Agent': 'curl/x.xx.x',
Via: '2.0 xxxxxxxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)',
'X-Amz-Cf-Id': 'xxxxxxxxxxxxxxxxxxxxxxx',
'X-Amzn-Trace-Id': 'Root=x-xxxxxxxxxx-xxxxxxxxxxx',
'X-Forwarded-For': '***.***.***.***, ***.***.***.***',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https'
}
},
'stage-variables': {},
context: {
'account-id': '',
'api-id': 'xxxxxxxxxx',
'api-key': '',
'authorizer-principal-id': '',
caller: '',
'cognito-authentication-provider': '',
'cognito-authentication-type': '',
'cognito-identity-id': '',
'cognito-identity-pool-id': '',
'http-method': 'POST',
stage: 'api',
'source-ip': '***.***.***.***',
user: '',
'user-agent': 'curl/x.xx.x',
'user-arn': '',
'request-id': 'xxxxxxx-xxxxxx-xxxxx-xxxxxx',
'resource-id': 'xxxxxxxxxx',
'resource-path': '/'
}
}
参考
- API Gateway + Lambdaを非同期で呼び出す by CDK
- Amazon API GatewayのREST APIからLambdaを非同期に呼んでみた | DevelopersIO
- バックエンド Lambda 関数の非同期呼び出しをセットアップする - Amazon API Gateway
- Amazon API GatewayのREST APIからLambdaを非同期に呼んでみた | DevelopersIO
- API Gateway を介して Lambda 関数にカスタムヘッダーを渡す | AWS re:Post
- [AWS]API Gatewayの本文マッピングテンプレートを理解する | DevelopersIO
- API Gateway のマッピングテンプレートとは
- AWS CDKでAPI作成 - Qiita
- API Gateway mapping template and access logging variable reference - Amazon API Gateway
- json - Raw body payload in AWS API Gateway Body Mapping Template - Stack Overflow
Discussion