Serverless Backend using AWS Lambda: Hands-on Guide

This article was originally written by Rajat S on Medium. Reposted with permission from Bits and Pieces.


Serverless architecture refers to the concept where you give your backend logic to a third party vendor’s server architecture. Doing this allows a developer to focus more on their app and not worry about the server at much.

The word Serverless may misguide one into thinking that there is no server attached to the app. In truth, it just means that the developer does not have to worry about maintaining the backend logic. That responsibility is handed over to the third party vendors like AWS, Azure, and Google.

Serverless Computing can be divided into two variants:

  • Backend-as-a-Service (BaaS)
  • Function-as-a-Service (FaaS)

With BaaS, the backend logic will be run on a third party vendor. The developer does not need to maintain the servers or the infrastructure which runs the backend services. The developer only needs to pay a subscription to the vendor. The BaaS runs on a shared infrastructure, and the same backend service will be used by multiple applications.

Technologies like AWS Lambda and Microsoft Azure Functions fall under the FaaS category. Here, the developers can implement their own backend logic and run them within the serverless framework. The vendor will handle the operation of the backend logic in a server, along with scalability, reliability, and security aspects.

In this post, we will use AWS Lambda to create a serverless API. We will go through the steps to build the first two endpoints of a REST API and store the data in DynamoDB.


Turn components into APIs with Bit

Bit is an open source tool that turns any reusable piece of JS code into an API which can be discovered, shared and developed from any project. Give it a try.


Getting Started

In order to interact with Amazon Web Services (AWS), we need to get an access key and a secret key along with the permissions to deploy an app.

If you do not already have an account in AWS, create one here. Once you have successfully registered, you will be able to see the AWS Console as shown below:

We can now get the keys and permissions by switching to the identity and access management.

Click on add User button and create a new user with any User name that you want to give. Make sure to enable the Programmatic Access. Then click on Next: Permissions.

Select, Attach existing properties directly and select the AdministratorAccess.

Click on Next: Review and then Confirm. You will then have created a user in AWS and will now have an Access Key ID and Secret Access Key. This is unique for each user, so I won’t be showing mine to you here.

Now open a command terminal on your computer and install the awscli. Check out the official docs of AWS CLI to see how to install it depending on your operating system.

Once installed, we need to configure the AWS CLI by running the command:

$ aws configure

The terminal will then ask you to enter the Access Key ID and the Secret Access Key. The terminal will also ask you to enter the Default Region Name and Default Output format. You can leave the last two inputs empty.


Serverless Framework

The serverless framework allows one to build apps comprised of microservices that run in response to events, auto-scale for you, and only charge you when they run. This lowers the total cost of maintaining your apps, enabling you to build more logic, faster.

Let’s globally install this framework into our system using NPM:

$ npm install -g serverless

Once installed, we can run the serverless framework in the terminal by running the command:

$ serverless

This command will display all the available commands that come with the serverless framework.

Note: You can also use the shorthand syntax for serverless by typing sls.


Deploy a Node Function to AWS Lambda

In this section, we will see how to configure a Lambda function, add some simple code to it, and deploy it to AWS Lambda.

First let’s create a folder where we can store all the code related to this post.

$ mkdir less-app

You can give any name you that you want to give to this folder. I have named my folder as less-app for “Serverless-app”.

Next create serverless.yml file inside this folder.

$ touch serverless.yml

Here we will define a couple of fields such as serviceprovider, and functions as shown below:

service: less-app
provider:
  name: aws
  runtime: nodejs8.10
functions:
  helloWorld:
    handler: handler.run

Here, the handler refers to the handler.js file which in turn needs to export the run function. We have not yet created this file, so lets do that:

$ touch handler.js

Inside this file, we need to export the run function. This function has three parameters: eventcontext, and callback.

The event is an object that contains all the necessary request data. The context object contains AWS-specific values like AWS request ID, log group name, and so on. The callback is a function and should be invoked with an error response as the first argument or a valid response as the second argument.

We can now deploy this run function using the deploy command of the serverless framework as shown below:

$ sls deploy

This command takes a couple of minutes to finish. You should get something like this in your terminal:

Here, the serverless framework will pack the code into a ZIP file and deploy it with a CloudFormation stack.

We can now invoke the Lambda function directly using the invokecommand of the serverless framework.

$ sls invoke --function helloWorld

Your output should look something like this:

The invoke command also lets us log out a debug statement. Go to the handler.js file and insert a console.log statement as shown below:

module.exports.run = (event, context, callback) => {
  console.log("Hello World")
  callback(null, "Hello World")
}

To display this statement in our output, we need to add a --log flag to our invoke command.

$ sls invoke --function helloWorld --log

Alternatively, we can also run the command sls logs to print only the past log statements.

$ sls logs --function helloWorld

If you do not want to use the callback function, you can return a Promise as well.

module.exports.run = (event) => {   
  return Promise.resolve("Hello world"); 
}

You might now think that we have to redeploy the whole thing, which takes some time. But we can also just deploy the function part. This quickens things for us as it skips the CloudFormation part and replaces the ZIP for the specific function.

$ sls deploy function --function helloWorld

We can also declare our function as asynchronous and return the desired result.

module.exports.run = async (event) => {
  return "Hello world";
}

