Building a Serverless Hogwarts Sorting Service with Node and AWS Lambda
Tags: node, serverless, aws, codenewbie
Published: 2020-12-31 13:27:33


Being on the job hunt for my first web developer role I realized that knowledge and experience with serverless architecture are required and critical for most positions. 

During my Full-Stack Web Development training program, I already built an authorization server with the Serverless Toolkit and AWS Lambda. But, only practice makes perfect, and I wanted to have another project to cement what I've learned. 

As I'm currently re-reading the Harry Potter book series, I had the idea to build a *Sorting Service* that assigns a user to a random Hogwarts house.  Besides, it should show other students already living in this house. To fetch data about characters, I used this [Harry Potter API](https://hp-api.herokuapp.com/).

With `Node`, `Serverless Toolkit`, `AWS Lambda`, and `API Gateway` we'll build a simple API that returns a random Hogwarts house as well as some housemates.

###Prerequisites
Before we can start with the actual project, we need to have `Node` and `npm` installed, as well as [create and activate our own AWS account](https://aws.amazon.com/de/premiumsupport/knowledge-center/create-and-activate-aws-account/).  

###Installing the Serverless Toolkit and Creating a Serverless Service
One reason we'll use the `Serverless Toolkit` here is, that we'll have to install `axios`, later on, to fetch data from the Potter API and `Serverless` lets us easily do this. I also like `Serverless` when working with `AWS` because it's easier to implement code or deploy functions locally without having to work with the inline code editor in `AWS`. 

To install `Serverless` globally and create a `Node` template, we run the following commands:
```
npm install -g serverless
serverless create --template aws-nodejs --path random-sorting-service
cd random-sorting-service
npm init // creates a package.json
```
Our directory structure should look like this now:
```
📦random-sorting-service
┣ 📜.gitignore
┣ 📜handler.js   // here we'll write our sorting function
┣ 📜package.json
┗ 📜serverless.yml // here we'll configure our function
```
###Configuring AWS Credentials

To make sure the `Serverless Toolkit` can interact with `AWS` we need to configure and add our `AWS` credentials. We'll create an access key that allows us to configure the `Serverless Toolkit` and enable it to deploy functions directly to `AWS`.

To achieve this, we have to follow these steps:

1. Log in to our newly created `AWS` account by clicking on "Sign in to the Console".
2. Click on our username in the upper right-hand corner and select "My Security Credentials".
3. Select "Access keys" and click the "Create New Access Key" button.
4. Download the new access key (it contains an access key ID and a secret access key) and store it in a safe place.

Back in our terminal, we run the following command (using our own credentials):
```
serverless config credentials --provider aws --key ACCESS_KEY_ID --secret SECRET_ACCESS_KEY
```

###Configuring and Setting Up `serverless.yml`

We now open the `serverless.yml` file in our code editor and declare our sections as follows:

![Screenshot of serverless.yml set up](https://dev-to-uploads.s3.amazonaws.com/i/htxvh453g7wz3e7p086l.png)

###Writing the Lambda Function
####The Random House Generator
We open the `handler.js` file, delete the dummy code, and replace it with the following:

![Screenshot of code to get a random house](https://dev-to-uploads.s3.amazonaws.com/i/b2g7s3t8k3hf2v7jfoht.png)

Let's take a closer look at the code example above: 

The function is named `getRandomHouseAndHousemates` because in [Step 2](#getting-data-from-3rd-party-api) we'll also fetch data of associated house members.

First, we put all four Hogwarts houses inside an array. Then, we create a random `index` that is used to display a random house. We use the `Math.random()` function that returns a floating-point, pseudo-random number between 0 (inclusive) and 1 (exclusive). We also need the `Math.floor()` function to get a whole number and not a floating type. Because `Math.floor()` returns a number representing the largest integer less than or equal to the specified number, if passing `Math.random()` as a parameter to `Math.floor()`, we get a whole number. 

But there is one problem left we have to handle: `Math.floor()` is always rounding down to the nearest decimal, so every decimal between 0 and less than 1 (and that is what `Math.random()` is returning) will always be 0. To get a random number between 0 and 4 (the length of our `houses` array) we have to multiply the `Math.random()` by `randomHouse.length`. This `index` now can be applied to get a random house.

Because we'll have cross-origin requests if we'd want to use our service within another app, we also have to add the `Access-Control-Allow-Origin` header to the return statement in `handler.js`

####Testing the Random House Generator

Before deploying, we can test our function locally. For now, it should only return a random Hogwarts house. From inside our project directory, we run:
```
serverless invoke local --function getRandomHouseAndHousemates
``` 
If everything works as expected, we should see something like this in our terminal:
```
{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*"
    },
    "body": "{\"randomHouse\":\"Hufflepuff\"}"
}
```

If that works, we can go on and implement the additional logic to fetch and return data about our new housemates.

####Getting data from 3rd party API

In order to be able to make requests to the [Harry Potter API](https://hp-api.herokuapp.com/), inside our `random-sorting-service` folder, we run:
```
npm install axios
```

In our `handler.js`, we add the code we need to get and return our housemates' data:

![Screenshot of completed code](https://dev-to-uploads.s3.amazonaws.com/i/10nptj5e0x78fljr1fl4.png)

Let's take a closer look at this code example, too: 

In our `handler.js`, we have to require `axios`. Then, we use the `randomHouse` variable declared in the first step to generate the URL to reach the API endpoint we need. Subsequently, we make the API request. As `getRandomHouseAndHousemates` already is an `async` function, we'll use a `try...catch` block to handle possible request errors with `axios`. 

Because we plan to return only students from the same house, but the given endpoint returns teachers as well as students, we have to apply the `filter()` method to the API response as well. 

####Testing the API Request

Now it is time to test our function locally again before we finally can deploy:
```
serverless invoke local --function getRandomHouseAndHousemates 
```
We should see something similar to that in our terminal:
```
{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*"
    },
    "body": "{\"randomHouse\":\"Ravenclaw\",\"housemates\":[{\"name\":\"Cho Chang\",\"species\":\"human\",\"gender\":\"female\",\"house\":\"Ravenclaw\",\"dateOfBirth\":\"\",\"yearOfBirth\":\"\",\"ancestry\":\"\",\"eyeColour\":\"brown\",\"hairColour\":\"black\",\"wand\":{\"wood\":\"\",\"core\":\"\",\"length\":\"\"},\"patronus\":\"swan\",\"hogwartsStudent\":true,\"hogwartsStaff\":false,\"actor\":\"Katie Leung\",\"alive\":true,\"image\":\"http://hp-api.herokuapp.com/images/cho.jpg\"},{\"name\":\"Luna Lovegood\",\"species\":\"human\",\"gender\":\"female\",\"house\":\"Ravenclaw\",\"dateOfBirth\":\"13-02-1981\",\"yearOfBirth\":1981,\"ancestry\":\"\",\"eyeColour\":\"grey\",\"hairColour\":\"blonde\",\"wand\":{\"wood\":\"\",\"core\":\"\",\"length\":\"\"},\"patronus\":\"hare\",\"hogwartsStudent\":true,\"hogwartsStaff\":false,\"actor\":\"Evanna Lynch\",\"alive\":true,\"image\":\"http://hp-api.herokuapp.com/images/luna.jpg\"}]}"
}
```
###Deploying to AWS

If that works, we can deploy:
```
serverless deploy
```
The `Serverless Toolkit` will now hand over our function to `AWS` using the infrastructure requirements we provided in our `serverless.yml`. When successfully finished, we should see this information in our terminal: 

![Screenshot of serverless deploy output](https://dev-to-uploads.s3.amazonaws.com/i/8mbj6pwz5pmtwq7himuu.png)

Under the `endpoints` output, we can see the (`API Gateway`-) generated URL we need to invoke our function. The path `/dev/house` not only contains the trailing `/house` as specified in our `serverless.yml` but also contains `/dev/` as Serverless deploys to the `dev` environment by default (we could also deploy to production by using the `--stage` flag: `serverless deploy --stage prod`).

Now, we can check if all files have been deployed to `Lambda` successfully. In our AWS Management Console, we search for `Lambda` and select `getRandomHouseAndHousemates`. We should see something like this: 

![Screenshot service at AWS Lambda](https://dev-to-uploads.s3.amazonaws.com/i/kxqwy9ox55ervxt89c3z.png)

###Testing the Endpoint
We can try out our endpoint by simply copying it from the terminal and paste it into the address bar of our browser. 

![Screenshot browser output](https://dev-to-uploads.s3.amazonaws.com/i/cmszo6bw95vekumf0402.png)

If we use Postman, we get a neater looking result:

![Screenshot Postman output](https://dev-to-uploads.s3.amazonaws.com/i/wvmif7rf4jnirzh0el98.png)


Now that our serverless function is up and running, we could access our newly created API from any web application (I'm planning to build a Sorting Hat App with React and TypeScript, but this is another story...). 

Find the completed code of this [Random Sorting Service on GitHub](https://github.com/evagrean/random-sorting-service).