For years, I spent a significant amount of my time managing EC2 instances, patching Linux kernels, and worrying about auto-scaling groups. It felt like I was spending more time being a sysadmin than a developer. That changed when I discovered the serverless paradigm. If you’re wondering how to build a serverless API with NodeJS, you’re looking at the most efficient way to deploy scalable backend logic without the overhead of server management.
In this tutorial, I’ll walk you through building a production-ready API using NodeJS, AWS Lambda, and the Serverless Framework. We’ll move from a blank folder to a live endpoint in under 15 minutes.
Prerequisites
Before we dive into the code, make sure you have the following installed and configured:
- Node.js (v18+): The runtime for our API.
- AWS Account: A free tier account is sufficient.
- AWS CLI: Installed and configured with your
aws configurecredentials. - Serverless Framework: Install globally via
npm install -g serverless.
Step 1: Initialize Your Project
First, let’s create a directory for our API and initialize the Serverless configuration. I prefer using the Serverless Framework because it abstracts the complex AWS CloudFormation templates into a simple YAML file.
mkdir nodejs-serverless-api
cd nodejs-serverless-api
serverless create --template aws-nodejs
This creates a serverless.yml file. This file is the heart of your infrastructure; it defines your functions, events (triggers), and environment variables.
Step 2: Define Your API Endpoints
Open serverless.yml. We want to create a simple API that can handle a GET request to fetch data and a POST request to save it. Here is how I structure my configuration for clarity and scalability:
service: nodejs-serverless-api
provider:
name: aws
runtime: nodejs18.x
region: us-east-1
stage: dev
functions:
helloWorld:
handler: handler.hello
events:
- http:
path: hello
method: get
createUser:
handler: handler.create
events:
- http:
path: users
method: post
Step 3: Writing the Handler Logic
Now, let’s create the handler.js file. In a serverless environment, your code is organized into “handlers”—functions that AWS Lambda invokes when an event (like an HTTP request) occurs.
'use strict';
module.exports.hello = async (event) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from Serverless NodeJS!' }),
};
};
module.exports.create = async (event) => {
const body = JSON.parse(event.body);
// In a real app, you'd save this to DynamoDB
return {
statusCode: 201,
body: JSON.stringify({
message: 'User created successfully',
user: body,
}),
};
};
As shown in the image below, the structure of your project should now be minimal: just the serverless.yml and handler.js. This simplicity is why I recommend this stack for MVPs.
Step 4: Deployment and Testing
The magic happens with a single command. Run the following in your terminal:
serverless deploy
The framework will package your code, upload it to S3, create a Lambda function, and set up an API Gateway. Once finished, you’ll receive a URL like https://xyz123.execute-api.us-east-1.amazonaws.com/dev/hello. Use Postman or cURL to test your endpoints.
Pro Tips for Production
Building the API is easy, but making it production-ready requires a few tweaks. In my experience, these three areas are where most developers struggle:
1. Handling Cold Starts
Serverless functions can suffer from “cold starts”—a delay when a function is invoked after being idle. I’ve found that minimizing package size and using Provisioned Concurrency can help. For a deeper dive, check out my guide on aws lambda cold start optimization.
2. Choosing Your Framework
While I used the Serverless Framework here, AWS has its own native tool. Depending on your project size, you might want to read my comparison of serverless framework vs aws sam to see which fits your workflow better.
3. Database Selection
Don’t use a traditional relational database (like MySQL) without a proxy. I highly recommend DynamoDB for serverless APIs because it handles the connection scaling natively, whereas traditional DBs can be overwhelmed by too many concurrent Lambda connections.
Troubleshooting Common Issues
- Permission Errors: Ensure your IAM user has
AdministratorAccessor specific permissions for Lambda, API Gateway, and CloudFormation. - Timeout Errors: By default, Lambda timeouts are short. If you’re doing heavy processing, increase the
timeoutvalue in theprovidersection ofserverless.yml. - CORS Issues: If your frontend can’t call the API, add
cors: trueunder thehttpevent in your config.
What’s Next?
Now that you know how to build a serverless API with NodeJS, the next logical step is adding security and persistence. I suggest exploring AWS Cognito for user authentication and DynamoDB for storage. If you’re feeling ambitious, try implementing a CI/CD pipeline using GitHub Actions to automate your serverless deploy command.