API Gateway + Lambdaを非同期で呼び出す by CDK

3 min read読了の目安(約3000字

API Gateway経由で少し長めの処理を行うLambdaを呼び出したい場合にちょっと困ったのでメモ。

困ったこと

先日LambdaでPDFを生成したりとちょっと重めの処理を実装してみたら見事にタイムアウトしてしまった。

確認してみたらLambda自体はタイムアウト値を伸ばしていたのだが、API Gateway側のタイムアウト値が最大29秒(上限引き上げ不可)なのでそれにひっかかってしまったのが原因。

そういった場合はSQS使うなりすればいいんだけども、同期的なレスポンス不要 & 裏で動いてくれればいいやっていう雑な処理だったのでLambdaを非同期で呼び出すように変えてみた。

最終的なコード

import * as cdk from "@aws-cdk/core"
import { Runtime } from "@aws-cdk/aws-lambda"
import { NodejsFunction } from "@aws-cdk/aws-lambda-nodejs"
import * as apigateway from '@aws-cdk/aws-apigateway';
import { Duration } from "@aws-cdk/core";

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)
    
    const lambda = new NodejsFunction(this, "cdkSampleStack", {
      entry: "lib/lambda/sample.ts",
      handler: "handler",
      runtime: Runtime.NODEJS_12_X,
      timeout: Duration.seconds(180)
    })

    const api = new apigateway.RestApi(this, 'CdkSampleApi', { cloudWatchRole: false })
    const lambdaIntegration = new apigateway.LambdaIntegration(lambda, {
      connectionType: apigateway.ConnectionType.INTERNET,
      proxy: false,
      requestParameters: {
        'integration.request.querystring.hoge_id': 'method.request.querystring.hoge_id',
        'integration.request.header.X-Amz-Invocation-Type': "'Event'"
      },
      integrationResponses: [
        {
          statusCode: '202',
        }
      ]
    })

    api.root.addMethod(
      'GET',
      lambdaIntegration,
      {
        requestParameters: {
          'method.request.querystring.hoge_id': true
        },
        methodResponses: [
          {
            statusCode: '202',
          }
        ],  
    })
  }
}

ざっくり解説

Lambda部分

ここは見ての通り。タイムアウト値180秒に設定してる。

const lambda = new NodejsFunction(this, "cdkSampleStack", {
    entry: "lib/lambda/sample.ts",
    handler: "handler",
    runtime: Runtime.NODEJS_12_X,
    timeout: Duration.seconds(180)
})

API Gateway部分

こっちがメイン。大事なのは下記3点。

  • 「Lambda プロキシ統合の使用」をOFF
  • リクエストヘッダーに X-Amz-Invocation-Type': "'Event'"を追加
  • レスポンスのステータスコードを202にする
const api = new apigateway.RestApi(this, 'CdkSampleApi', { cloudWatchRole: false })
const lambdaIntegration = new apigateway.LambdaIntegration(lambda, {
    connectionType: apigateway.ConnectionType.INTERNET,
    // ここ重要。Webコンソール上では「Lambda プロキシ統合の使用」にあたる項目
    // デフォルトのTrueの場合、非同期で呼び出したレスポンスがLambdaの形式ではないと怒られてエラーになってしまう
    proxy: false,
    requestParameters: {
        // Lambdaを非同期で呼び出すためにヘッダーに追記
        'integration.request.header.X-Amz-Invocation-Type': "'Event'"
    },
    integrationResponses: [
    {
        // Lambdaを非同期で呼び出した場合のステータスコードは202になるので合わせる
        statusCode: '202',
    }
    ]
})

api.root.addMethod(
    'GET',
    lambdaIntegration,
    {
        methodResponses: [
            {
                // こちらもステータスコードを合わせる
                statusCode: '202',
            }
        ],  
    }
)

Webコンソール上の設定のやり方しらべてからCDKのコードに落とし込んだりと微妙にハマったけどとりあえずこれで問題なく動いてる。