How to integrate AWS lamda serverless with Node.js Nest.js

Create Serverless yaml: service: backend-api frameworkVersion: '^3.0.0' useDotenv: true configValidationMode: error provider: name: aws region: us-east-2 runtime: nodejs20.x stage: ${opt:stage, 'dev'} deploymentMethod: direct logRetentionInDays: 7 vpc: securityGroupIds: - sg-13f39e24a9d - sg-87037c0f81 subnetIds: - subnet-d40bc57 - subnet-45gf564g3454 ecr: scanOnPush: true images: backend-api-image: path: . file: Dockerfile environment: NODE_ENV: ${env:NODE_ENV} DATABASE_URL: ${env:DATABASE_URL} JWT_TOKEN_SECRET: ${env:JWT_TOKEN_SECRET} API_BACKEND_AWS_REGION: ${env:STUDY_BUDS_AWS_REGION} API_BACKEND_AWS_ACCESS_KEY: ${env:STUDY_BUDS_AWS_ACCESS_KEY} API_BACKEND_AWS_SECRETE_KEY: ${env:STUDY_BUDS_AWS_SECRETE_KEY} AWS_BUCKET_NAME: ${env:AWS_BUCKET_NAME} AWS_CLOUD_FRONT_URL: ${env:AWS_CLOUD_FRONT_URL} AWS_SMTP_HOST: ${env:AWS_SMTP_HOST} AWS_SMTP_USER: ${env:AWS_SMTP_USER} AWS_SMTP_PASS: ${env:AWS_SMTP_PASS} AWS_FROM_EMAIL: ${env:AWS_FROM_EMAIL} AI_BACKEND_URL: ${env:AI_BACKEND_URL} CORS_ALLOWED_HOSTS: ${env:CORS_ALLOWED_HOSTS} FRONTEND_BASE_URL: ${env:FRONTEND_BASE_URL} MAIL_HOST: ${env:MAIL_HOST} MAIL_USER: ${ env:MAIL_USER } MAIL_PASSWORD: ${env:MAIL_PASSWORD} MAIL_FROM_NAME: ${env:MAIL_FROM_NAME} MAIL_FROM_ADDRESS: ${env:MAIL_FROM_ADDRESS} apiGateway: binaryMediaTypes: - '*/*' custom: prune: automatic: true number: 2 functions: main: image: name: backend-api-image command: - dist/src/serverless.handler entryPoint: - '/lambda-entrypoint.sh' memorySize: 1024 timeout: 330 url: true provisionedConcurrency: 0 events: - http: method: ANY path: / - http: method: ANY path: '{proxy+}' Install below two packages: npm i @codegenie/serverless-express npm i -D @types/aws-lambda Create serverless.ts import { NestFactory, Reflector } from '@nestjs/core'; import { AppModule } from './app.module'; import serverlessExpress from '@codegenie/serverless-express'; import { Callback, Context, Handler } from 'aws-lambda'; import { RequestMethod, ValidationPipe } from '@nestjs/common'; import helmet from 'helmet'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { ResponseTransformInterceptor } from './common/interceptor/global-response-interceptor'; import { RolePermissionsSeederService } from './modules/v1/user/seed/role-permissions.seeder.service'; import { SeedService } from './modules/v1/character/seeder/seeder.service'; let server: Handler; async function bootstrap() { const app = await NestFactory.create(AppModule); const allowedHosts = (process.env.CORS_ALLOWED_HOSTS as string) || '*'; app.enableCors({ origin: allowedHosts.split(','), methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', credentials: true, }); app.setGlobalPrefix('api/v1', { exclude: [{ path: '/', method: RequestMethod.GET }], }); app.use(helmet()); app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true, }), ); const reflector = app.get(Reflector); app.useGlobalInterceptors(new ResponseTransformInterceptor(reflector)); //const rolePermissionsService = app.get(RolePermissionsSeederService); //await Promise.all([rolePermissionsService.insertRolePermissions()]); const rolePermissionsService = app.get(RolePermissionsSeederService); const seedsService = app.get(SeedService); const config = new DocumentBuilder() .setTitle('Backend api') .setDescription('Swagger docs for backend apis') .setVersion('1.0') .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('/api-docs', app, document); await app.init(); const expressApp = app.getHttpAdapter().getInstance(); return serverlessExpress({ app: expressApp, }); } export const handler: Handler = async ( event: any, context: Context, callback: Callback, ) => { try { server = server ?? (await bootstrap()); return server(event, context, callback); } catch (error) { console.error('Error during Lambda execution:', error); return { statusCode: 500, body: JSON.stringify({ error: 'Internal Server Error', message: error.message, }), }; } }; Create Dockerfile: # Use the AWS Lambda base image for Node.js FROM public.ecr.aws/lambda/nodejs:22 # Set the working directory in the container WORKDIR ${LAMBDA_TASK_ROOT} # Copy only package files first for dependency installation COPY package*.json ${LAMBDA_TASK_ROOT}/ # Install dependencies (both production and development for the build phase) RUN npm install # Copy the rest of the application code (excluding files in .dockerignore) COPY . ${LAMBDA_TASK_ROOT}/ # Build the NestJS applicati

Jan 23, 2025 - 15:02
 0
How to integrate AWS lamda serverless with Node.js Nest.js

Create Serverless yaml:

service: backend-api

frameworkVersion: '^3.0.0'

useDotenv: true
configValidationMode: error

provider:
  name: aws
  region: us-east-2
  runtime: nodejs20.x
  stage: ${opt:stage, 'dev'}
  deploymentMethod: direct
  logRetentionInDays: 7

  vpc:
    securityGroupIds:
      - sg-13f39e24a9d
      - sg-87037c0f81
    subnetIds:
      - subnet-d40bc57
      - subnet-45gf564g3454

  ecr:
    scanOnPush: true
    images:
      backend-api-image:
        path: .
        file: Dockerfile

  environment:
    NODE_ENV: ${env:NODE_ENV}
    DATABASE_URL: ${env:DATABASE_URL}
    JWT_TOKEN_SECRET: ${env:JWT_TOKEN_SECRET}
    API_BACKEND_AWS_REGION: ${env:STUDY_BUDS_AWS_REGION}
    API_BACKEND_AWS_ACCESS_KEY: ${env:STUDY_BUDS_AWS_ACCESS_KEY}
    API_BACKEND_AWS_SECRETE_KEY: ${env:STUDY_BUDS_AWS_SECRETE_KEY}
    AWS_BUCKET_NAME: ${env:AWS_BUCKET_NAME}
    AWS_CLOUD_FRONT_URL: ${env:AWS_CLOUD_FRONT_URL}
    AWS_SMTP_HOST: ${env:AWS_SMTP_HOST}
    AWS_SMTP_USER: ${env:AWS_SMTP_USER}
    AWS_SMTP_PASS: ${env:AWS_SMTP_PASS}
    AWS_FROM_EMAIL: ${env:AWS_FROM_EMAIL}
    AI_BACKEND_URL: ${env:AI_BACKEND_URL}
    CORS_ALLOWED_HOSTS: ${env:CORS_ALLOWED_HOSTS}
    FRONTEND_BASE_URL: ${env:FRONTEND_BASE_URL}
    MAIL_HOST: ${env:MAIL_HOST}
    MAIL_USER: ${ env:MAIL_USER }
    MAIL_PASSWORD: ${env:MAIL_PASSWORD}
    MAIL_FROM_NAME: ${env:MAIL_FROM_NAME}
    MAIL_FROM_ADDRESS: ${env:MAIL_FROM_ADDRESS}

  apiGateway:
    binaryMediaTypes:
      - '*/*'
custom:
  prune:
    automatic: true
    number: 2

functions:
  main:
    image:
      name: backend-api-image
      command:
        - dist/src/serverless.handler
      entryPoint:
        - '/lambda-entrypoint.sh'
    memorySize: 1024
    timeout: 330
    url: true
    provisionedConcurrency: 0
    events:
      - http:
          method: ANY
          path: /
      - http:
          method: ANY
          path: '{proxy+}'

Install below two packages:

npm i @codegenie/serverless-express
npm i -D @types/aws-lambda

Create serverless.ts

import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
import serverlessExpress from '@codegenie/serverless-express';
import { Callback, Context, Handler } from 'aws-lambda';
import { RequestMethod, ValidationPipe } from '@nestjs/common';
import helmet from 'helmet';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ResponseTransformInterceptor } from './common/interceptor/global-response-interceptor';
import { RolePermissionsSeederService } from './modules/v1/user/seed/role-permissions.seeder.service';
import { SeedService } from './modules/v1/character/seeder/seeder.service';
let server: Handler;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const allowedHosts = (process.env.CORS_ALLOWED_HOSTS as string) || '*';

  app.enableCors({
    origin: allowedHosts.split(','),
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    credentials: true,
  });

  app.setGlobalPrefix('api/v1', {
    exclude: [{ path: '/', method: RequestMethod.GET }],
  });

  app.use(helmet());

  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      whitelist: true,
    }),
  );

  const reflector = app.get(Reflector);
  app.useGlobalInterceptors(new ResponseTransformInterceptor(reflector));

  //const rolePermissionsService = app.get(RolePermissionsSeederService);

  //await Promise.all([rolePermissionsService.insertRolePermissions()]);

  const rolePermissionsService = app.get(RolePermissionsSeederService);
  const seedsService = app.get(SeedService);

  const config = new DocumentBuilder()
    .setTitle('Backend api')
    .setDescription('Swagger docs for backend apis')
    .setVersion('1.0')
    .addBearerAuth()
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('/api-docs', app, document);

  await app.init();

  const expressApp = app.getHttpAdapter().getInstance();
  return serverlessExpress({
    app: expressApp,
  });
}

