0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NestJS プロジェクトを作成してCRUD APIを作成しserverlessを用いてlambdaにデプロイする

0
Last updated at Posted at 2026-03-11

概要

NestJSにてプロジェクト新規作成からserverlessでのデプロイまでを簡単にまとめる。

前提

下記の作業が完了していること。

AWSのアカウントを持っており、AWS CLIが導入されており、IAMアクセスキーがCLIに設定されていること。

方法

  1. 前提で記載したNestJSのプロジェクトルート(nest-testing)の1階層上で、下記を実行してserverlessのプロジェクト作成

    serverless create --template aws-nodejs-typescript --path nest-testing-serverless
    
  2. serverlessのプロジェクトルートに移動

    cd nest-testing-serverless
    
  3. 下記を実行してパッケージインストール

    npm install
    
  4. serverless.tsを下記の様に修正

    • 実施内容
      • service 名を task-api-serverless に設定
      • runtimenodejs20.x に変更
      • regionap-northeast-1 に追加
      • iam にDynamoDB全操作権限を追加
      • pluginsserverless-offline を追加
      • functions をhello削除してNestJS用の api に変更(/{proxy+} で全リクエストをNestJSに流す)
      • resources にDynamoDBテーブル定義を追加
      • custom.esbuildplugins: './esbuild.plugins.js' を追加
      • custom.esbuildexternal にNestJS関連モジュールを追加
      • custom.esbuildtargetnode20 に変更
    serverless.ts
    import type { AWS } from '@serverless/typescript';
    
    const serverlessConfiguration: AWS = {
      service: 'task-api-serverless',
      frameworkVersion: '3',
      plugins: ['serverless-esbuild', 'serverless-offline'],
      provider: {
        name: 'aws',
        runtime: 'nodejs20.x',
        region: 'ap-northeast-1',
        iam: {
          role: {
            statements: [
              {
                Effect: 'Allow',
                Action: ['dynamodb:*'],
                Resource: '*',
              },
            ],
          }
        },
        apiGateway: {
          minimumCompressionSize: 1024,
          shouldStartNameWithService: true,
        },
        environment: {
          AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
          NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000',
        },
      },
      // import the function via paths
      functions: {
        api: {
          handler: 'src/handler.handler',
          events: [
            {
              http: {
                method: 'any',
                path: '/{proxy+}'
              }
            }
          ]
        }
      },
      package: { individually: true },
      resources: {
        Resources: {
          UsersTable: {
            Type: 'AWS::DynamoDB::Table',
            Properties: {
              TableName: 'Users',
              AttributeDefinitions: [
                { AttributeName: 'id', AttributeType: 'S' },
              ],
              KeySchema: [
                { AttributeName: 'id', KeyType: 'HASH' },
              ],
              BillingMode: 'PAY_PER_REQUEST',
            },
          },
        },
      },
      custom: {
        esbuild: {
          bundle: true,
          plugins: './esbuild.plugins.js',
          minify: false,
          sourcemap: true,
          exclude: ['aws-sdk'],
          external: [
            '@nestjs/websockets/socket-module',
            '@nestjs/microservices/microservices-module',
            '@nestjs/microservices',
            'class-transformer',
            'class-validator',
          ],
          target: 'node20',
          define: { 'require.resolve': undefined },
          platform: 'node',
          concurrency: 10,
        },
      },
    };
    
    module.exports = serverlessConfiguration;
    
  5. tsconfig.jsonを下記のように修正

    • 実施内容
      • experimentalDecorators: true を追加(デコレータを有効化)
      • emitDecoratorMetadata: true を追加(DIに必要なメタデータを埋め込む)
    tsconfig.json
    {
      "extends": "./tsconfig.paths.json",
      "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "lib": ["ESNext"],
        "moduleResolution": "node",
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "removeComments": true,
        "sourceMap": true,
        "target": "ES2020",
        "outDir": "lib"
      },
      "include": ["src/**/*.ts", "serverless.ts"],
      "exclude": [
        "node_modules/**/*",
        ".serverless/**/*",
        ".webpack/**/*",
        "_warmup/**/*",
        ".vscode/**/*"
      ],
      "ts-node": {
        "require": ["tsconfig-paths/register"]
      }
    }
    
  6. 下記を実行して必要なパッケージをインストール

    # NestJS本体
    npm install @nestjs/core @nestjs/common @nestjs/platform-express reflect-metadata rxjs
    
    # Lambda対応
    npm install @codegenie/serverless-express
    
    # DynamoDB
    npm install zod @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
    
    # 型定義
    npm install --save-dev @types/aws-lambda
    
    # serverless-offline(v3対応版)
    npm install --save-dev serverless-offline@13
    
    # esbuildデコレータ対応
    npm install --save-dev @anatine/esbuild-decorators
    
    # ULIDを使うためのパッケージ
    npm install ulidx
    
  7. プロジェクトルートにesbuild.plugins.jsを作成し、下記のように記載

    esbuild.plugins.js
    const { esbuildDecorators } = require('@anatine/esbuild-decorators');
    
    module.exports = [esbuildDecorators()];
    
  8. src/handler.tsを作成し、下記の様に記載(lambdaのエントリーポイント)

    src/handler.ts
    import 'reflect-metadata';
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import serverlessExpress from '@codegenie/serverless-express';
    import { Handler, Context, Callback } from 'aws-lambda';
    
    let server: Handler;
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      await app.init();
      const expressApp = app.getHttpAdapter().getInstance();
      return serverlessExpress({ app: expressApp });
    }
    
    export const handler: Handler = async (event, context: Context, callback: Callback) => {
      server = server ?? (await bootstrap());
      return server(event, context, callback);
    };
    
  9. serverless側リポジトリルートで下記を実行して「nest-testing」のプロジェクトから必要なファイル郡をコピーで持って来る。

    cp -r ../nest-testing/src/users ./src/
    cp -r ../nest-testing/src/pipes ./src/
    cp ../nest-testing/src/dynamodb.client.ts ./src/
    cp ../nest-testing/src/app.module.ts ./src/
    cp ../nest-testing/src/app.controller.ts ./src/
    cp ../nest-testing/src/app.controller.spec.ts ./src/
    cp ../nest-testing/src/app.service.ts ./src/
    cp ../nest-testing/src/create-table.ts ./src/
    
  10. src/dynamodb.client.tsを下記の様に書き換えてDB接続先をDockerコンテナを指定

    src/dynamodb.client.ts
    import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
    import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
    
    const client = new DynamoDBClient({
      region: 'ap-northeast-1',
      endpoint: 'http://localhost:8000',
      credentials: {
        accessKeyId: 'dummy',
        secretAccessKey: 'dummy',
      },
    });
    
    export const dynamoDb = DynamoDBDocumentClient.from(client);
    
  11. プロジェクトルートにdocker-compose.ymlを作成して下記の様に記載(AWSが公式に提供しているDockerイメージを利用しているのでDockerfileは不要)

    docker-compose.yml
    services:
      dynamodb-local:
        image: amazon/dynamodb-local
        ports:
          - "8000:8000"
        volumes:
          - dynamodb-data:/home/dynamodblocal
        command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal"
    
    volumes:
      dynamodb-data:
    
  12. 下記を実行してコンテナを起動

    docker-compose up -d
    
  13. 下記を実行してローカルでserverlessを起動(Dockerなどが動いている場合停止してから下記を実行)

    npx serverless offline
    
  14. 下記を実行してテーブルを作成(「テーブル作成成功」と表示されたら完了)

    npx ts-node src/create-table.ts
    
  15. 下記を実行してローカル環境での動作確認を実施

    # ユーザー作成
    curl -X POST http://localhost:3000/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎", "email": "persist_taro@example.com"}'
    
    # 一覧取得
    curl http://localhost:3000/dev/users
    
    # 1件取得(作成で返ってきたidを使う)
    curl http://localhost:3000/dev/users/{id}
    
    # 更新
    curl -X PATCH http://localhost:3000/dev/users/{id} \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化次郎"}'
    
    # 一覧取得(更新された内容が返ることを確認)
    curl http://localhost:3000/dev/users
    
    # 削除
    curl -X DELETE http://localhost:3000/dev/users/{id}
    
    # 一覧取得(何も返らないことを確認)
    curl http://localhost:3000/dev/users
    
    # 値チェックエラー確認 nameが空文字(min(1)違反)
    curl -X POST http://localhost:3000/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "", "email": "persist_taro@example.com"}'
    
    # 値チェックエラー確認 emailの形式が不正
    curl -X POST http://localhost:3000/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎", "email": "これはメールじゃない"}'
    
    # 値チェックエラー確認 必須フィールドが欠けている
    curl -X POST http://localhost:3000/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎"}'
    
  16. src/dynamodb.client.tsを下記の様に書き換えてDB接続先をデプロイ後のものを指定

    src/dynamodb.client.ts
    import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
    import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
    
    const client = new DynamoDBClient({
      region: 'ap-northeast-1',
    });
    
    export const dynamoDb = DynamoDBDocumentClient.from(client);
    
  17. 下記を実行してAWSにデプロイ(完了まで数分かかる。ローカル環境は止めても起動したままでも問題ない。)

    npx serverless deploy
    
  18. 実行結果の「endpoint」の/dev/までをコピーして下記を置き換えて実行

    # ユーザー作成
    curl -X POST https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎", "email": "persist_taro@example.com"}'
    
    # 一覧取得
    curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users
    
    # 1件取得(作成で返ってきたidを使う)
    curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users/{id}
    
    # 更新
    curl -X PATCH https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users/{id} \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化次郎"}'
    
    # 一覧取得(更新された内容が返ることを確認)
    curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users
    
    # 削除
    curl -X DELETE https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users/{id}
    
    # 一覧取得(何も返らないことを確認)
    curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users
    
    # 値チェックエラー確認 nameが空文字(min(1)違反)
    curl -X POST https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "", "email": "persist_taro@example.com"}'
    
    # 値チェックエラー確認 emailの形式が不正
    curl -X POST https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎", "email": "これはメールじゃない"}'
    
    # 値チェックエラー確認 必須フィールドが欠けている
    curl -X POST https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/users \
      -H "Content-Type: application/json" \
      -d '{"name": "永続化太郎"}'
    
  19. 下記を実行してデプロイしたリソース郡を削除

    npx serverless remove
    

本作業実施後のコードはこちら

付録

ローカルターミナルからログを見るコマンド

npx serverless logs -f api --stage dev

デプロイ後にスキーマを変更するために一旦テーブルを削除するコマンド

aws dynamodb delete-table --table-name テーブル名 --region ap-northeast-1

デプロイしたものをすべて消すコマンド

npx serverless remove

デプロイするコマンド

npx serverless deploy
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?