Self-hosted service
ashiina/lambda-local avatar
ashiina/lambda-local

lambda-local: a small Node.js harness for running Lambda functions on your machine

Commandline tool to run Amazon Lambda function on local machines.

754 stars101 forksJavaScriptMIT

At a glance

What is it?
Lambda-local is a command line tool and API that runs NodeJS Amazon Lambda functions locally, providing a built-in context object and nothing else. It suits unit tests that want coverage of a handler, and it is not a serverless emulator, because its own maintainer says so and is looking for someone to take it over.
Who is it for?
lambda-local is the right tool for a narrow but common job, running a Node.js handler in a Mocha test and asserting on the result while collecting coverage, because it gives you a real context object with the parameters a Lambda would have and nothing else to get in the way. It is the wrong tool for reproducing a deployment, for anything but Node.js, and for a team that needs a maintained dependency.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Lambda harness, not a serverless emulator

Lambda-local lets you test NodeJS Amazon Lambda functions on your local machine, by providing what the project calls a simplistic API and a command line tool. The framing in the readme is unusually direct about scope: it does not aim to be perfectly feature proof, unlike projects such as serverless-offline or docker-lambda, and instead aims to remain very light.

What light means here is spelled out. It still provides a fully built Context, it handles all of its parameters and functions, and everything is customizable easily. So the deliverable is one of the pieces of the Lambda environment rather than the whole thing: a context object, an event, a handler, and a way to run them.

The main target is unit tests and running lambda functions locally. The readme points at the test directory for examples, especially the ones used with Mocha, and the point of using it that way is coverage. That is a much smaller ambition than running a whole API Gateway in front of your function, and it is the right way to think about the tool.

The repository is small and legible: src/ for the source, test/ for the suite, examples/ with event fixtures and handlers, package.json, tsconfig.json, a Makefile, a .jshintrc and a CHANGELOG.md. The published package lists build, examples, the json files and the Makefile as its contents.

The execute() options are the whole API surface

The API is one function. You require the package and call lambdaLocal.execute() with an options object:

js
const lambdaLocal = require("lambda-local");

On TypeScript, supported from version 1.7.0 onwards, the import form is available as well.

The options table is long, and it is the most useful page in the readme, so it is worth knowing what is in it. The event is the requested event as a JSON object. The handler is located either by lambdaPath, a path to the lambda function, or by lambdaFunc, the function itself, and the two cannot be used together. The lambdaHandler option names the handler and defaults to handler.

Then come the AWS specifics: profilePath for a path to your AWS credentials file and profileName for the profile within it, and region which defaults to us-east-1. timeoutMs is the timeout, defaulting to 3000 ms. The esm flag marks the script as an ECMAScript module for use with import syntax, and environment, envfile and envdestroy cover extra environment variables, loading an environment file before booting, and destroying the added environment on closing.

Three options exist purely for the purposes of a test. callbackWaitsForEmptyEventLoop waits for an empty loop before returning, and the readme is candid that this is off by default because the implementation is not perfect and only emulates the behaviour. clientContext populates the clientContext property of the second parameter, and contextOverwrite is a function that overwrites the context object, for example its awsRequestId, so that a test can assert on a stable value.

There is also a logger you can reach. lambdaLocal.setLogger(logger) and lambdaLocal.getLogger() give access to the winston logger the package uses, which is the way to capture or silence log output in a test run.

Promises by default, callbacks when you want them

The execution returns a Promise when no callback is supplied, and the readme shows both styles. The Promise form is the shortest path:

js
const lambdaLocal = require('lambda-local');

var jsonPayload = {
    'key': 1,
    'another_key': "Some text"
}

lambdaLocal.execute({
    event: jsonPayload,
    lambdaPath: path.join(__dirname, 'path_to_index.js'),
    profilePath: '~/.aws/credentials',
    profileName: 'default',
    timeoutMs: 3000
}).then(function(done) {
    console.log(done);
}).catch(function(err) {
    console.log(err);
});

The callback form passes the same options and adds a callback key, which becomes the lambda third parameter:

js
const lambdaLocal = require('lambda-local');

var jsonPayload = {
    'key': 1,
    'another_key': "Some text"
}

lambdaLocal.execute({
    event: jsonPayload,
    lambdaPath: path.join(__dirname, 'path_to_index.js'),
    profilePath: '~/.aws/credentials',
    profileName: 'default',
    timeoutMs: 3000,
    callback: function(err, data) {
        if (err) {
            console.log(err);
        } else {
            console.log(data);
        }
    },
    clientContext: JSON.stringify({clientId: 'xxxx'})
});

The verboseLevel option controls how much gets printed, which matters when a test suite runs many invocations. The default is 3. Level 2 dismisses the handler text, level 1 dismisses the lambda-local text as well, level 0 also dismisses the result, and level -1 shows only the handler text.

The onInvocationEnd option is the one that shows this library has been used against real response streaming. It is called once the invocation has ended, which is useful when awslambda.streamifyResponse is in play, because it distinguishes the end of the response stream from the end of the invocation.

The command line tool exposes the same options as flags

Installing it globally puts a lambda-local binary on your path:

bash
npm install -g lambda-local

From there, the CLI mirrors the API. The lambda path and the event path are required, except when watch mode is in use. The handler name defaults to handler, the timeout defaults to 3 seconds, and the region defaults to us-east-1.

