Smart Hike – Backend

What was the project?

We worked on a platform to show nearby hikes, recommend them and advanced filters to allow users to choose their hikes in a personal manner – https://develop.dg28ry4dtil6u.amplifyapp.com/ (API used might have expired)

Backend Architecture

The application architecture uses AWS LambdaAmazon API GatewayAmazon DynamoDBAmazon Cognito, and AWS Amplify Console. Amplify Console provides continuous deployment and hosting of the static web resources including HTML, CSS, JavaScript, and image files which are loaded in the user’s browser. JavaScript executed in the browser sends and receives data from a public backend API built using Lambda and API Gateway. Amazon Cognito provides user management and authentication functions to secure the backend API. Finally, DynamoDB provides a persistence layer where data can be stored by the API’s Lambda function.

In this blog, I will discuss the Dynamic API calls over HTTP API more deeply. We used it to power our recommendations.

Recommendation flows

For User

Users set their preference on the register page and from there on see the recommended section on their home page. We define recommendation based on user’s preference about the things he or she likes.

For System

System registers user preferences on DynamoDB using the POST API and eventually gets the user preferences from the GET API on the home page.

Serverless Service Backend

We used AWS Lambda and Amazon DynamoDB to build a backend process for handling requests for your web application. The browser application that is deployed on amplify allows platforms to fetch information about users and their preferences. In order to fulfill those requests, the JavaScript running in the browser will need to invoke a service running in the cloud.

We use AWS.DynamoDB.DocumentClient in Javascript to call DynamoDB API – for updating, adding or fetching from DynamoDB.

We use async, await for call the APIs. The async and await keywords are a form of syntactic sugar for JavaScript Promises, making sequences of asynchronous tasks wrapped in Promises easier to write, read, and debug. They’re a perfect fit if you want to hold your program execution until some asynchronous action finishes.

Setting up DynamoDB

DynamoDB

Writing Lambda

exports.handler =  async function(event, context) {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2))
  return context.logStreamName
}

The Lambda function handler is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method. When the handler exits or returns a response, it becomes available to handle another event.

When you configure a function, the value of the handler setting is the file name and the name of the exported handler method, separated by a dot. The default in the console and for examples in this guide is index.handler. This indicates the handler method that’s exported from the index.js file.

The runtime passes three arguments to the handler method. The first argument is the event object, which contains information from the invoker. The invoker passes this information as a JSON-formatted string when it calls Invoke, and the runtime converts it to an object. When an AWS service invokes your function, the event structure varies by service.

The second argument is the context object, which contains information about the invocation, function, and execution environment. In the preceding example, the function gets the name of the log stream from the context object and returns it to the invoker.

The third argument, callback, is a function that you can call in non-async handlers to send a response. The callback function takes two arguments: an Error and a response. When you call it, Lambda waits for the event loop to be empty and then returns the response or error to the invoker. The response object must be compatible with JSON.stringify.

For asynchronous function handlers, you return a response, error, or promise to the runtime instead of using callback.

I would recommend going through the official documentation for more details about writing lambda function in Javascript.

Here in our use-case implementation, we use switch cases based on the API routes(will be defined later) to call relevant DynamoDB APIs like get, put etc. In the json object sent with the function call, we inform AWS about the Table Name, Key(in case of Get) and Payload(in case of POST or PUT).

The entire function is written inside a try – catch blog to capture the Error Exception.

const AWS = require("aws-sdk");

const dynamo = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event, context) => {
  let body;
  let statusCode = 200;
  const headers = {
    "Content-Type": "application/json"
  };

  try {
    switch (event.routeKey) {
      case "GET /users/{userid}":
        body = await dynamo
          .get({
            TableName: "Users",
            Key: {
              userId: event.pathParameters.userid
            }
          })
          .promise();
        break;
      case "GET /users":
        body = await dynamo.scan({ TableName: "Users" }).promise();
        break;
      case "POST /users/{userid}":
        let requestJSON = JSON.parse(event.body);
        await dynamo
          .put({
            TableName: "Users",
            Item: {
              userId: requestJSON.userid,
              city: requestJSON.city,
              name: requestJSON.name,
              backpacking: requestJSON.backpacking,
              pet: requestJSON.pet,
              trails: requestJSON.trails,
              walking: requestJSON.walking,
              biking: requestJSON.biking,
              wheelchair: requestJSON.wheelchair
            }
          })
          .promise();
        body = `Post item ${requestJSON.userid}`;
        break;
      default:
        throw new Error(`Unsupported route: "${event.routeKey}"`);
    }
  } catch (err) {
    statusCode = 400;
    body = err.message;
  } finally {
    body = JSON.stringify(body);
  }

  return {
    statusCode,
    body,
    headers
  };
};

API Contracts to interact with the Database.

  • GET 
  • Path: /users
StatusResponse body
200{“Items”:[{“city”:”myitem”,”name”:12345,”userId”:”abcdef234″},{“userId”:”sid123″}],”Count”:2,”ScannedCount”:2}
401Unauthorized access

  • GET
    • Path: /users/{userid}
StatusResponse body
200{“Item”:{“userId”:”sid123″}}
401Unauthorized access
404Key not found
  • POST
    • Path: /users/{userid}
StatusResponse body
201Items added
401Unauthorized access

Results

Our team won both the best tech and best pitch award at UCI MCS Capstone showcase 2022(picture below). More importantly, I got exposure to various service by AWS and also how to work on them. Please let me know if you have quesitons.

https://develop.dg28ry4dtil6u.amplifyapp.com/

Reference

  1. https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html
  2. https://aws.amazon.com/getting-started/hands-on/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: