メインコンテンツまでスキップ

AWS Lambdaではじめる

このガイドでは、AWS Lambda上でZeltアプリケーションをゼロから構築する手順を説明します。

前提条件​

  • Node.js v20以上(またはBun v1.0以上)
  • パッケージマネージャ: pnpm(推奨)、npm、またはbun

AWS Lambdaでは追加で以下が必要です:

インストール​

pnpm add @zeltjs/core @zeltjs/adapter-lambda
pnpm add -D @types/aws-lambda esbuild

プロジェクト構成​

my-app/
├── src/
│   ├── entry/
│   │   ├── controllers/    # HTTPエンドポイント
│   │   └── commands/       # CLIコマンド
│   ├── services/           # ビジネスロジック
│   ├── configs/            # 設定クラス
│   ├── app.ts              # アプリケーション定義
│   ├── cli.ts              # CLIエントリポイント
│   └── main.ts             # HTTPサーバーエントリポイント
├── package.json
└── tsconfig.json
ディレクトリ用途
entry/外部向けエントリポイント(HTTP、CLI)
services/ビジネスロジック、DIで注入される
configs/環境変数と設定

AWS Lambdaの場合、プロジェクトルートに template.yaml(SAMテンプレート)も追加します。

Hello World​

Step 1: Controllerを作成する​

src/entry/controllers/hello.controller.ts を作成します:

@Controller('/hello')
export class HelloController {
  @Get('/:name')
  greet(req = request()) {
    const name = req.pathParam('name');
    return { message: `Hello, ${name}!` };
  }
}

Step 2: アプリケーションを作成する​

src/app.ts を作成します:

export const app = createApp([
  http({ controllers: [HelloController] }),
  lambdaHandler(),
]);

Step 3: Lambda Handlerを作成する​

src/handler.ts を作成します:

import { onLambda } from '@zeltjs/adapter-lambda';
import { app } from './app';

const lambdaApp = await onLambda(app);

export const handler = lambdaApp.lambdaHandler.create(lambdaApp.http);

onLambda() はLambda用の環境configを注入してruntimeを初期化します。HTTP featureを必須とせず、宣言したfeatureの名前空間をそのまま返します。

lambdaHandler() はデフォルトで lambdaHandler 名前空間を公開します。lambdaHandler.create(対象HTTP) はAPI Gateway v2用の関数を生成し、{ payloadFormat: '1.0' } を渡すとv1用の関数を生成します。生成した関数をAWSへ公開してください。HTTPの名前は任意で、同じfeatureから複数のHTTP用のhandlerを作れます。

lambdaHandler({ name: 'gateway' }) とすれば、lambdaApp.gateway.create(lambdaApp.http) で使えます。通常のruntime APIである shutdown() と get(Class) も利用できます。

Step 4: SAMテンプレートを設定する​

template.yaml を作成します:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 30
    Runtime: nodejs20.x

Resources:
  HelloFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: dist/
      Handler: handler.handler
      Events:
        Api:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2022
        EntryPoints:
          - src/handler.ts

Outputs:
  ApiEndpoint:
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com"

Step 5: デプロイする​

sam build
sam deploy --guided

API Gatewayのバージョン​

このadapterは両方のAPI Gatewayバージョンをサポートします:

export const handler = lambdaApp.lambdaHandler.create(lambdaApp.http);

REST API (v1)​

export const handler = lambdaApp.lambdaHandler.create(lambdaApp.http, { payloadFormat: '1.0' });

Warmupオプション​

デフォルトでは、onLambda() はコールドスタート時間を最小化するために遅延初期化(warmup: false)を使用します。controllerは最初のリクエストで解決されます。

即時初期化にするには:

const lambdaApp = await onLambda(app, { warmup: true });
オプション挙動用途
warmup: false(デフォルト)controllerは最初のリクエストで解決されるコールドスタートの最適化
warmup: true全controllerが初期化時に解決されるプロビジョンド同時実行

バイナリレスポンス​

このadapterは、バイナリレスポンス(画像、音声、動画、octet-stream)をbase64エンコードして自動的に処理します。

@Controller('/files')
export class FileController {
  @Get('/image')
  getImage() {
    const imageBuffer = new Uint8Array([/* ... */]);
    return response()
      .header('Content-Type', 'image/png')
      .body(imageBuffer);
  }
}

次のステップ​