Building A Serverless Backend API Part 2

Oct 31, 2019

 

building-serverless-backend-2

Before we get started…

  • Check out Part 1 to get set up.


What we’ll go over

  • Updating the Lambda function to handle other types of requests
  • Updating the API Gateway to accept different request methods
  • Creating Reusable Resources
  • Adding Thundra Monitoring to our project

Adding additional Endpoints and Methods

Currently, the only request that we can make is a POST request. To add additional endpoints and request types, just add additional elements to the Events array that was started in Part 1. It will look like this:

Events:
 - http:
     path: <Pathway>
     method: <METHOD>
     cors: true
 - http:
     path: <Another-Pathway>
     method: <ANOTHER-METHOD>
     cors: true

Each of these endpoints will trigger the Lambda function they’re nested under, so we need to make sure that our Lambda can handle those request methods.

Updating our Lambda 

With the additional endpoints and methods set up, we need to make sure our Lambda function is equipped to handle those events. This tutorial is going to stay limited to GET(all), PUT, DELETE, and GET(by ID) requests, in addition to our already programmed POST request. 

How to structure the Lambda?

There are several different ways to structure our Lambda, but for this tutorial, we’re going to stick to nested if...else statements. In the end it will look something like this:

if (event.path === '/path') {
 if (event.httpMethod === 'GET') {
   //GET logic
 } else if (event.httpMethod === 'POST') {
   //POST Logic
 }
}

GET (all records)

This method can greatly reduce the performance of your app if you have a lot of records, so use it sparingly.

API Endpoint

1

Lambda

2

We’re using the scan method on the document client here. This will scan every entry for items that match the parameters. In this case, everything will match, this returning every record.

PUT

API Endpoint

3

The {id} portion of the path is how you will grab the ID — keep a note of this for later.

Lambda

Within the core logic of our Lambda, we first need to determine the value of the ID parameter. This is expressed through
 

event.pathParameter.<parameter>

 Keep that in mind for the remaining methods. Pass that variable into a new function:

4

This one is a little more complex so I’ve added a few more attributes to make it (hopefully) clearer.

  • Key: The primary key of the record in question
  • UpdateExpressions:
"set #<TableHeaderName> = :<PassedVariable>, <...repeat for other variables>"
  • ExpressionAttributeValues: This is where you set the table attribute name. This must match the column header you have in your DynamoDB Table
  • ExpressionAttributeValues: this is the value you are updating to.

DELETE

API Endpoint

5

Lambda

Grab the ID parameter, and pass that variable into a new function:

6

Again, pretty simple. We just reference the record ID and tell the database to delete it.

GET (by ID)

This version of GET requires a little more work, but not much. We’re going to grab the ID using a path parameter, which will take a slight alteration to our api endpoint.

API Endpoint

7

Lambda

Grab the ID parameter, and pass that variable into a new function:

8

In return we get the record corresponding with the ID we initially referenced.

Reusability

It’s really kind of amazing how simple it is to create an API using the Serverless Framework. As easy as it is, however, it can still be a pain if you have to do it frequently. One key feature of the Serverless framework is the implementation of Environment Variables. Environment variables are just like any other variable — they are dynamic and can be referenced again and again. What makes them very handy within the Serverless Framework is that they can dynamically create and access resources as needed, making your serverless stack more autonomous. 

To start, we’re going to update a few things:

  • provider.profile: aws-profile ➡️ ${opt:profile, “aws-profile”}
  • provider.region: us-west-2 ➡️ ${opt:region, “us-west-2”}
  • provider.stage: dev ➡️ ${opt:stage, “dev”}

What are we doing here? Instead of hardcoding the values, we are allowing users to specify their values in the form of:
 

sls deploy --profile client-profile --region us-east-1 --stage prod

If nothing is passed through as an override value, the default values (aws-profile, us-west-2, dev) are used.

Making things dynamic

Now that we have some user-generated values coming into our serverless.yml, let’s update some of our resources to reflect them. First, we’re going to create a ‘custom’ property underneath our ‘provider’ property. Within it, we’re going to create a property called DynamoDBTableName, and assign it a dynamic value.
 

DynamoDBTableName: ${self:service}-thundra-thunder-${self:provider.stage}

What’s going on here?

  • The variables are marked with the ${} syntax.
  • ‘self:’ references a value that will be found within this serverless.yml file.
  • Anything following ‘self:’ denotes the path to the value that should be referenced, similar to how you would in a nested JSON.

By making changes like this, we can create entirely new resource stacks with the same structures for different stages or regions, all by typing out a few additional commands in the terminal.

Now we just need to update our table name we made previously to reference our newly created dynamic table name. It should look like this:
 

TableName: ${self:custom.DynamoDBTableName}

 
Cross-account Reusability

If you quickly scan through your serverless.yml, you’ll notice that your AWS Account ID is still hard-coded. Currently, the Serverless Framework does not have the ability to dynamically fill in an AWS account ID, but the ‘serverless-pseudo-parameters’ plugin grants us that functionality.

To add the plugin, run:

sls plugin install --name serverless-pseudo-parameters

 Then in under provider:iamRoleStatements:Resource, you can convert you account ID to:
 

#{AWS::AccountId}

With that change made, this serverless.yml can be reused across multiple accounts, and will look something like this:

9

Adding Thundra Monitoring

Adding Thundra to a project that uses the Serverless Framework is very easy. First, we’re going to install the Thundra plugin by running 
 

sls plugin install --name serverless-pseudo-parameters


Make sure the plugin is the first listed.

After that, all we need to do is set up an environment variable for our Thundra API Key. Under functions:api:environment, add another environment variable:

thundra_apiKey: <API Key>

 10

This sets up automatic wrapping for our functions and lets us take advantage of Thundra’s Monitoring tools. If you hop into your Thundra Dashboard, you’ll be able to take a deep dive into your app’s metrics.

11

12

13

14