Run AWS Lambda Locally – NodeJS + API Gateway (No Installs!)

The goal of this guide is to help you with the following setup:

  1. You have a NodeJS AWS Lambda function
  2. You want to trigger it with API Gateway (via the simple PROXY Integration)
  3. You do not care that the dev environment is EXACTLY the same as the prod environment.
  4. Furthermore, you don’t want to install anything new like AWS SAM, Docker or some other serverless framework

If the above is you, then this guide will show you how you can run aws lambda locally for this kind of setup.

Note: If you do need the dev environment to be exactly the same as your production…

You would be better off installing AWS SAM. If you use SAM along with Docker, you can create an API Gateway URL using this guide.

What I offer, in this guide, does not involve Docker or any other installation.

A Simple (Quick & Dirty) Script To Simulate The API Gateway URL On Your Machine

I am going to assume that you have NodeJS installed on your machine. All you need to do is create a new file with the below code.

I called the file: “simulated_api_gateway.js”

// Update the 2 lines below with your information & run the script
let pathToAwsLamdaFunction = './aws_lamda_function/index'
let nameOfFunction = 'handler'

let lambdaMethod = require(pathToAwsLamdaFunction)[nameOfFunction]

const http = require('http')

const serverWrapper = http.createServer(function (request, response) {
    
    const url = new URL(request.url, `http://${request.headers.host}/`)

    let data = "";
    request.on("data", (chunk) => {
        data += Buffer.from(chunk).toString('base64');
    });

    request.on("end", () => {

        const event = {
            body: data,
            headers: request.headers,
            httpMethod: request.method.toUpperCase(),
            isBase64Encoded: (request.method.toUpperCase() == 'POST' ? true : false),
            path: url.pathname,
            pathParameters: {},
            queryStringParameters: Array.from(url.searchParams.keys()).reduce((output, key) => {
                output[key] = url.searchParams.get(key);
                return output;
            }, {}),
            requestContext: {},
            resource: "/{proxy+}",
            stageVariables: {},
        };
        
        lambdaMethod(event, {})
        .then((res) => {
            let {
                body,
                headers,
                statusCode,
            } = res

            if (res.isBase64Encoded) {
                body = Buffer.from(body, 'base64')
            }

            if (!headers['content-length'] && body) {
                headers['content-length'] = body.length
            }

            response.writeHead(statusCode, headers)
            response.end(body)
        })
        .catch((err) => {
            console.error('Something went horribly, horribly wrong')
            console.error(err)
            response.writeHead(500, { 'content-length': 0 })
            response.end('')
        })

    });
})

serverWrapper.listen(process.env.PORT || 3000, () => {
    console.log(`Listening on http://localhost:${serverWrapper.address().port}/`)
})

Now, as you can see, this creates a simulated API Gateway server.

What you need to do..

  • Change the code in the first 2 lines.
  • Update it to reflect the location of your AWS Lambda function and the name of the handler.

Once you do this, you can run the node server like so:

> node simulated_api_gateway.js

The server will start on port 3000 (you can change it if you want, on line number 65)

A Simple Lambda Function To Test Things Out…

To test things, out, I have created a small lambda function that just echoes out the event data in the JSON format.

Below is that simple function..

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        headers: {"content-type": "application/json"},
        body: JSON.stringify(event),
    };
    return response;
};

If you go to the URL: http://localhost:3000/ you should see that the server is running and responding with JSON just like it would if it were an API Gateway Proxy Integration for AWS Lambda.

If I go, the URL: http://localhost:3000/?name=livefiredev&type=blog

I see something like the below image in my browser…

running a simulated API gateway using node

I have tested to see that this simulated API gateway generates an event object properly (and very similar to API gateway) for the following types of common requests:

  1. A GET request with query string parameters
  2. A POST request with form parameters
  3. A POST request, used to upload a file via a form

In case, are not sure how to parse POST requests, I have written an in-depth guide on the same here.

Note: You do not need to keep your lambda function in the same folder as this script. They can be placed anywhere you like on your machine.

I hope that you found this useful.

Credits:
I have built upon and refined the work shown in this gist: https://gist.github.com/Sleavely/f87448d2c1c13d467f3ea8fc7e864955