export const handler: Handler = async (
  event: any,
  context: Context,
  callback: Callback,
) => {
  try {
    server = server ?? (await bootstrap());
    return server(event, context, callback);
  } catch (error) {
    console.error('Error during Lambda execution:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({
        error: 'Internal Server Error',
        message: error.message,
      }),
    };
  }
};

Create Dockerfile:

# Use the AWS Lambda base image for Node.js
FROM public.ecr.aws/lambda/nodejs:22

# Set the working directory in the container
WORKDIR ${LAMBDA_TASK_ROOT}

# Copy only package files first for dependency installation
COPY package*.json ${LAMBDA_TASK_ROOT}/

# Install dependencies (both production and development for the build phase)
RUN npm install

# Copy the rest of the application code (excluding files in .dockerignore)
COPY . ${LAMBDA_TASK_ROOT}/

# Build the NestJS application
RUN npm run build

# Set the Lambda function handler
CMD ["dist/src/serverless.handler"]

Create .env.dev in project root

NODE_ENV=development
PORT=8035
DATABASE_URL=postgresql://postgres:postgres@database_container:5432/nestjs
REDIS_HOST=redis_container
REDIS_PORT=6379
JWT_TOKEN_SECRET=dkfjkjdfkjdfkdfjkjdfkjkdkdfjk
AWS_BUCKET_NAME=store-backend
API_BACKEND_AWS_REGION=us-east-2
API_BACKEND_AWS_ACCESS_KEY=JKDIUENDIUEKNIUEIJKIWJKWJOUIW
API_BACKEND_AWS_SECRETE_KEY=LKFLKORJKIJKUFKJFKJKFJKFKFJ
AWS_CLOUD_FRONT_URL=https://your.cloudfront.net
AWS_SMTP_HOST=email-smtp.ap-southeast-1.amazonaws.com
AWS_SMTP_USER=AJKDIUENSDUJEHUSDJBUE
AWS_SMTP_PASS=fkdjkjrtiu93j934jkjf98493ikjkjrjii4ju5
AWS_FROM_EMAIL=alamin.cse15@gmail.com
AI_BACKEND_URL=https://aibackend.io/api/v2
CORS_ALLOWED_HOSTS='http://localhost:3000'
FRONTEND_BASE_URL="http://localhost:3000"

