Build a CRUD Rest API from Scratch with AWS API Gateway, Lambda and Dynamodb using Nodejs

Build a CRUD Rest API from Scratch with AWS API Gateway,  Lambda and Dynamodb using Nodejs

Introduction

In this article we will focus in special AWS services who want to fast build and deploy endpoints, to GET/POST/PUT/DELETE information on a database regarding data of some application. The importance of server-less application are getting higher demand each day and aws available services offers a cheap and secured environment, so that the reason so many huge companies are migrating great part of their infrastructure to the cloud. We are using three awesome services API Gateway, Lambda and DynamoDB.

What is AWS API Gateway ?

Amazon API Gateway is a service provide by AWS that is used to create, publish, maintain, monitor & secure various API such as Rest APIs , HTTP & web socket at any scale. As an API developer you can create APIs that can access either AWS or any other web services along with the data store in the AWS cloud.

api.png

Lets start with Demo

Aim: To create an API with CRUD operation. Services Used -:

  • DynamoDB

  • Lambda

  • API gateway

  • IAM Service

Creating the DynamoDB Table

We will be using AWS DynamoDB a NoSQL database service to store data. DynamoDB is a fully managed, key-value, and document database that delivers single-digit-millisecond performance at any scale. Login to your AWS console and search for DynamoDB service.

  • Click on create table and enter table name, primary key always unique.

  • If you want to retrieve the data from dynamo db we should write the query to get the data by passing the key parameter.

  • If you want to retrieve the data you can retrieve it by using primary id.

Screenshot 2022-02-28 at 12.49.39 AM.png I am creating table which save the user data: -

  • Table name : member_table

  • Partition key : MemberId (String)

Create a Role & Policy

Before we go for Lambda to be able to access DynamoDB we need to give Lambda permission. Let’s create a new Policy.

  • Open AWS Console, type IAM in the search bar, and hit enter.

  • you can create role.

  • After clicking create role select AWS service and choose the service as lambda and click on next.

Screenshot 2022-02-28 at 1.01.40 AM.png

  • In next page attach policy search for AMAzonDynamoDBFullAccess, api_excute, cloudfullacess, select that policy and click on next.

  • Enter role name and click create role.

  • Now attache this role to lambda function under the execution role select IAM role which you have created.

Create a Lambda Function

AWS Lambda is event-driven computing rather than an imperative model. Events-driven computing responds to events when the event source happens, it triggers the lambda function. The event source could be a request to an endpoint which we will go through later using API Gateway.

Search for lambda service

  • Double click on the lambda service and click on create function

  • There you select an author from scratch, give some function name and select the language as Nodejs

  • After selecting the required parameters click on create function. then you will be getting a code editor. there you write your nodejs code for get/put data from dynamo DB.

Screenshot 2022-02-28 at 1.08.04 AM.png

  • Function name: users_details_api

  • Runtime: Node.js 14.x

  • Execution role: Use an existing role

  • Role name: myServerlessRole

The handler method is the method that will be executed when the lambda function is invoked. This function takes in two objects, event and context.

The next code is already tested and it’s working

const AWS = require('aws-sdk');
AWS.config.update( {
  region: 'ap-south-1'
});
const dynamodb = new AWS.DynamoDB.DocumentClient();
const dynamodbTableName = 'member_table';

const userPath = '/user';
const usersPath = '/users';

exports.handler = async function(event) {
  console.log('Request event: ', event);
  let response;
  switch(true) {

    case event.httpMethod === 'GET' && event.path === userPath:
      response = await getUser(event.queryStringParameters.MemberId);
      break;
    case event.httpMethod === 'GET' && event.path === usersPath:
      response = await getUsers();
      break;
    case event.httpMethod === 'POST' && event.path === userPath:
      response = await saveUser(JSON.parse(event.body));
      break;
    case event.httpMethod === 'PATCH' && event.path === userPath:
      const requestBody = JSON.parse(event.body);
      response = await modifyUser(requestBody.MemberId, requestBody.updateKey, requestBody.updateValue);
      break;
    case event.httpMethod === 'DELETE' && event.path === userPath:
      response = await deleteUser(JSON.parse(event.body).MemberId);
      break;
    default:
      response = buildResponse(404, '404 Not Found');
  }
  return response;
}

async function getUser(MemberId) {
  const params = {
    TableName: dynamodbTableName,
    Key: {
      'MemberId': MemberId
    }
  }
  return await dynamodb.get(params).promise().then((response) => {
    return buildResponse(200, response.Item);
  }, (error) => {
    console.error('Do your custom error handling here. I am just gonna log it: ', error);
  });
}

