kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Pulumi で API Gateway と Lambda のデプロイに入門できる「Lambda-backed REST API」

Pulumi で Amazon API Gateway と AWS Lambda 関数のデプロイに入門できる How-to Guides「Lambda-backed REST API」を試してみた❗️最近 Pulumi の導入検証をしてて色々と試しているところ.

www.pulumi.com

アーキテクチャ図

以下のようなアーキテクチャをデプロイする.

手順: Prerequisites

まずは pulumi/examples リポジトリを clone しておく.

github.com

あとは How-to Guides の手順通りに進めれば OK👌

$ cd aws-ts-apigateway-lambda-serverless
$ pulumi stack init aws-ts-apigateway-lambda-serverless
$ pulumi config set aws:region us-east-2
$ yarn install

手順: Deploy the App

準備ができたら pulumi up コマンドを実行する.ちなみにプレビュー結果は少し違っていた.手順には + 20 created と載っているけど,実際に実行したら + 41 to create になっていた.

$ pulumi up
Previewing update (aws-ts-apigateway-lambda-serverless)

(中略)

     Type                             Name                                                                     Plan       Info
 +   pulumi:pulumi:Stack              aws-ts-apigateway-lambda-serverless-aws-ts-apigateway-lambda-serverless  create     3 messages
 +   ├─ aws:iam:Role                  delete-handler                                                           create
 +   ├─ aws:iam:Role                  get-handler                                                              create
 +   ├─ aws:iam:Role                  post-handler                                                             create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-d32a66fa                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-1b4caae3                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-019020e7                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-a1de8170                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-b5aeb6b6                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-7cd09230                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-e1a3786d                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-4aaabb8e                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-e1a3786d                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-a1de8170                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-4aaabb8e                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-7cd09230                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-b5aeb6b6                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-d32a66fa                                                    create
 +   ├─ aws:lambda:Function           delete-handler                                                           create
 +   ├─ aws:lambda:Function           get-handler                                                              create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-019020e7                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-1b4caae3                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-7cd09230                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-019020e7                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-a1de8170                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-e1a3786d                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-74d12784                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-d32a66fa                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-74d12784                                                    create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-1b4caae3                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  get-handler-b5aeb6b6                                                     create
 +   ├─ aws:iam:RolePolicyAttachment  delete-handler-74d12784                                                  create
 +   ├─ aws:iam:RolePolicyAttachment  post-handler-4aaabb8e                                                    create
 +   ├─ aws:lambda:Function           post-handler                                                             create
 +   └─ aws-apigateway:index:RestAPI  hello-world                                                              create
 +      ├─ aws:apigateway:RestApi     hello-world                                                              create
 +      ├─ aws:apigateway:Deployment  hello-world                                                              create     1 warning
 +      ├─ aws:apigateway:Stage       hello-world                                                              create
 +      ├─ aws:lambda:Permission      hello-world-d21e9c98                                                     create
 +      ├─ aws:lambda:Permission      hello-world-29d762f7                                                     create
 +      └─ aws:lambda:Permission      hello-world-86405973                                                     create

(中略)

Outputs:
    endpointUrl: [unknown]

Resources:
    + 41 to create

デプロイ確認

動作確認

$ curl -s -X GET https://dtnnju41df.execute-api.us-east-2.amazonaws.com/stage/example | jq .
{
  "route": "example",
  "affirmation": "Nice job, you've done it! :D",
  "requestBodyEcho": null
}

$ curl -s -X POST https://dtnnju41df.execute-api.us-east-2.amazonaws.com/stage/example | jq .
{
  "message": "POST successful"
}

$ curl -s -X DELETE https://dtnnju41df.execute-api.us-east-2.amazonaws.com/stage/example | jq .
{
  "message": "DELETE successful"
}

手順: Clean Up

最後は削除しておく🗑️

$ pulumi destroy
$ pulumi stack rm

aws.lambda.Function と aws.lambda.CallbackFunction

How-to Guides の手順にはコード解説は入ってなく追加で少し調べていたら,Pulumi には aws.lambda.Functionaws.lambda.CallbackFunction があって,今回は aws.lambda.CallbackFunction が使われていた.

www.pulumi.com www.pulumi.com

aws.lambda.CallbackFunction を使うと AWS Lambda 関数のコードを Pulumi コードとしてインラインで実装できる.そして Pulumi コンパイラによって自動的にデプロイできる仕組みになっていて,現状では TypeScript 限定でサポートされている💡

ドキュメントには以下のサンプルコードが載っていた.callback というパラメタにコールバック関数をそのまま実装できる.

import * as aws from "@pulumi/aws";

// Create an AWS Lambda function that fetches the Pulumi website and returns the HTTP status
const lambda = new aws.lambda.CallbackFunction("fetcher", {
    callback: async(event) => {
        try {
            const res = await fetch("https://www.pulumi.com/robots.txt");
            console.info("status", res.status);
            return res.status;
        }
        catch (e) {
            console.error(e);
            return 500;
        }
    },
});

そして今回の How-to Guides「Lambda-backed REST API」だとコールバック関数を直接実装するパターン(post-handlerdelete-handler)と別の handler.ts を呼び出すパターン(get-handler)の2種類があった.

// Create Lambda functions for our API
const handlerFunction = new aws.lambda.CallbackFunction("get-handler", {
  callback: handler,
  runtime: aws.lambda.Runtime.NodeJS18dX,
});

const postHandlerFunction = new aws.lambda.CallbackFunction("post-handler", {
  callback: async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    console.log("Inline event handler");
    console.log(event);
    return {
      statusCode: 200,
      body: JSON.stringify({ message: "POST successful" }),
    };
  },
  runtime: aws.lambda.Runtime.NodeJS18dX,
});

const deleteHandlerFunction = new aws.lambda.CallbackFunction("delete-handler", {
  callback: async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    console.log(event);
    return {
      statusCode: 200,
      body: JSON.stringify({ message: "DELETE successful" }),
    };
  },
  runtime: aws.lambda.Runtime.NodeJS18dX,
});

ちなみに aws.lambda.CallbackFunction でデプロイされた AWS Lambda 関数 get-handler は以下のようになっていた.

この callback というパラメタに関しては「Function serialization」というドキュメントに詳しく載っている📝

www.pulumi.com

Node.js 22 にアップデート

今回試した How-to Guides「Lambda-backed REST API」は2025年9月1日に非推奨になった Node.js 18 ランタイムを使っていて今後使えなくなる可能性がある.

docs.aws.amazon.com

よって最新の Node.js 22 ランタイムに変更するプルリクエストを出しておいた🎉 merge してもらえるとイイな〜

github.com