DEV Community

Avdhut Nalawade
Avdhut Nalawade

Posted on

Node JS Docker Deployment with lambda & API Gateway

Here's a comprehensive guide to deploying a Node.js application in a Docker container with AWS Lambda and exposing it via API Gateway. This approach leverages Docker for packaging the Node.js application, Lambda for serverless execution, and API Gateway for handling requests and serverless framework to deploy above services into AWS account through Github Actions.

File structure

├── Dockerfile
├── controller
├── docker-compose.yml
├── environment.py
├── lambda.js
├── layer.sh
├── middleware
├── models
├── node_modules
├── output
├── package.json
├── routes
├── server.js
├── serverless.yml
├── uploads
└── utils
Enter fullscreen mode Exit fullscreen mode

Dockerfile

FROM public.ecr.aws/lambda/nodejs:20
COPY package.json ${LAMBDA_TASK_ROOT}
RUN npm install --production
COPY . ${LAMBDA_TASK_ROOT}
COPY lambda.js ${LAMBDA_TASK_ROOT}
CMD ["lambda.handler"]

Enter fullscreen mode Exit fullscreen mode

lambda.js

import serverlessExpress from 'aws-serverless-express';
import app from './server.js';

const server = serverlessExpress.createServer(app);

export const handler = (event, context) => {
  return serverlessExpress.proxy(server, event, context);
};
Enter fullscreen mode Exit fullscreen mode

server.js

import express from 'express';
import userRouter from './routes/userRoutes.js'
import cors from 'cors';

const app = express()
const PORT = 3000;
app.use(cors());

app.use(express.json({ limit: '100mb' }));
app.use(express.urlencoded({ limit: '100mb', extended: true }));
app.use('/user', userRouter);

export default app; 
Enter fullscreen mode Exit fullscreen mode

serverless.yml

service: backend
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1
  timeout: 900
  memorySize: 128
  vpc: 
    securityGroupIds:
      - sg-group-id
    subnetIds:
      - subnet-id-1
      - subnet-id-2
  iam:
    role:
      statements:
        - Effect: 'Allow'
          Action:
            - 'lambda:InvokeFunction'
            - 'lambda:GetFunction'
            - 'lambda:ListFunction'
            - 'lambda:CreateEventSourceMapping'
            - 'lambda:GetEventSourceMapping'
            - 'lambda:ListEventSourceMappings'
            - 'lambda:UpdateEventSourceMapping'
            - 'iam:DetachRolePolicy'
            - 'iam:getRolePolicy'
          Resource: '*'
        - Effect: 'Allow'
          Action:
            - 'logs:*'
            - 'ec2:DescribeInstances'
            - 'ec2:CreateNetworkInterface'
            - 'ec2:AttachNetworkInterface'
            - 'ec2:DescribeNetworkInterfaces'
            - 'ec2:DeleteNetworkInterface'
            - 'ec2:DetachNetworkInterface'
            - 'ec2:AssignPrivateIpAddresses'
            - 'ec2:UnassignPrivateIpAddresses'
          Resource:
            - '*'
        - Effect: 'Allow'
          Action:
            - 'secretsmanager:GetSecretValue'
            - 'secretsmanager:ListSecrets'
            - 'secretsmanager:GetResourcePolicy'
          Resource:
            - '*'
        - Effect: 'Allow'
          Action:
            - 'geo:SearchPlaceIndexForText'
            - 'geo:SearchPlaceIndexForPosition'
          Resource:
            - '*'
        # Allow x-ray-tracing
        - Effect: 'Allow' # xray permissions (required)
          Action:
            - 'xray:PutTraceSegments'
            - 'xray:PutTelemetryRecords'
          Resource:
            - '*'
        - Effect: 'Allow'
          Action:
            - 'sns:Publish'
          Resource:
            - '*'
        - Effect: 'Allow'
          Action:
            - 's3:ListBucket'
            - 's3:GetObject'
          Resource:
            - '*'
        - Effect: 'Allow'
          Action:
            - 'sqs:*'
          Resource:
            - "*"

  apiGateway:
    apiKeys:
      - firstapikey

plugins:
  - serverless-plugin-log-retention


custom:
  logRetentionInDays: 7

package:
  exclude:
    - node_modules/**
    - layers/**


functions:  
  api:
    name: backend
    image: ${aws:accountId}.dkr.ecr.${self:provider.region}.amazonaws.com/backend:latest
    events:
      - http:
          path: /{proxy+}
          method: any
Enter fullscreen mode Exit fullscreen mode

github actions file

name: Deploy backend

on:
  push:
    branches:
      - development

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Check out the repository
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Configure AWS credentials using OIDC
        uses: aws-actions/configure-aws-credentials@v2
        with:
           role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
           aws-region: us-east-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to ECR
        env:
          AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
          ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com
          ECR_REPOSITORY: backend
          IMAGE_TAG: latest
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Install Dependencies
        run: |
          npm install -g serverless@3
          npm install serverless-plugin-log-retention --save-dev

      - name: serverless deploy
        uses: serverless/github-action@v3.2
        with:
          args: deploy --stage prod --config serverless.yml

Enter fullscreen mode Exit fullscreen mode

Top comments (0)