Build CRUD RESTful Microservices with AWS Lambda, API Gateway, DynamoDB with AWS SDK JS v3

In this article, we are going to build CRUD RESTful Microservices with AWS Lambda, API Gateway and DynamoDB using latest AWS SDK JavaScript v3 and ES6 standards.

CRUD RESTful Microservices with AWS Lambda, API Gateway, DynamoDB

We will develop Hands-on Lab : Build CRUD RESTful Microservices with AWS Lambda, API Gateway, DynamoDB using Node.js AWS-SDK V3. By the end of the article, we will expose and make http call to API Gateway and perform crud logic with AWS Lambda and DynamoDB. You can also watch the whole article with step by step development at the YouTube video:

I have just published a new course — AWS Lambda & Serverless — Developer Guide with Hands-on Labs.

RESTful Microservices with AWS Lambda, API Gateway and DynamoDB

When building a microservice, you’re thinking about how a business context can be delivered as a re-usable service for your consumers. For that reason, the implementation should be specific as per individual use cases. But there are common approaches when it comes to develop Restful microservices with using AWS Serverless services.

We are going to create a serverless API that creates, reads, updates, and deletes items from a DynamoDB table. DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability.

First, we create a DynamoDB table using the DynamoDB console. Then we create a Lambda function using the AWS Lambda console. Next, we create an REST API using the API Gateway console. Lastly, we test your API.

RESTful Microservices with AWS Lambda, API Gateway and DynamoDB

This is the Reference architecture for RESTful microservices.

  • Clients send request our microservices by making HTTP API calls. Ideally, our clients should have a tightly bounded service contract to our API in order to achieve consistent expectations of microservice responsibility.
  • Amazon API Gateway hosts RESTful HTTP requests and responses to customers. In this scenario, API Gateway provides built-in authorization, throttling, security, fault tolerance, request/response mapping, and performance optimizations.
  • AWS Lambda contains the business logic to process incoming API calls and leverage DynamoDB as a persistent storage.
  • Amazon DynamoDB persistently stores microservices data and scales based on demand. Since microservices are often designed to do one thing well, a schema-less NoSQL data store is regularly incorporated.

So when we invoke our Api Gateway API, API Gateway routes the request to your Lambda function. The Lambda function interacts with DynamoDB, and returns a response to API Gateway. API Gateway then returns a response to us.

Create a DynamoDB Table

We are going to Create a DynamoDB table. We use a DynamoDB table to store data for your API. Each item has a unique ID, which we use as the partition key for the table.

To create a DynamoDB table, Open the DynamoDB console at https://console.aws.amazon.com/dynamodb/.

Choose Create table.
For Table name, product

For Primary key,
PK — id.

Choose Create.

Create a Lambda Function

We are going to 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.

We create a Lambda function for the backend of your API. This Lambda function creates, reads, updates, and deletes items from DynamoDB. The function uses events from API Gateway to determine how to interact with DynamoDB.

To create a Lambda function; https://console.aws.amazon.com/lambda.

Choose Create function.
For Function name
productFunction

Give Microservice Permission
Under Permissions choose Change default execution role.
Select Create a new role from AWS policy templates.

For Role name
productRole

For Policy templates, choose “Simple microservice permissions”. This policy grants the Lambda function permission to interact with DynamoDB.

Choose Create function.

As you can see that we have successfully create our Product Lambda function with simple microservices role that provide to interact crud operations on DynamoDB Tables.

Configure Lambda Function

Now we can configure our Lambda function. You can click configuration and see details. This time we will follow best practices when developing our crud operations on this lambda function. One of the best practices is use Environment Variables as soon as possible. For that reason, I would like to define some ENV Variables to get DynamoDB related parameters as a environment variables. Lets create ENV Variables:

environment: {
PRIMARY_KEY: ‘id’,
DYNAMODB_TABLE_NAME: ‘product’,
},

