How I developed, tested and automatically deployed an AWS Lambda for the first time

Updated: Jul 28

So I was just a beginner in this topic and I didn’t realize what I needed to know to develop an AWS Lambda. I wanted to do it well so my main focus was on code, test, CI/CD, security, availability, reliability and learning to use something I’ve never used before…. it's a lot, I know, right?…but what about infrastructure and all possible combinations of settings needed for it to work correctly?… (developer anxiety coming).

…I took my first cup of coffee…



Development

I started searching for tools to speed up my work and Serverless Framework came out. In my own words, it is a cloud agnostic framework that provides you with a comprehensive, easy-to-use, non-invasive and integrable method that allows you to create, deploy and destroy your serverless application. The only thing you need to have is an AWS account, a Serverless Framework account (mine was a free one), setup Serverless CLI, and start your project in your preferred language (I chose NodeJS based on AWS Lambda cold starts, my lambda would be off most of the time). Then execute:


>serverless create --template aws-nodejs --path path/to/your/app
>npm init

And voilá. You have your application's main skeleton. Then go to the “serverless.yml” file (this will be the core of your lambda, the main piece of configuration). I’ll show you something simple but not too basic (to spice this thing up).

Let’s say we have a little market, a few items to sell and an inventory containing its basic information. We will need something to get and maybe update information of an item if we sell it. Let’s practice!


So…

  1. Give a unique name to your lambda.

  2. I told you that it was cloud agnostic, right? So tell it what your requirements are.

  3. Don’t worry! We will talk about this later. Just for testing purposes.

  4. Define which resources your lambda will access and the actions it will perform on them. In this case, a “Market”. DynamoDB is the only resource that my lambda will access, and it only can read and update items from that table.

  5. Your lambda needs “something” through which it can be accessed. In this case, Serverless Framework will expose an API through API Gateway with 2 methods: The first one will get an item and the second one will update an item. The handlers will be the JS functions containing the implementation for those methods to work properly.

  6. Define the resources that you will create as if you were using CloudFormation.

At the moment, the only dependency you will need is aws-sdk, then you can save efforts on the implementation of accessing the most important AWS services through JS code. In order to assure you can work locally, you will need to install the AWS-CLI and configure your AWS credentials so you can give Serverless access to manage the application’s entire lifecycle.

Now is time to implement the Handler class. I’ll show you just one method but the concept is the same for the other one.



You should note that your API is returning HTTP status codes like any other API would, and you can configure the response body however you want. Besides, since Lambda is part of an event-driven architecture, it makes sense that it receives an event when it is invoked, then it must be parsed to a JSON object so that the body can be processed properly. Another point to consider is that AWS service invocations are asynchronous, so “async — await” joins must be performed depending on what is expected in the Lambda behavior.

The remaining code is not shown here to keep this post less difficult to read. You can find a complete implementation in the GitHub repo at the end of this document, but with what I’ve shown and implemented with the DynamoDB integration, we can deploy our Lambda (but wait for the next section), and we still don’t touch AWS, we just coded. Awesome uh?.

…I realized I deserved a second cup of coffee…



Testing


We can deploy now, but that’s not ideal. Testing must be mandatory in every development process.

I decided to use Jest to develop my unit tests and to mock my AWS connection, I used aws-sdk-mock.

But first, let’s test our lambda locally, kind of like deploying it on our machine. To do that, we need to configure Serverless to work locally. So we need the serverless-dynamodb-local dependency to manage local Dynamo databases and the serverless-offline dependency to enable Serverless to work locally. Besides, we need to use both dependencies as plugins in our “serverless.yml”. Just add the following lines to the end of the file:




You should tell serverless in which environment you want to install dyanamodb-local:




Then the local Dynamo database will need to be populated:


>serverless dynamodb install
>serverless dynamodb start --migrate

The last command will start a local DynamoDB instance available in http://localhost:8000/shell in dev stage, if you decided to install it in another stage, you should use the following command (for test stage, for example):


>serverless dynamodb migrate --stage test

Insert some items following batchWriteItem syntax (you can find some queries on the example repo at the end of this article):



You can review all inserted items executing the following command:


aws dynamodb scan — table-name Market — endpoint-url http://localhost:8000

There is one thing that I see as a disadvantage of the AWS-SDK which is that it is mandatory to specify which Dynamo connection to use directly in the code implementation, but we can design some good strategies to do it properly. To keep this example, manually change your DynamoDB connection as follows:



Remember that in this article I don’t publish the getItem function code, you can find that in the GitHub repo at the end of this post.

Now we can start Serverless in offline mode, and invoke our lambda getItem method locally (be careful with the json format).


Start Serverless in offline mode if you want to execute the request using an external tool like Postman or SoapUI
>serverless offline

Or invoke the lambda directly in your terminal
>serverless invoke local --