Serverless Node.js on AWS Lambda

January 10, 2017
serverless aws lambda node.js aws lambda

The Serverless framework makes it very simple to create and deploy a function to a serverless platform such as AWS Lambda. I’m amazed by how easy it is to start a project and have it deployed to AWS Lambda within a couple of hours. Let’s try creating a couple of HTTP endpoints with it.

To get started, we need to first install Serverless npm install -g serverless.

Set up AWS credentials

I prefer using an AWS profile to manage my credentials instead of exporting to environment variable so I can manage multiple AWS profiles on my dev machine.

serverless config credentials \
  --provider aws \
  —-key AWS_ACCESS_KEY_ID \
  --secret AWS_SECRET_ACCESS_KEY \
  —-profile dev-profile

Note that the user associated with this ACCESS_KEY_ID needs the relevant permissions in AWS IAM.

Create a service using Serverless

In this demo app, let’s create an API with 2 endpoints:

Let’s start by creating a new project in a new directory, calling it serverless-lambda:

serverless create \
  --template aws-nodejs \
  --name serverless-lambda \
  --path serverless-lambda

This will create a boilerplate for the serverless-lambda service. It creates 2 files:

Configuring serverless.yml

Let’s open serverless.yml, and have a look at the boilerplate provided.

The service name serverless-lambda, provider name and runtime are automatically generated when we created the service using the Serverless CLI.

Now let’s add a few items to the provider section. stage: dev will indicate that this is a development stage. As I’m located in Singapore, I’m using region: ap-southeast-1. profile: dev-profile tells Serverless to use the dev-profile we made earlier.

service: serverless-lambda

provider:
  name: aws
  runtime: nodejs4.3
  stage: dev
  region: ap-southeast-1
  profile: dev-profile

Next are the functions, which are the meat of the Serverless service. The default function hello has the handler set to handler: handler.hello which correspond to the function exported in handler.js. Since we are creating 2 endpoints, we need to define 2 functions in serverless.yml. Next, we need to specify the triggers which will invoke these functions which in our demo app are going to be HTTP calls. Let’s add them to the serverless.yml, and the final serverless.yml should look like this:

service: serverless-lambda

provider:
  name: aws
  runtime: nodejs4.3
  stage: dev
  region: ap-southeast-1
  profile: dev-profile

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
  echo:
    handler: handler.echo
    events:
      - http:
          path: echo
          method: post

The rest of the boilerplate can be deleted for now since we’re not going to need it.

Implementing handler.js

Now we have the serverless.yml set up, let’s go to the implementation in handler.js. As we have defined 2 functions hello and echo earlier, we need to export these 2 functions in handler.js. The handler function takes parameters (event, context, callback) and responds by executing callback, with (err, response) as parameters, where response is the HTTP response. In this example, we will just respond to the call with the appropriate response.

'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Hello World!'
    }),
  };

  callback(null, response);
};

module.exports.echo = (event, context, callback) => {
  const body = JSON.parse(event.body)
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Echoes body from POST request',
      echo: body
    }),
  };

  callback(null, response);
};

Deploying to AWS Lambda

At this point, serverless.yml has all that’s needed for Serverless to deploy the service to AWS Lambda. All it takes is to type serverless deploy in the command line. When it’s completed we’ll see a summary of the service, along with the endpoints where we can call the endpoints.

Service Information
service: serverless-node-sample
stage: dev
region: ap-southeast-1
api keys:
  None
endpoints:
  GET - https://sample.execute-api.ap-southeast-1.amazonaws.com/dev/hello
  POST - https://sample.execute-api.ap-southeast-1.amazonaws.com/dev/echo

Calling the endpoints gives us the expected response:

curl https://sample.execute-api.ap-southeast-1.amazonaws.com/dev/hello

// response
{"message":"Hello World!"}
curl -X POST -d '{"message": "Echoing Hello World"}' https://sample.execute-api.ap-southeast-1.amazonaws.com/dev/echo

// response
{"message":"Echoes body from POST request","echo":{"message":"Echoing Hello World"}}~

Drawback

Currently AWS only supports Node.js v4.3, so support for ES2015 is very limited. There are ways to go around that by transpiling using Babel, but I’m not keen to have additional dependencies duplicated in multiple projects, so I’m sticking as much as possible to what is supported at the moment.

GRPC vs REST on Node.js

Performance comparsion between GRPC and REST on Node.js
grpc node.js javascript