Deploying your node backend [Express / Koa] in AWS Lambda

Deploying your node backend [Express / Koa] in AWS Lambda

GitHub link: https://github.com/chenchunaidu/koa-lambda-template

Why do we need to deploy your node backend application on AWS Lambda?

There are many ready-made options like vercel, netlify to deploy frontend applications on the cloud. We also have free services like Heroku (Not free anymore), and fly.io but the capacity of the free machine is limited only. AWS lambda is a free alternative for deploying your nodejs application on the cloud.

Setting up the Koa application

In this demo project, we are going to use koa as the node backend framework along with typescript.

Tech stack

  1. Koa + Typescript

  2. Prisma [Node ORM]

  3. Serverless http [For converting your node application into lambda]

Create a node package

mkdir myapp
cd myapp
npm init -y

Install required dependencies

npm install koa koa-bodyparser koa-jwt @koa/cors @prisma/client prisma serverless-http dotenv koa-router

Install required dev dependencies

npm install -D @types/koa @types/koa__cors @types/koa__router @types/koa-bodyparser @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser esbuild eslint nodemon ts-node typescript @types/koa-router

Folder structure

# create new folder called src inside your myapp folder
mkdir src
cd src
# create two new files called index.ts and router.ts
touch index.ts
touch router.ts

Copy and paste the following code into the index.ts file

import ServerlessHttp from "serverless-http";
import cors from "@koa/cors";
import koa, { Context } from "koa";
import bodyParser from "koa-bodyparser";
import router from "./router";
import * as dotenv from "dotenv";
import { PrismaClient } from "@prisma/client";
dotenv.config();

// create prisma client
const prisma = new PrismaClient();

// initialize app 
const app = new koa();

app
  .use(cors())
  .use(bodyParser())
  .use(async (ctx: Context, next) => {
    ctx.prisma = prisma;
    await next();
  })
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(3000, () => {
  console.log("app listening on port 3000");
});

Copy and paste the following code into the router.ts file

import Router from '@koa/router';

const router = new Router();

router.get("/api", (ctx, next) => {
  ctx.body = "Hello world";
});

export default router;

Initialize the Prisma

npx prisma init --datasource-provider sqlite

The above command will create a file prisma/schema.prisma add the following code to schema.prisma file

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}

Enter the following command to migrate your schema

npx prisma migrate dev --name init

Add the following line to your package.json scripts. By adding start:dev to your package.json scripts you can use npm run start:dev to run your application

  "scripts": {
    "build-esbuild": "rm -rf dist && esbuild ./src/index --entry-names=[dir]/[name]/index --bundle --minify --sourcemap --platform=node --target=node14.14 --outdir=dist", 
    "build": "npm run build-esbuild  && npm run copy-prisma-schema && npm run zip", // for building your application
    "start:dev": "nodemon --ignore tests/ --watch src -e ts,tsx --exec ts-node src/index.ts",
    "copy-prisma-schema": "cp -r prisma/schema.prisma dist/index", // for copying your prisma schema
    "zip": "zip -r sam/dist.zip dist/" // for zipping your application
  },

run the following command to start your application

npm run start:dev

Now the application will be running on localhost:3000 go to localhost:3000 to check your application

Converting your application serverless

currently, your application is served through HTTP to convert it into a lambda function we will use serverless-http package.

install serverless-http npm package by entering the following command in terminal

npm i serverless-http

In the index.js file, change listen app.listen code with the following code

The following code will convert your application into a lambda function

 export const handler = ServerlessHttp(app);

Building application

run the following command to build your application

npm run build

npm run build will run the following 4 commands

  1. npm run build-esbuild

    1. this will build your package along with all node module dependencies.
  2. npm run copy-prisma-schema

    1. copies your Prisma schema into your build folder
  3. npm run zip

    1. Compresses your build folder into zip format and copy it into the sam folder

Setup with AWS SAM

Install AWS sam in your local machine by following the steps in this documentation

AWS sam installation documentation

Create a folder inside the root folder and create a file called template.yaml inside it

copy the following YAML config into your template.yaml file

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
  EasySchoolBackendFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./dist.zip
      Handler: dist/index/index.handler
      Runtime: nodejs14.x
      Timeout: 900
      PackageType: Zip
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /{any+}
            Method: ANY

now cd into your sam folder and sam build and sam local start-API

cd sam
sam build # builds sam 
sam local start-api # start local api

now go to localhost:3000/api to check your application.

CI/CD setup

Create a file called deploy-lambda.yaml inside .github/workflows folder and copy and paste the following code into it

touch .github/workflows/deploy-lambda.yaml
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages

name: Node.js Package

on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 14
      - run: npm ci
      - run: npm run build
      - uses: aws-actions/setup-sam@v2
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-south-2
      - run: |
          cd sam
          sam build --use-container
      # Prevent prompts and failure when the stack is unchanged
      - run: |
          cd sam
          sam deploy --no-confirm-changeset --no-fail-on-empty-changeset

now add the following secrets to your GitHub account

following these steps to create access keys if you don't have it already

https://aws.amazon.com/premiumsupport/knowledge-center/create-access-key/

  1. AWS_ACCESS_KEY_ID (Your AWS access key)

  2. AWS_SECRET_ACCESS_KEY (Your AWS secret access key)