So with these environment variables instead of writing hard-coded our DynamoDB table names, we will use ENV Variables with getting values from process.ENV.DYNAMODB_TABLE_NAME parameters into lambda functions.

Lets open vs code window, Open index.js under src/product. Put minimum function handler code

src/product/index.jsexports.handler = async function(event) { 
console.log(“request:”, JSON.stringify(event, undefined, 2));
return {
statusCode: 200,
headers: { “Content-Type”: “text/plain” },
body: `Hello from Product ! You’ve hit ${event.path}\n`
};
};

For now, its enough to implement handler code. This is only return a message with 200 status code. But we will develop this function interacting with product DynamoDB table in order to perform crud operations. and this crud implementation , all these interacting with product DynamoDB table will be handled by using AWS SDK.

Create an API Gateway

API Gateway also supports HTTP APIs and WebSocket APIs, but an REST API is the best choice for this exercise. The REST API provides an REST HTTP endpoint for your Lambda function.

API Gateway routes requests to your Lambda function, and then returns the function’s response to clients. First of all we should design our API resource and methods:

CRUD Product APIs
GET /product
GET /product/{id}
POST /product
DELETE /product/{id}

Now we can create REST API for our example, Create a REST API in API Gateway: Open AWS Console, type API Gateway

Goto API Gateway — create an REST API — productApi — Create Resource — product — Create Methods GET, GetbyId, POST, Delete — Deploy the API

Develop Lambda Function

We are going to Start to Developing Product Lambda Microservice.
We are going to Install npm package — @aws-sdk/client-dynamodb — DynamoDB Client — AWS SDK for JavaScript v3.

goto new folder under “microservices/product”, goto index.js , if not create index.js. Run Command:

npm init -y

See that its created default parameters package json file. Create “package.json” under “src/product” next to “index.js” file.

Add
“type”: “module”,

As you know that, we should add type-module in order to gain ES6 standards and modular architecture on AWS SDK JS v3. This provide to use ES6 codes into NodeJS projects. for example “import statements”.

Install Required Packages

Ok lets continue to install required packages. We added empty template of package.json file, because we will install some npm packages after that. Install required dynmodb sdk package. We are going to install “@aws-sdk/client-dynamodb” package. We can see the package detail into API reference document of AWS SDK for JavaScript v3. Run command :

npm install @aws-sdk/client-dynamodb

See package.json dependencies:

{
“name”: “@microservices/basket”,
“version”: “1.0.0”,
“main”: “index.js”,
“dependencies”: {
@aws-sdk/client-dynamodb”: “³.45.0”
}
}

As you can see that node_modules are generated when running npm install command. So we have successfully installed @aws-sdk/client-dynamodb package and ready for usage this library into our index.js code.

Also we need to install 1 more package, install required dynmodb sdk package @aws-sdk/util-dynamodb. Run Command :

npm install @aws-sdk/util-dynamodb

Finally we are ready for development !

Developing Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3

We are going to Developing Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3. Create db connection module src/product, create a Node.js module with the file name ddbClient.js:

src/product/ddbClient.js// Create service client module using ES6 syntax.
import { DynamoDBClient } from “@aws-sdk/client-dynamodb”;
const REGION = “us-east-2”;
// Create an Amazon DynamoDB service client object.
const ddbClient = new DynamoDBClient({ region: REGION });
export { ddbClient };

You should replace your region into code. So basically, We have created ddbClient as a NodeJS module, because the connection management will be handle 1 time for our lambda executions. This is best practice to create database connections before lambda handler method execution, by this way, it reduce the execution time and reduce to concurrency of lambda function and of course it reduce to costing pay-as-you-go model, because the execution time reduce dramatically if we have already database connection.

AWS API Gateway trigger AWS Lambda with API Gateway proxy event (REST API)

We are going to see how AWS API Gateway trigger AWS Lambda with API Gateway proxy event (REST API). We will see Example event.json API Gateway proxy event (REST API).

