Deploy a Node.js App on AWS Lambda with DynamoDB Using the Serverless Framework: A Step-by-Step Guide
Table of contents
Deploying applications in a serverless environment is becoming increasingly popular for its cost-effectiveness and ease of scaling. In this guide, you’ll learn how to deploy a Node.js application on AWS Lambda using the Serverless Framework, and connect it to DynamoDB. By the end, you’ll have a fully functional serverless app that interacts with DynamoDB, all with minimal setup and management!
1. What is the Serverless Framework?
The Serverless Framework is an open-source framework that helps developers manage serverless deployments on various cloud platforms, including AWS, Azure, and Google Cloud. It simplifies deploying serverless applications by allowing you to define resources and functions in a configuration file, eliminating the need for complex infrastructure setup.
2. Prerequisites
To follow along with this guide, ensure you have the following:
Node.js and npm: Install the latest version of Node.js from nodejs.org.
AWS Account: Set up an AWS account with access to Lambda and DynamoDB services. Sign up here.
Serverless Framework: Install it globally by running the command below if you haven’t already:
npm install -g serverless
3. Setting Up a New Serverless Project
The first step is to create a Serverless project that will serve as the base for deploying your AWS resources and functions.
Steps:
Create the Project
Open your terminal and run the following command:💡Make sure to create your account on serverless website and validate your credentials through terminal.serverless
This will show you several Templates. Choose 3rd option Aws / Node.js / Express Api with Dynamodb
-
Then, select your project name in that folder. The Serverless Framework will generate your complete boilerplate code
- Then select create a new app to continue.
4. Next, provide an app name. A Lambda function will be created with this name
- Install Dependencies
All ready some dependencies are installed through boiler plate code and we have to add some more dependencies for this project
cd "your project name"
npm i
npm i crypto-js
npm i jsonwebtoken
npm i cors
The aws-sdk
library is essential for integrating our app with AWS services.That is already installed if you selected aws + node.js+dynamodb template.
4. Configuring serverless.yml
for Lambda and DynamoDB
The serverless.yml
file defines your resources and functions. Here’s how to configure it to create a Lambda function and DynamoDB table.
Edit the serverless.yml
file as follows:
# "org" ensures this Service is used with the correct Serverless Framework Access Key.
org: basir #change this according to your orgname
# "app" enables Serverless Framework Dashboard features and sharing them with other Services.
app: basirapp #change this to your app name
# "service" is the name of this project. This will also be added to your AWS resource names.
service: basirapp #change this according to your service name
stages:
default:
params:
tableName: "basirtable" #change or create this on your aws account
provider:
name: aws
runtime: nodejs20.x
region: ap-south-1
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- Fn::GetAtt: [UsersTable, Arn]
environment:
USERS_TABLE: ${param:tableName}
httpApi:
cors:
allowedOrigins:
- '*' # Allows all origins; use specific origins for production
allowedHeaders:
- Content-Type
- Authorization
- X-Amz-Date
- X-Api-Key
- X-Amz-Security-Token
- X-Requested-With
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
maxAge: 86400
functions:
api:
handler: handler.handler
events:
- httpApi:
path: "/{proxy+}" # Correct syntax for a catch-all route
method: ANY
resources:
Resources:
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: email
AttributeType: S
KeySchema:
- AttributeName: email
KeyType: HASH
BillingMode: PAY_PER_REQUEST
TableName: ${param:tableName}
Explanation:
org, app, and service:
The
org
specifies the Serverless Framework organization (basir
), ensuring that the service is linked with the correct Serverless Framework Access Key.The
app
name (basirapp
) enables features on the Serverless Framework Dashboard, facilitating easier management and sharing with other services.The
service
(basirapp
) is the name of this project, which will also be included in the names of the AWS resources created by this service.
stages:
- Defines a default stage with a parameter
tableName
, set to"basirtable"
. This parameter is referenced throughout the configuration to specify the DynamoDB table name.
- Defines a default stage with a parameter
provider:
Specifies AWS as the cloud provider and sets the runtime to
nodejs20.x
.The
region
is defined asap-south-1
.Configures an IAM role with permissions to perform various DynamoDB actions (
Query
,Scan
,GetItem
,PutItem
,UpdateItem
, andDeleteItem
) on the specified DynamoDB table.The
environment
section defines an environment variableUSERS_TABLE
, which utilizes thetableName
parameter value (basirtable
) for use within Lambda functions.
httpApi:
Configures CORS (Cross-Origin Resource Sharing) settings to allow requests from any origin (
allowedOrigins: '*'
). For production environments, it is advisable to specify allowed origins explicitly.Specifies the headers and HTTP methods that are permitted in requests, along with a cache duration of
maxAge: 86400
seconds (24 hours).
functions:
Defines a Lambda function named
api
, with the handler implemented inhandler.handler
.Sets up an
httpApi
event with a catch-all route (/{proxy+}
) that allows all HTTP methods (method: ANY
) to facilitate flexible request handling.
resources:
Creates an AWS DynamoDB table named
UsersTable
.The table's primary key is defined by the
email
attribute, which is of typeString
(S
).Configured with
BillingMode: PAY_PER_REQUEST
, allowing the table to automatically scale and bill based on the number of requests.The table name is dynamically set using the
tableName
parameter (basirtable
).
5. Writing the Lambda Handler Code
Next, create the Lambda function to save data to DynamoDB.
Steps:
Update handler.js code into your project
In the root of the project. updatehandler.js
.Add the Following Code:
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); const { DynamoDBDocumentClient, GetCommand, PutCommand, } = require("@aws-sdk/lib-dynamodb"); const crypto = require("crypto-js"); const jwt = require("jsonwebtoken"); const express = require("express"); const serverless = require("serverless-http"); const cors = require('cors'); const app = express(); const USERS_TABLE = process.env.USERS_TABLE; const AES_SECRET = "56snbwuy#kdhuyethj39738626rhhgfd"; const jwtSecret = "jskhshs54w57qjhyt2652geftsrhvhagskn@medgus"; const client = new DynamoDBClient(); const docClient = DynamoDBDocumentClient.from(client); app.use(express.json()); const corsOptions = { origin: '*', // or '*' for all origins during development methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Amz-Date', 'X-Api-Key', 'X-Amz-Security-Token', 'X-Requested-With'], credentials: true, optionsSuccessStatus: 200 // Some legacy browsers choke on status 204 }; // Use CORS with the specified options app.use(cors(corsOptions)); app.get("/",(req,res)=>{ res.send("Hello World") }) //all routes starts from here app.post("/register", async (req, res) => { const {name,email,password,clg,phone} = req.body const hashpass = crypto.AES.encrypt(password,AES_SECRET).toString(); //checking the user is exist or not; const getParams = { TableName: USERS_TABLE, Key: { email: email, }, }; try{ const data = await docClient.send(new GetCommand(getParams)); if(data.Item){ return res.status(400).json({error:"User already exists",success:false}) } } catch(err){ console.log(err) res.status(500).json({error:"Could not create user",success:false}) } //create user const params = { TableName: USERS_TABLE, Item: { name: name, email: email, password: hashpass, clg: clg, phone: phone, }, }; try{ let a = await docClient.send(new PutCommand(params)); res.status(200).json({message:"User created successfully",success:true}) } catch(err){ console.log(err) res.status(500).json({error:"Could not create user",success:false}) } }) //login endpoint app.post("/login", async (req, res) => { try{ const {email,password} = req.body; const params = { TableName: USERS_TABLE, Key: { email: email, }, }; try{ let data = await docClient.send(new GetCommand(params)); if(!data.Item){ return res.status(404).json({error:"User not found",success:false}) } else{ const decryptpass = crypto.AES.decrypt(data.Item.password,AES_SECRET).toString(crypto.enc.Utf8); if(decryptpass == password){ const token = jwt.sign({email:email},jwtSecret); return res.status(200).json({message:"Login Successfull",success:true,token:token}) } else{ return res.status(401).json({error:"Invalid Password",success:false}) } } } catch(err){ console.log(err) res.status(500).json({error:"Login Failed ! Db Error.",success:false}) } } catch(err){ console.log(err) res.status(500).json({error:"Some thing Went Wrong .Try again later!",success:false}) } }) app.use((req, res, next) => { return res.status(404).json({ error: "Not Found", }); }); exports.handler = serverless(app);
Explanation:
This Lambda function, implemented with Node.js and Express, interacts with Amazon DynamoDB to manage user registrations and logins. Key features include:
Dependencies: Utilizes
@aws-sdk/client-dynamodb
for database operations,crypto-js
for password encryption,jsonwebtoken
for creating JWT tokens, andexpress
for routing.Environment Variables: Configures
USERS_TABLE
for DynamoDB, along with secret keys for AES encryption and JWT signing.Endpoints:
GET
/
: Returns "Hello World" to indicate the server is running.POST
/register
: Handles user registration by checking for existing users, encrypting passwords, and saving user data to DynamoDB.POST
/login
: Authenticates users by verifying credentials and returning a JWT token upon successful login.
Error Handling: Includes robust error handling to provide informative responses for various scenarios, such as user existence and authentication failures.
404 Handling: Returns a 404 status for any undefined routes.
AWS Lambda Integration: The
exports.handler
allows the Express app to run in the AWS Lambda environment.
In summary, this function provides a secure user management system with encrypted passwords and JWT-based authentication, leveraging DynamoDB for data storage.
6. Deploying the Application
With everything configured, let’s deploy your application to AWS Lambda!
Steps:
Configure AWS Credentials
Set up your AWS credentials on your local machine:serverless config credentials --provider aws --key YOUR_AWS_ACCESS_KEY --secret YOUR_AWS_SECRET_KEY
Deploy the Application
Run the following command to deploy your application:serverless deploy
This command packages your application, uploads it to AWS, and creates the resources specified in
serverless.yml
.A DynamoDB table named "basirtable" is created through the
serverless.yml
configuration file, which defines the primary key as theemail
attribute. The table operates inPAY_PER_REQUEST
billing mode, ensuring efficient scaling and cost management based on actual usage.Also check lamda our function is deployed successfully.
7. Testing the Deployment
After deployment, test the endpoint to confirm that it works as expected.
Get the Endpoint URL
The deployment process will output an endpoint URL in the terminal.Send a Request to the URL
Use a tool like curl or Postman to send a GET request to the endpoint:GET https://x60hefyye3.execute-api.ap-south-1.amazonaws.com
Sends a post request to create an account to “/register” end point through postman.
Feel free to check another end point as well in my case all end point is working fine.
8. Verifying DynamoDB Data
Now, let’s confirm that the data was saved in DynamoDB.
Go to the DynamoDB Console
In your AWS Management Console, open DynamoDB.Check the Table
Find your table (MyTable
), and open it to view items. You should see the item created by your Lambda function.
9. Wrapping Up
Congratulations! 🎉 You’ve successfully deployed a Node.js app to AWS Lambda using the Serverless Framework and integrated it with DynamoDB. This setup demonstrates the power of serverless architecture, reducing costs and simplifying scaling while allowing you to quickly deploy applications.
By following this guide, you now have a foundational setup you can build upon, expanding your application’s features or integrating more AWS services. Enjoy building with serverless, and keep experimenting to leverage AWS Lambda and DynamoDB for even more advanced use cases!
10. Bonous Section
In this section, we will connect the Lambda function to our frontend via a REST API, allowing seamless interaction between the backend and frontend components. This integration will enable us to handle requests efficiently, enhancing the application's responsiveness and user experience. Let's proceed with setting up the API endpoint to establish this connection.
1. Clone the github repo .
git clone https://github.com/BasirKhan418/basirapp-serverless-frontend.git
cd basirapp-serverless-frontend
npm i
Create a
.env
file and add the following content. Feel free to update the value ofVITE_BACKEND_URL
with the URL of your own Lambda function deployment or API gateway:VITE_BACKEND_URL=<your_lambda_function_url>
Build your project / frontend.
npm run build
After a successful build, a
dist
folder will be generated. This folder contains the production-ready files, which we need to upload to S3 to host the frontend application.Create an S3 bucket with your desired name. Uncheck the option to block all public access, leave all other settings as default, and click Create bucket to proceed.
Click on recently created bucket go to permissions and click on edit bucket policy and paste the content below of it and make sure to add your s3 arn.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Principal": "*", "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": "your arn/*" } ] }
Upload you dist folder contents to s3
Navigate to the Properties tab, scroll down to the Static website hosting section at the bottom of the page, and click Edit. Enable static website hosting, enter
index.html
in the Index document field, and click Save changes to apply.🎉 Hooray! Your frontend has been deployed successfully.
Open the generated link in a new tab, try registering, and check if the data is being saved in DynamoDB.
Conclusion:
By following this guide, titled "Deploy a Node.js App on AWS Lambda with DynamoDB Using Serverless Framework: Step-by-Step Guide," you've successfully deployed a full-stack, serverless registration and login system using AWS. Your Node.js backend now operates on AWS Lambda, with DynamoDB handling data storage, ensuring both security and scalability. Additionally, hosting the frontend on S3 offers a cost-effective, high-performance solution for web access.
This setup not only provides a robust foundation for authentication workflows but also streamlines application management by leveraging AWS services. With this approach, you're well-equipped to scale effortlessly, adding new features or integrating additional AWS services as needed.
In summary, this beginner-friendly, step-by-step guide has equipped you with the knowledge to deploy a fully functional serverless application in the cloud, perfect for developers looking to learn AWS Lambda and DynamoDB integration!
Subscribe to my newsletter
Read articles from Basir Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Basir Khan
Basir Khan
Full-Stack Developer | MERN + Next.js | DevOps & Cloud Enthusiast I specialize in building dynamic web applications using the MERN stack and Next.js. Currently exploring DevOps and cloud technologies to streamline workflows, automate deployments, and optimize cloud infrastructure for scalable, efficient solutions.