Credentials are handled with two flags that work together. The profile path flag reads a specified AWS credentials file, and the profile flag selects the AWS profile inside it, so you point at a file and then name a profile within that file.

The rest are pass-through switches: --esm loads the function as an ECMAScript module, -E sets extra environment variables as a JSON object, --envfile loads extra environment variables from a file, and --envdestroy destroys the added environment on closing, off by default. The verbosity flag takes the same 3, 2, 1 or 0 values as the API option, and --wait-empty-event-loop turns on the callbackWaitsForEmptyEventLoop behaviour, with the readme repeating that it exists because the implementation only emulates it.

There is one flag that stands apart from the rest. --inspect starts lambda-local using the NodeJS inspector, available in NodeJS above 8.0.0, and it takes an optional host and port. That is what makes the tool usable for interactive debugging of a handler, stepping through it in a debugger attached to the local process rather than attaching to a deployed Lambda.

The flag list in the readme is cut off at the watch option, so the details of the watch mode behaviour are not in the documentation you have here.

Event fixtures in the examples folder show what a realistic event looks like

The examples directory is the most practical part of the repository for learning the tool, because it contains ready-made event payloads for the services a Lambda is most likely to be triggered by. There are fixtures for API Gateway, CloudFormation, CloudFront, a DynamoDB update, a Kinesis record, a Lex event, an S3 put, an SES received email, an IoT button press and a configuration change, plus a hello world event.

There are handlers to match, including a CloudFront edge handler and a gateway handler alongside the hello world one. The point of these fixtures is that you do not have to hand-write the awkward parts of an AWS event shape, which is where a locally executed test usually goes wrong.

The readme also points out that the event path argument is not required when watch is in use, which tells you the tool is built for a reload-on-change workflow where the event comes from somewhere other than a file.

The build system is the other half of the practical story. The Makefile provides install, test, clean and build targets, where the build target depends on tsconfig.json and node_modules and runs a NODE_ENV=build npm install followed by npm run build, and the test target installs first and then runs the mocha binary from the test directory. There is also a plain npm route, with npm install, npm install --only=dev and npm run build, where the build script is simply tsc. The package itself is written in TypeScript and published compiled, with main pointing at build/lambdalocal.js and types at build/lambdalocal.d.ts.

A project asking for a maintainer, and a release line that stopped

The readme carries a maintainer wanted warning, and it is unusually direct about the reasons. The current maintainer says he has been maintaining the project for the last 10 years but lately no longer has the time, and that the use case he had for it also ended more than eight years ago. He invites contact by email.

The release history supports that story. The most recent published version is 2.2.0, released on 2024-04-13, before it 2.1.2 from 2023-08-19 and 2.1.1 from 2023-07-24. The default branch is develop, and the last push to the repository was on 2026-10-01, so work is landing on the branch while the published package on npm stays where it was. If you install from npm you get 2.2.0; if you clone develop you get something newer, and the difference between those two is not documented in the changelog.

The licence is MIT, stated both in the package metadata and in the LICENSE file at the root, which is about as permissive as a dependency licence gets and carries no obligation for a company that ships it.

The runtime requirements are modest: the engines field asks for Node above 8, and the runtime dependencies are only commander for argument parsing, dotenv for environment files and winston for logging. The development dependencies are mocha, chai, sinon and typescript pinned below 5.1.0, which is a sign of a codebase that has not been updated to a current TypeScript.

For a team that only needs this in a test suite, that is all workable. What it lacks is a promise that a dependency will still be there in two years, and the readme asks for a maintainer precisely because that promise currently depends on one person with a full-time job.

Editorial conclusion

lambda-local is the right tool for a narrow but common job, running a Node.js handler in a Mocha test and asserting on the result while collecting coverage, because it gives you a real context object with the parameters a Lambda would have and nothing else to get in the way. It is the wrong tool for reproducing a deployment, for anything but Node.js, and for a team that needs a maintained dependency. Before you adopt it, check the three things the repository itself puts in front of you: that the project is looking for a new maintainer, since the current one says in the readme that ten years of maintenance ended because his own use case ended more than eight years ago; that the newest published release is 2.2.0 from 2024-04-13 while the last push was on 2026-10-01, so the branch has moved on from what npm will give you; and that the --wait-empty-event-loop behaviour is described as an emulation that is not perfect, which is the kind of detail that decides whether a test suite can rely on it.

Frequently asked questions

How do I install lambda-local?

Install it globally from npm with npm install -g lambda-local, which puts a lambda-local binary on your path. To work on the project itself, the Makefile provides make build, or you can run npm install, npm install --only=dev and npm run build, where the build script runs tsc.

How do I run a Lambda function locally with lambda-local?

Require the package and call lambdaLocal.execute() with an options object holding at least an event and either lambdaPath or lambdaFunc, plus optionally lambdaHandler, timeoutMs, region, profilePath, profileName and environment settings. The call returns a Promise unless you pass a callback option, in which case the callback receives the error and the data.

Is lambda-local a full serverless emulator?

No. The readme states it does not aim to be perfectly feature proof, unlike serverless-offline or docker-lambda, and aims instead to stay very light while providing a fully built Context with all its parameters handled. The stated main target is unit tests and running lambda functions locally.

Official sources

  1. ashiina/lambda-local on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ashiina-lambda-local.svg)](https://hysenlabs.com/projects/ashiina-lambda-local)

Was this useful?