This is very important step, before start to developing we should understand incoming event structure from the trigger resource of our lambda function. In this case, our product lambda function will trigger from API gateway with synchronously. We have already create infrastructure our API Gateway proxy event (REST API).

Amazon API Gateway invokes your function synchronously with an event that contains a JSON representation of the HTTP request. For a custom integration, the event is the body of the request. For a proxy integration, the event has a defined structure. The following example shows a proxy event from an API Gateway REST API. See Example event.json API Gateway proxy event (REST API). Here is example event json:

{
“resource”: “/”,
“path”: “/”,
“httpMethod”: “GET”,
“requestContext”: {
“resourcePath”: “/”,
“httpMethod”: “GET”,
“path”: “/Prod/”,

},

So that means API Gateway will trigger lambda with this json structure.
So the event object include these json object that we can access httpMethod, pathParameters , queryStringParameters and so on.

Also we can click event link, Open full github event json object;

So according to this event object how we can know event has event.httpMethod and event.pathParameters.id ? Its very easy, in lambda function, if it trigger from API Gateway, then we can easily access these values with writing event.httpMethod event.path and so on. So that means now we can put our conditions according to event httpMethod and path informations.

Developing Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3

Ok now we should consider the structure of our main lambda function;

exports.handler = async function(event) { 
console.log(“request:”, JSON.stringify(event, undefined, 2));
let body;
// TODO — switch case event.httpmethod to perform CRUD operations with using dbClient object
switch (event.httpMethod) {
case “GET”:
if (event.pathParameters != null) {
body = await getProduct(event.pathParameters.id);
} else {
body = await getAllProducts();
}
}
return {
statusCode: 200,
headers: { “Content-Type”: “text/plain” },
body: `Hello from Product ! You’ve hit ${event.path}\n`
};
};

So with this code, we have check the httpMethod if it is GET after that we checked that is there any pathParameters like id = which is productId if there is a productId path Parameter then we call get single product method
otherwise we get all products.

Developing getProduct methods for Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3

We are going to Developing getProduct methods for Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3.

So now we should create our methods:

  • getProduct
  • getAllProducts
import { GetItemCommand } from “@aws-sdk/client-dynamodb”;
import { marshall, unmarshall } from “@aws-sdk/util-dynamodb”;
import { ddbClient } from “./ddbClient.js”;
const getProduct = async (productId) => {
console.log(“getProduct”);
try {
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME,
Key: marshall({ id: productId })
};

const { Item } = await ddbClient.send(new GetItemCommand(params));
console.log(Item);
return (Item) ? unmarshall(Item) : {};
} catch(e) {
console.error(e);
throw e;
}
}

Let examine the code, and try to explain. We start with logging, this is very important, because we won’t debug any code when developing serverless microservice application. So we will check our code from aws cloudwatch logs, so its important to put logs efficiently.

console.log(“getProduct”);

As the same way, if we interact or connect other resources, its good to cover try-catch block, in order to follow errors into AWS Cloudwatch logs. After that, we have followed dynamodb sdk documentation and create parameters for “GetItemCommand” request. Basically, you can think DynamoDB is a service and expose some apis to us for performing crud operations on database. And these apis requires some parameters. So in our case we use “GetItemCommand” DynamoDB API and it requires parameters which includes table name and key — primary key = partition key.

Developing getAllProducts methods for Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3

Now its time to develop “getAllProducts” method. This time we will use “ScanCommand” in order to retrieve all records into product DynamoDB table.

const getAllProducts = async () => {
console.log(“getAllProducts”);
try {
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME
};

const { Items } = await ddbClient.send(new ScanCommand(params));
console.log(Items);
return (Items) ? Items.map((item) => unmarshall(item)) : {};
} catch(e) {
console.error(e);
throw e;
}
}

As you can see that we have developed getAllProducts and its very similar to getProduct method. The only differences is the command that we use; ScanCommand.

Developing createProduct CRUD methods for Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3

We are going to Developing createProduct CRUD methods for Product Lambda Microservice with DynamoDB Client — AWS SDK for JavaScript v3.

