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:

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.

Project directory structure for a NodeJS serverless API showing serverless.yml and handler.js
Project directory structure for a NodeJS serverless API showing serverless.yml and handler.js

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

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.