async function getUsers() {
  const params = {
    TableName: dynamodbTableName
  }
  const allUsers = await scanDynamoRecords(params, []);
  const body = {
    users: allUsers
  }
  return buildResponse(200, body);
}

async function scanDynamoRecords(scanParams, itemArray) {
  try {
    const dynamoData = await dynamodb.scan(scanParams).promise();
    itemArray = itemArray.concat(dynamoData.Items);
    if (dynamoData.LastEvaluatedKey) {
      scanParams.ExclusiveStartkey = dynamoData.LastEvaluatedKey;
      return await scanDynamoRecords(scanParams, itemArray);
    }
    return itemArray;
  } catch(error) {
    console.error('Do your custom error handling here. I am just gonna log it: ', error);
  }
}

async function saveUser(requestBody) {
  const params = {
    TableName: dynamodbTableName,
    Item: requestBody
  }
  return await dynamodb.put(params).promise().then(() => {
    const body = {
      Operation: 'SAVE',
      Message: 'SUCCESS',
      Item: requestBody
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('Do your custom error handling here. I am just gonna log it: ', error);
  })
}

async function modifyUser(MemberId, updateKey, updateValue) {
  const params = {
    TableName: dynamodbTableName,
    Key: {
      'MemberId': MemberId
    },
    UpdateExpression: `set ${updateKey} = :value`,
    ExpressionAttributeValues: {
      ':value': updateValue
    },
    ReturnValues: 'UPDATED_NEW'
  }
  return await dynamodb.update(params).promise().then((response) => {
    const body = {
      Operation: 'UPDATE',
      Message: 'SUCCESS',
      UpdatedAttributes: response
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('Do your custom error handling here. I am just gonna log it: ', error);
  })
}

async function deleteUser(MemberId) {
  const params = {
    TableName: dynamodbTableName,
    Key: {
      'MemberId': MemberId
    },
    ReturnValues: 'ALL_OLD'
  }
  return await dynamodb.delete(params).promise().then((response) => {
    const body = {
      Operation: 'DELETE',
      Message: 'SUCCESS',
      Item: response
    }
    return buildResponse(200, body);
  }, (error) => {
    console.error('Do your custom error handling here. I am just gonna log it: ', error);
  })
}

function buildResponse(statusCode, body) {
  return {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  }
}

Click on the Deploy button to deploy the Lambda function.

Create an API Gateway

When your API method receives an HTTP request, API Gateway invokes your Lambda function.

  • Create a REST API in API Gateway.

  • Open AWS Console, type API Gateway in the search bar, and hit enter.

  • Click on the Build button on REST API.

Fill in the details as below.

  • Choose the protocol: REST

  • Create new API: New API

  • API name: user-api

  • Endpoint Type: Regional

Select the Actions drop-down list, choose to Create Resource. Fill in the details as below.

  • Resource Name: users

  • Second Resource Name : user

Click on Create Resource button to complete. Select the Actions drop-down list again, choose Create Method. Select POST to insert a new record. Again repeat process for all methods Get, Update, Delete Screenshot 2022-03-06 at 11.12.26 PM.png

  • Integration type: Lambda Function

  • Use Lambda Proxy integration: Checked(remember to check so event details will be passed to Lambda)

  • Lambda Function: users_details_api

Screenshot 2022-03-14 at 10.40.51 PM.png

Click the Save button, a message with Add Permission to Lambda Function will pop out, click OK to grant the permission.

Screenshot 2022-03-14 at 9.17.55 PM.png

Deploy the API

In the last step, we need to deploy the API so that we can access it thru the public. Click on the Actions drop-down list, select Deploy API.

Screenshot 2022-03-06 at 11.25.26 PM.png

Click the Deploy button and then the Save Changes button. An Invoke URL will appear on top. Try accessing the URL from the browser or Postman.

Test API using Postman

Copy the Invoke URL and paste in Postman Paste the test data to Request Body and click send.

{ "Name": "Ashu", "Last Name": "V", "Gender": "Male", "phone_number" : "9898989898" }

Screenshot 2022-03-06 at 11.36.17 PM.png

Verify the data in the DynamoDB table.

Screenshot 2022-03-06 at 11.39.17 PM.png

In conclusion, we have learned about creating a serverless CRUD API using AWS services such as Lambda, DynamoDB, and API Gateway.

You can Watch YouTube Video

Did you find this article valuable?

Support Ashu Blog by becoming a sponsor. Any amount is appreciated!