Post

Building Serverless Apps using AWS

The services which we are going to use in this article are:

  • AWS Lambda

  • Amazon API Gateway

  • AWS Amplify

  • Dynamo DB

  • AWS Cognito

The working of the architecture is Amplify provides the continuous deployment and hosting of the static web resources which will be loaded in the user’s browser. The JavaScript which is executed in the browser sends and receives data from a public backend API built using API Gateway and Lambda functions. The Cognito Service provides user management and authentication functions to secure the backend API. Dynamo DB used to store the data sent by the API’s Lambda function.


A git-based workflow for continuous deployment and hosting of full-stack web apps is offered via the Amplify Console. In later Steps, you’ll use JavaScript to call distant RESTful APIs created using AWS Lambda and Amazon API Gateway and add dynamic functionality to these pages.

Region Selection:

We need to select a region which will support the following services:

  • AWS Cognito

  • AWS Amplify

  • AWS CodeCommit

Let suppose, I will be using us-east-2 (Ohio) for this article.

Environment Setup:

Open your windows PowerShell or the terminal with admin permissions, and install the aws-shell

pip install aws-shell

Once you are done, you need to configure the AWS.

aws configure

it will ask you for the AWS access keys, where you will be getting them from the security credentials in the account page.

There you need to create new Access keys and save them to the local file like notepad.

Enter these access key and secret access key in the terminal, and your are successful with your configuration.

Creating a repository in codecommit

aws codecommit create-repository –repository-name wild-rydes

This codecommit is a platform where you can create and manage your projects and repositories but included in AWS.

Now we have created a repository in the AWS named wild-rydes and we need to push some code into that repository. AWS provides some sample codes for us to use them.

https://github.com/aws-samples/aws-serverless-webapp-workshop.git

This is link where you can use the sample codes for building the server less webapps.

we are going to clone this repository and deploy the application.

git clone https://github.com/aws-samples/aws-serverless-webapp-workshop.git

This repository actually has many projects included, so we will just remove them unwanted projects and get the project which we wanted to use and then push that to the Code Commit repository.

git subtree split -P .\resources\code\WildRydesVue\ -b WildRydesVue

so we have created a new subtree which has the code we wanted to use, and i will create a new directory for the repository

mkdir ../wild-rydes

  • in the newly created folder

git init

git pull ../aws-serverless-webapp-workshop WildRydesVue

git remote add origin codecommit://wild-rydes

git push -u origin master

By this step we will be having the code loaded into the Code commit.

Now we need to deploy this to AWS Amplify.


Setting up AWS Amplify:

Open the AWS Amplify and click on create Amplify Hosting. since you wanted to host the existing code you can select the AWS Code Commit option for the deployment.

Select the repository which you have created in the AWS Code Commit. The branch name would be master and the App name would be wild-rydes.

Create a newprod environment for the production chain and attach the execution role with the required policies. Once you are done setting up all the required things you can click on Save and Deploy.

Once this is done, you will be getting a link where you can visit the static website without having any backend functionality.


Setting the User-management Cognito:

First install the aws-amplify/cli. To do that open the terminal and enter

npm install -g @aws-amplify/cli

to start the amplify we need to initialize that…

amplify init

1
2
3
4
5
6
Enter the name for the environment: prod
Choose your defualt editor: Visual Studio code
Select the authentication menthod you want to use: AWS Access keys
accessKeyId: 
secretAccessKey: 
region: us-east-2

Now we need to add the authentication which means the users pool for the environment which we are using.

amplify add auth

1
2
3
4
5
Do you want to use the default authentication 
and security configuration? Default Configuration
How do you want users to be able to sign in? username
Do you want to configure advanced settings? No, I am done.

On doing this step we will be getting some files in the local folder which are related to authentication.

so we need to add them and push to the repository.

git add .
git commit -m "added userpool"
git push

Go to the AWS Amplify Console and reload the page. It will rebuild the branch with the changes we have made.

If you redirect to the auth page of the website you will be getting a SignIn and SignUp page for you to login and create Account.


Setting up Dynamo DB:

we need to create a table with name Rides and the partition key to be Rideid . And we need to create a IAM role for the lambda function. with this IAM role i can give access from the lambda function to other AWS Services.

Create a IAM role with the following policies:

  • AWSLambdaBasicExecutionRole

and also add a inline policy for the access to the dynamo DB. The permission we need to give for this policy is PutItem , and add the ARN of the Table.


Setting up Lambda Function:

Create a function with name RequestUnicorn and the Runtime to be Node.js 16.x and attach the execution role.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

import { randomBytes } from 'crypto';
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import { marshall } from '@aws-sdk/util-dynamodb';

const ddbClient = new DynamoDBClient({ region: 'us-east-1' }); // Update with your desired region

const fleet = [
  {
    Name: 'Bucephalus',
    Color: 'Golden',
    Gender: 'Male',
  },
  {
    Name: 'Shadowfax',
    Color: 'White',
    Gender: 'Male',
  },
  {
    Name: 'Rocinante',
    Color: 'Yellow',
    Gender: 'Female',
  },
];

export async function handler (event) {
  if (!event.requestContext.authorizer) {
    return errorResponse('Authorization not configured', event.requestContext.requestId);
  }

  const rideId = toUrlString(randomBytes(16));
  console.log('Received event (', rideId, '): ', event);

  const username = event.requestContext.authorizer.claims['cognito:username'];

  const requestBody = JSON.parse(event.body);

  const pickupLocation = requestBody.PickupLocation;

  const unicorn = findUnicorn(pickupLocation);

  try {
    await recordRide(rideId, username, unicorn);

    return {
      statusCode: 201,
      body: JSON.stringify({
        RideId: rideId,
        Unicorn: unicorn,
        UnicornName: unicorn.Name,
        Eta: '30 seconds',
        Rider: username,
      }),
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    };
  } catch (err) {
    console.error(err);

    return errorResponse(err.message, event.requestContext.requestId);
  }
};

function findUnicorn(pickupLocation) {
  console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude);
  return fleet[Math.floor(Math.random() * fleet.length)];
}

async function recordRide(rideId, username, unicorn) {
  const params = {
    TableName: 'Rides',
    Item: marshall({
      RideId: rideId,
      User: username,
      Unicorn: unicorn,
      UnicornName: unicorn.Name,
      RequestTime: new Date().toISOString(),
    }),
  };

  await ddbClient.send(new PutItemCommand(params));
}

function toUrlString(buffer) {
  return buffer
    .toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

function errorResponse(errorMessage, awsRequestId) {
  return {
    statusCode: 500,
    body: JSON.stringify({
      Error: errorMessage,
      Reference: awsRequestId,
    }),
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  };
}

Deploy the above code and test the event with the following text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
    "path": "/ride",
    "httpMethod": "POST",
    "headers": {
        "Accept": "*/*",
        "Authorization": "eyJraWQiOiJLTzRVMWZs",
        "content-type": "application/json; charset=UTF-8"
    },
    "queryStringParameters": null,
    "pathParameters": null,
    "requestContext": {
        "authorizer": {
            "claims": {
                "cognito:username": "the_username"
            }
        }
    },
    "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
}

you will be getting a response statuscode of 201.


Setting the API Gateway:

Create a REST API with API management capabilities and we need to configure with the authorizers with the Cognito users

Click on Authorizers and give it a name, and choose the Cognito. And the token Source to be Authorization and create the authorizer

Now we need to add the Resources… Create a resources with name ride and path will be /ride and enable the API Gateway CORS

in that create a POST method and attach the lambda function RequestUnicorn.

Edit the method request and change the Authorization settings from None to wildrydes which we created in the Cognito User pool.

Now Deploy the API and the stage name to be Prod.

Make the copy of url and include that in the website code.

1
2
3
4
5
6
// src/config.js
module.exports = {
    api: {
        invokeUrl: 'your_url'
    }
}

save the edits and commit the changes…

git add .
git commit -m "API Appended"
git push

This post is licensed under CC BY 4.0 by the author.