MAIL_HOST=smtp.gmail.com
MAIL_USER=alamin.cse15@gmail.com
MAIL_PASSWORD='sdjkdfjkdfkdfhjdfhdjfhj'
MAIL_FROM_NAME=Shaikh
MAIL_FROM_ADDRESS=alamin.cse15@gmail.com

*Install serverless framework globally: *

npm i -g serverless@3.39.0

Setup AWS access and secret key using AWS CLI:

**
Now navigate to project root and run **

sls deploy --stage dev

Make sure you have .env.dev exists in your project root:

**CI/CD: Deploy from github actions:**
name: Deploy Serverless App to Dev Environment

on:
  push:
    branches:
      - dev
    pull_request:
      types: [closed]
      branches:
        - dev

jobs:
  deploy:
    if: github.event.pull_request.merged == true || github.event_name == 'push'
    runs-on: ubuntu-latest

    steps:
      # 1. Checkout code
      - name: Checkout code
        uses: actions/checkout@v3

      # 3. Configure AWS Credentials
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-2

      # 4. Setup Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 20.x

      - name: Clear Serverless Cache
        run: |
          rm -rf .serverless

      # 5. Map environment variables manually
      - name: Set Environment Variables
        run: |
          rm -f .env.dev
          touch .env.dev
          echo "NODE_ENV=${{ secrets.NODE_ENV }}" >> .env.dev
          echo "DATABASE_URL=${{ secrets.DEV_DATABASE_URL }}" >> .env.dev
          echo "JWT_TOKEN_SECRET=${{ secrets.DEV_JWT_TOKEN_SECRET }}" >> .env.dev
          echo "AWS_BUCKET_NAME=${{ secrets.DEV_AWS_BUCKET_NAME }}" >> .env.dev
          echo "BACKEND_AWS_REGION=${{ secrets.DEV_BACKEND_AWS_REGION }}" >> .env.dev
          echo "BACKEND_AWS_ACCESS_KEY=${{ secrets.BACKEND_AWS_ACCESS_KEY }}" >> .env.dev
          echo "BACKEND_AWS_SECRETE_KEY=${{ secrets.DEV_BACKEND_AWS_SECRETE_KEY }}" >> .env.dev
          echo "AWS_CLOUD_FRONT_URL=${{ secrets.DEV_AWS_CLOUD_FRONT_URL }}" >> .env.dev
          echo "AWS_SMTP_HOST=${{ secrets.AWS_SMTP_HOST }}" >> .env.dev
          echo "AWS_SMTP_USER=${{ secrets.AWS_SMTP_USER }}" >> .env.dev
          echo "AWS_SMTP_PASS=${{ secrets.AWS_SMTP_PASS }}" >> .env.dev
          echo "AWS_FROM_EMAIL=${{ secrets.AWS_FROM_EMAIL }}" >> .env.dev
          echo "AI_BACKEND_URL=${{ secrets.DEV_AI_BACKEND_URL }}" >> .env.dev
          echo "CORS_ALLOWED_HOSTS=${{ secrets.DEV_CORS_ALLOWED_HOSTS }}" >> .env.dev
          echo "FRONTEND_BASE_URL=${{ secrets.DEV_FRONTEND_BASE_URL }}" >> .env.dev
          echo "MAIL_HOST=${{ secrets.MAIL_HOST }}" >> .env.dev
          echo "MAIL_USER=${{ secrets.MAIL_USER }}" >> .env.dev
          echo "MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}" >> .env.dev
          echo "MAIL_FROM_NAME=${{ secrets.MAIL_FROM_NAME }}" >> .env.dev
          echo "MAIL_FROM_ADDRESS=${{ secrets.MAIL_FROM_ADDRESS }}" >> .env.dev


      # 6. Install Serverless Framework v3
      - name: Install Serverless Framework v3
        run: npm i -g serverless@3.39.0

        # 7. Deploy to the specified stage
      - name: Deploy to dev
        run: sls deploy --stage dev

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow