How to create a local Lambda / Node.js development environment

What is AWS Lambda?

For details visit Product Page

You can use AWS Lambda:

  • As an event-driven compute service where AWS Lambda runs your code in response to events, such as changes to data in an Amazon S3 bucket or an Amazon DynamoDB table.
  • As a compute service to run your code in response to HTTP requests using Amazon API Gateway or API calls made using AWS SDKs.

Simple AWS Lambda Function

It is pretty easy to develop an AWS Lambda function in Node.js. All a developer has to do is implementing the handler(event, context) function.

handler(event, context) is then called and executed by AWS Lambda when triggerred by an event.

This is the simplest code

exports.handler = function(event, context) {
    console.log("Hello Lambda World");
    context.succeed();
};

This also can be implemented as below;

myLambda = function(event, context) {
    console.log("Hello Lambda");
    context.succeed();
};

exports.handler = myLambda;

And also like this;

myLambda = function(event, context) {
    console.log("Hello Lambda");
    context.succeed();
};

module.exports.handler = myLambda;

So what is exports and module.exports?

Understanding exports.handler

Module Object

To understand exports.handler we need to understand module object first.

Node.js has a simple module loading system. In Node.js, files and modules are in one-to-one correspondence. For example, person.js represents person module.

In each module (implemented by .js file), the module free variable is a reference to the object representing the current module. module has module.exports variable that references the object which is instance of some class.

You may visualize this as { module : exports }

There are two possiblities at this point;

1- module.exports can represent the whole module acting as a constructor;

Below hello.js loads module person.js.

Contents of person.js

module.exports = function(){
    return {
        speak: function(){
            console.log("Hello I am a person");
        },
        ask: function(){
            console.log("Hello, how are you?");
        }
    };
};

Contents of hello.js

var person = require("./person.js");
var jack = new person();
jack.speak();
jack.ask();

2- module.exports can hold other functions

Contents of person.js

module.exports.speak = function(){
    console.log("Hello I am a person");
};

module.exports.ask = function(){
    console.log("Hello, how are you?");
};

There is exports alias to modules.exports.

Contents of person.js using exports alias instead of modules.exports;

exports.speak = function(){
    console.log("Hello I am a person");
};

exports.ask = function(){
    console.log("Hello, how are you?");
};

Contents of hello.js

var person = require("./person.js");
person.speak();
person.ask();

How it relates to Lambda?

With Lambda, lets assume, we create a file, give it a name MyFunction.js and define our handler as MyFunction.handler while configuring AWS Lambda.

Contents of MyFunction.js

exports.handler = function(event, context) {
    console.log("Hello Lambda World");
    context.succeed("Yay :)");
};

At this point we know that, AWS Lambda will require MyFunction.js and call handler function. It will also provide event and context objects.

An imaginary AWS Lambda code would be something like this

Contents of lambda.js (not final)

var lambda = require('./MyFunction.js');

lambda.handler(event, context);

lambda.js magically passes event and context object but to be able to work locally we need to pass these as well. So lets create two modules for these.

event.js module will provide a sample event to pass to our lambda function. This can be anything you want to parse.

Contents of event.js

module.exports = function(){
    return {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
        "key4": "value4"
    };
}

context.js module is a bit more complicated as it partialy implements real context object as define here.

Contents of context.js

module.exports = function() {
    return {
        succeed: function(result) {
            console.log("-> CONTEXT SUCEEED: ", result);
        },
        fail: function(err) {
            console.log("-> CONTEXT FAIL: ", err);
        },
        done: function(err, result) {
            console.log("-> CONTEXT DONEL", err, result);
        },
        functionName: 'local_function',
        awsRequestId: 'local_awsRequestId',
        logGroupName: 'local_logGroupName',
        logStreamName: 'local_logStreamName',
        clientContext: 'local_clientContext',
        identity: {
            cognitoIdentityId: 'local_cognitoIdentityId'
        }
    };
};

If we improve our lambda.js to use these two modules it looks like this;

Contents of lambda.js

var lambda = require('./MyFunction.js');
var context = require('./context.js');
var event = require('./event.js');

var thisEvent = new event();
var thisContext = new context();

lambda.handler(thisEvent, thisContext);

Four Files We Created

Finally we have all the files we need to develop lambda function locally. These are;

  • MyFunction.js
    • This is the file you will change to implement your function
  • event.js
    • This is the file to put your sample event data
  • context.js
    • Probably no changes here
  • lambda.js
    • This refernces MyFunction.js, so if you change MyFunction.js, you need to change it here as well.

Executing Lambda Function

All files being in the same folder, you need to run;

node lambda.js

All Code

Contents of MyFunction.js

exports.handler = function(event, context) {
    console.log("Hello Lambda World");
    context.succeed("Yay :)");
};

Contents of event.js

module.exports = function(){
    return {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
        "key4": "value4"
    };
}

Contents of context.js

module.exports = function() {
    return {
        succeed: function(result) {
            console.log("-> CONTEXT SUCEEED: ", result);
        },
        fail: function(err) {
            console.log("-> CONTEXT FAIL: ", err);
        },
        done: function(err, result) {
            console.log("-> CONTEXT DONEL", err, result);
        },
        functionName: 'local_function',
        awsRequestId: 'local_awsRequestId',
        logGroupName: 'local_logGroupName',
        logStreamName: 'local_logStreamName',
        clientContext: 'local_clientContext',
        identity: {
            cognitoIdentityId: 'local_cognitoIdentityId'
        }
    };
};

Contents of lambda.js

var lambda = require('./MyFunction.js');
var context = require('./context.js');
var event = require('./event.js');

var thisEvent = new event();
var thisContext = new context();

lambda.handler(thisEvent, thisContext);

Bonus, Creating the Zip File

zip -r MyFunction.zip . -i MyFunction.js node_modules/\*
comments powered by Disqus