Modify switch statement;

// TODO — switch case event.httpmethod to perform CRUD operations with using dbClient object
switch (event.httpMethod) {
case “GET”:
if (event.pathParameters != null) {
body = await getProduct(event.pathParameters.id);
} else {
body = await getAllProducts();
}
case “POST”:
body = await createProduct(event);
break;
default:
throw new Error(`Unsupported route: “${event.httpMethod}”`);
}

We have added POST methods so we will focus on body = await createProduct(event); method. Now we can develop our own code;

await createProduct(event);const createProduct = async (event) => {
try {
console.log(`createProduct function. event : “${event}”`);

const requestBody = JSON.parse(event.body);
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME,
Item: marshall(requestBody || {})
};
const createResult = await ddbClient.send(new PutItemCommand(params));
console.log(createResult);
return createResult;
} catch(e) {
console.error(e);
throw e;
}
}

Don’t forget to import:

import { DeleteItemCommand, GetItemCommand, PutItemCommand, QueryCommand, ScanCommand, UpdateItemCommand } from “@aws-sdk/client-dynamodb”;
import { marshall, unmarshall } from “@aws-sdk/util-dynamodb”;
import { ddbClient } from “./ddbClient”;

Ok basically, we are following the same dicipline, create a requestBody and pass this json object as a DynamoDB item into params. As you know that, TableName comes from environment variables that we set this information, when creating infrastructure of lambda function, you can configurations of lambda function from aws management console. And Item is the json object of product item. After that we use “PutItemCommand” in order to insert new item into dynamodb table. I am going to skip other commands but if you would like to continue, you can follow the below course.

E2E Test REST API and Lambda Function with Incoming REST API Event Json Object

we are going to E2E Test REST API and Lambda Function with Incoming REST API Event Json Object to perform crud operations.
Lets get started. Create request one by one;

GET
https://xxxx.execute-api.us-east-2.amazonaws.com/product
{“message”:”Successfully finished operation: \”GET /product\””,”body”:”Processing Get All Products”}GET by ID
https://xxxx.execute-api.us-east-2.amazonaws.com/product/343
{“message”:”Successfully finished operation: \”GET /product/{id}\””,”body”:”Processing Get Product Id with \”343\””}POST
https://xxxx.execute-api.us-east-2.amazonaws.com/product
payload:
{
“name”:”IPhone”,
“price”:”900"
}
{“message”:”Successfully finished operation: \”POST /product\””,”body”:”Processing Post Product Id with \”[object Object]\””}DELETE
https://xxxxx.execute-api.us-east-2.amazonaws.com/product/343
{“message”:”Successfully finished operation: \”DELETE /product/{id}\””,”body”:”Processing Delete Product Id with \”343\””}

As you can see that we have successfully tested our REST API and Lambda functions and finished Hands-on Lab: Build CRUD Microservice with REST API and Lambda. You can also watch the whole article with step by step development at the YouTube video:

Step by Step Design AWS Architectures w/ Course

I have just published a new course — AWS Lambda & Serverless — Developer Guide with Hands-on Labs.

In this course, we will learn almost all the AWS Serverless Services with all aspects. We are going to build serverless applications with using AWS Lambda, Amazon API Gateway, Amazon DynamoDB, Amazon Cognito, Amazon S3, Amazon SNS, Amazon SQS, Amazon EventBridge, AWS Step Functions, DynamoDB and Kinesis Streams. This course will be 100% hands-on, and you will be developing a real-world application with hands-on labs together and step by step.

Source Code

Get the Source Code from Serverless Microservices GitHub — Clone or fork this repository, if you like don’t forget the star. If you find or ask anything you can directly open issue on repository.

--

--

Mehmet Ozkaya
AWS Lambda & Serverless — Developer Guide with Hands-on Labs

Software Architect | Udemy Instructor | AWS Community Builder | Cloud-Native and Serverless Event-driven Microservices https://github.com/mehmetozkaya