You can re-deploy this function and invoke it, and it should work perfectly.


Attaching HTTP Endpoint to AWS Lambda Function

Using the serverless framework, we can attach an HTTP endpoint to a Lambda function. To do this, we need to define the path and method in the serverless.yml file.

functions:   
  helloWorld:     
    handler: handler.run     
    events:        
      - http:            
          path: /           
          method: get

But now that we are using HTTP, we need to make a couple of changes to our function because the expected response needs to be an object that contains the statusCode and the body. Here, I am going to turn my response into JSON.stringify() as shown below:

module.exports.run = async (event) => {
  return{
    statusCode: 200,
    body: JSON.stringify({
      message: "Hello World"
    })
  }
}

Run the sls deploy command in the terminal. Unlike previous cases, we cannot add the --function flag here because the configuration has been changed.

Once deployed, we can use curl to invoke the published HTTP endpoint.


DynamoDB

DynamoDB is a fully managed NoSQL database service by AWS that provides fast and predictable performance with seamless scalability. It allows us to create a database table that can store and retrieve any amount of data, and serve any level of request traffic.

Lets see how to deploy a DynamoDB table. In order to add a DynamoDB table to our app, we need to add a resources section to our serverless.yml file.

resources:   
  Resources:     
    HeroesTable:       
      Type: 'AWS::DynamoDB::Table'       
      Properties:        
        TableName: heroes        
        AttributeDefinitions:           
          -             
            AttributeName: id             
            AttributeType: S         
        KeySchema:           
          -             
            AttributeName: id             
            KeyType: HASH         
        ProvisionedThroughput:            
          ReadCapacityUnits: 1           
          WriteCapacityUnits: 1

We then run sls deploy command in our terminal. This will tell CloudFormation to set up our table.

Go to the DynamoDB Console and you will that a table named heroes has been automatically created. We can add more Attributes to this table later on. Right now, I have only added one attribute named id to create the table.


Storing Data in DynamoDB

Before we can start storing data in our DynamoDB, we need to set some permissions for the Lambda function to have write access.

Inside the serverless.yml file, remove the contents of the functions section completely and create a new function called createHero as shown below:

functions:
  createHero:
    handler: create.run
      events:
        - http:
            path: heroes
            method: post

But the Lambda function does not have the permissions to interact with our DynamoDB table by default. So we need to give these permissions to our Lambda function. This can be done with the help of iamRole.

The serverless framework already uses iamRole under the hood. We need to use iamRoleStatements to extend the permissions for the specific iamRole.

In your DynamoDB console, click on the table to open its overview. In the Table Details section, you should find the Amazon Resourse Name (ARN). Enter its content as Resource inside the iamRoleStatements in the serverless.yml file as shown below:

provider:
  name: aws
  runtime: nodejs8.10
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:PutItem
      Resource: "<enter your Amazon Resource Name here>"

We also have to create the function that will actually send the data to our DynamoDB table. So create a new file named create.js.

$ touch create.js

Inside this file, add a function that will return a response.

module.exports.run = async (event) => {
  const data = JSON.parse(event.body);
  return {
    statusCode: 200,
    body: JSON.stringify(data)
  };
};

To let this function interact with DynamoDB, we need to import the aws-sdkand instantiate a documentClient() inside the serverless.yml file:

const AWS = require("aws-sdk");
const client = new AWS.DynamoDB.documentClient();

Along with this, create a new const named params as shown below:

module.exports.run = async (event) => {
  const data = JSON.parse(event.body);
  const params = {
    TableName: "heroes",
    Item: {
      id: "hero1",
      name: data.name,
      checked: false
    }
  };
  await client.put(params).promise();
  return {
    statusCode: 200,
    body: JSON.stringify(data)
  };
};

There is one issue in this code, every new item that we add to our table will have the same id. To solve this, we can add the uuid package to our code. This package will generate a new id for every new request.

Create a new file named package.json:

$ touch package.json

Inside this file, write the following code:

{
  "name": "less-app",
  "private": true
}

Then install the uuid package using NPM/Yarn:

$ npm install --save uuid

Finally, import this package into the create.js file:

const uuid = require("uuid/v4");

Also re-write the id property inside the Item object using the uuid package as shown here:

Item: {
  id: uuid(),
  name: data.name,
  checked: false
}

With that, we have set up everything and can redeploy this using serverless.

$ sls deploy

Once deployed we can use curl to create an entry into the DynamoDB table as shown below:

$ curl -X "<POST URL>" --data '{"text": "batman"}'

Check in the AWS console to see if this submission was recorded.


Conclusion

I hope this post helped you understand the basics of AWS Lambda and understand why serverless architecture is worth trying out.

Serverless Architecture like AWS Lambda will simplify the maintenance of backend systems while giving cost benefits for handling all sorts of different user behaviors.

You should especially consider implementing serverless architecture if you have a small number of functions that you need hosted. If your application is more complex, a serverless architecture can still be beneficial, but you will need to build your app in a different way.

This may not be feasible if you have an existing application. It may make more sense to migrate pieces of the application into serverless functions over time.


Further Reading

Rajat S

Former Content Writer At GeekyAnts. Now writing Tech Articles for fun while studying at UTDallas. Loves DC Comics, Marvel Movies, and RPG Games.