In this article, we will explore how to build a Todo API service with Hapi.js and harnessing the power of Amazon DynamoDB as a serverless NoSQL database provided by AWS
DynamoDB
Amazon DynamoDB is a managed NoSQL database service provided by Amazon Web Services (AWS). It is designed for applications that require seamless and fast performance at any scale. DynamoDB is known for its high availability, durability, and scalability, making it suitable for a wide range of use cases, including web and mobile applications, gaming, IoT (Internet of Things), and more.
Follow me for more
Hapi.js
Hapi.js commonly referred to as "hapi," is an open-source web application framework for building web and application server systems in Node.js. It was created by Walmart Labs and is designed to provide a flexible and robust foundation for building web applications, APIs, and other networked software.
Prerequisites
Before we dive into the implementation, make sure you have the following prerequisites in place:
- Install AWS CLI: You can install the AWS Command Line Interface by following the official documentation.
Make sure that your AWS CLI is already installed by running this command and it should return the respective output.
$ aws --version
aws-cli/2.13.23 Python/3.11.5 Windows/10 exe/AMD64 prompt/off
- Configure AWS CLI: After installation, configure your AWS CLI,then run the following command and ensuring it returns your user information:
$ aws sts get-caller-identity
{
"UserId": "YOUR-USER-ID",
"Account": "YOUR-ACCOUNT-ID",
"Arn": "arn:aws:iam::YOUR-ACCOUNT-ID:user/YOUR-USER-NAME"
}
Additionally, make sure you have the AdministratorAccess policy attached to your AWS CLI user by running:
$ aws iam list-attached-user-policies --user-name YOUR-USERNAME
{
"AttachedPolicies": [
{
"PolicyName": "AdministratorAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
}
]
}
Table of Contents
- Obtain Access and Secret Keys
- Project Preparation
- Configure DynamoDB Client
- Create DynamoDB Table
- Create CRUD Handler
- Register Handlers to Routes
- Create Server and Register Route
- Test the API
- Cleanup
- Conclusion
Obtain Access and Secret Keys
To obtain Access Key and Secret Key, follow these steps:
-
Create a User: You can create a new IAM user using the AWS CLI. (Replace
YOUR-USERNAMEwith the desired username) :
$ aws iam create-user --user-name YOUR-USERNAME
{
"User": {
"Path": "/",
"UserName": "YOUR-USERNAME",
"UserId": "YOUR-USERID",
"Arn": "arn:aws:iam::YOUR-ACCOUNTID:user/YOUR-USERNAME",
"CreateDate": "2023-10-16T17:46:59+00:00"
}
}
-
Create a DynamoDB CRUD Policy: Create a policy that defines the permissions for DynamoDB. You can use a JSON policy document like the one below and save it in a file (e.g.
dynamodb-crud-policy.json).
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Scan",
"dynamodb:Query"
],
"Resource": "*"
}
]
}
Then create the policy using the AWS CLI:
$ aws iam create-policy --policy-name dynamodb-crud-policy --policy-document file://dynamodb-crud-policy.json
{
"Policy": {
"PolicyName": "dynamodb-crud-policy",
"PolicyId": "XXXXXX",
"Arn": "arn:aws:iam::YOUR-ACCOUNTID:policy/dynamodb-crud-policy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2023-10-16T17:48:17+00:00",
"UpdateDate": "2023-10-16T17:48:17+00:00"
}
}
- Attach the Policy to the User: Attach the DynamoDB CRUD policy to the IAM user you created earlier:
$ aws iam attach-user-policy --user-name your-username --policy-arn arn:aws:iam::YOUR-ACCOUNTID:policy/dynamodb-crud-policy
- Generate Access Keys: You can generate access and secret access keys for the IAM user:
$ aws iam create-access-key --user-name your-username
{
"AccessKey": {
"UserName": "YOUR-USERNAME",
"AccessKeyId": "YOUR-ACCESSID",
"Status": "Active",
"SecretAccessKey": "YOUR-SECRET-ACCESS-KEY",
"CreateDate": "2023-10-16T17:49:44+00:00"
}
}
This command will return the AccessKeyId and SecretAccessKey for your IAM user. Be sure to save these keys securely, as the secret access key will not be retrievable again.
Now you have created a user, attached a DynamoDB CRUD policy to that user, and generated access keys for them using the AWS CLI. Ensure you store these keys securely, as they provide access to your AWS resources.
Project Preparation
To set up our project, follow these steps:
- Create a
todoappfolder. - Initialize your project by running:
mkdir todoapp && cd todoapp
npm init -y
- Install the required packages for your project using the following command:
npm install @hapi/hapi @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb dotenv
- Create a
.envfile to store your environment variables, including your AWS credentials and other configuration settings. Here's an example of the content:
PORT=your_available_port
HOST=127.0.0.1
TABLE_NAME=your_table_name
AWS_REGION=your_aws_region
AWS_ACCESS_KEY_ID=your_access_key_id
AWS_SECRET_ACCESS_KEY=your_secret_access_key
- Replace the placeholders (
your_available_port, your_table_name, your_aws_region, your_access_key_id, and your_secret_access_key) with your actual values. The AWS credentials (access key and secret access key) are essential for your application to interact with DynamoDB and you can get it from previous step.
Configure DynamoDB Client
For DynamoDB client configuration, create a client.js file and include the following code:
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
exports.client = new DynamoDBClient({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});
This code exports a DynamoDB client object that can be used to interact with a DynamoDB table. The DynamoDBClient is imported from the @aws-sdk/client-dynamodb package.
The exports.client statement exports a new instance of the DynamoDBClient class. The constructor for the DynamoDBClient class takes an object with two properties: region and credentials. The region property is set to the value of the AWS_REGION environment variable. The credentials property is an object with two properties: accessKeyId and secretAccessKey. These properties are set to the values of the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, respectively.
Create DynamoDB Table
To create your DynamoDB table using the AWS SDK, follow these steps:
- Create a
migrate.jsfile in your project and include the following code:
require("dotenv").config();
const { CreateTableCommand } = require("@aws-sdk/client-dynamodb");
const client = require("./client").client;
(async () => {
try {
const command = new CreateTableCommand({
TableName: process.env.TABLE_NAME,
AttributeDefinitions: [
{
AttributeName: "id",
AttributeType: "S",
},
],
KeySchema: [
{
AttributeName: "id",
KeyType: "HASH",
},
],
BillingMode: "PAY_PER_REQUEST",
});
const response = await client.send(command);
return response;
} catch (error) {
console.error(error.message);
}
})();
This code creates a DynamoDB table using the AWS SDK for JavaScript. It first imports the
CreateTableCommandclass from the@aws-sdk/client-dynamodbpackage and theclientobject from a localclient.jsfile. Theclientobject is an instance of theDynamoDBClientclass, which is used to interact with DynamoDB.The code then defines a new
CreateTableCommandobject, passing in an object with several properties. TheTableNameproperty is set to the value of theTABLE_NAMEenvironment variable. TheAttributeDefinitionsproperty is an array of objects that define the attributes of the table. In this case, there is only one attribute,id, which is a string. TheKeySchemaproperty is an array of objects that define the primary key of the table. In this case, the primary key is theidattribute. TheBillingModeproperty is set to"PAY_PER_REQUEST", which means that the table will be billed on a per-request basis.The code then sends the
CreateTableCommandto DynamoDB using theclient.send()method. This method returns a promise that resolves to an object containing information about the table that was created.If an error occurs during the execution of the code, the error message is logged to the console.
To run the create table code in
migrate.js, you can run this command in your terminal.
node migrate.js
To check whether the table is created or not, you can run this AWS CLI command in your terminal. It should output a list of tables created in your DynamoDB.
$ aws dynamodb list-tables
{
"TableNames": [
"your_table_name"
]
}
Create CRUD Handler
Now, let's create the CRUD (Create, Read, Update, Delete) handlers for managing our todo items.
Create handlers.js, then start by typing this code :
const client = require("./client").client;
const {
DynamoDBDocumentClient,
PutCommand,
UpdateCommand,
QueryCommand,
DeleteCommand,
ScanCommand,
} = require("@aws-sdk/lib-dynamodb");
const docClient = DynamoDBDocumentClient.from(client);
const TableName = process.env.TABLE_NAME;
This code imports the client object from a local client.js file and several classes from the @aws-sdk/lib-dynamodb package. The imported classes are DynamoDBDocumentClient, PutCommand, UpdateCommand, QueryCommand, DeleteCommand, and ScanCommand. These classes are used to interact with a DynamoDB table.
The docClient object is created by calling the DynamoDBDocumentClient.from() method and passing in the client object. This creates a new instance of the DynamoDBDocumentClient class that is configured to use the client object to interact with DynamoDB.
The TableName constant is set to the value of the TABLE_NAME environment variable.
Still in handlers.js file, continue including following code below.
1. Create Add Todo Handler
const addTodo = async (request, h) => {
const { title } = request.payload;
try {
const command = new PutCommand({
TableName,
Item: {
id: Date.now().toString(),
title: title,
iscompleted: false,
},
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(201);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named addTodo that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function extracts the title property from the request.payload object, which contains the payload of the HTTP request. This property is used as the value of the title property in the object passed to the PutCommand constructor. The id property is set to the current timestamp converted to a string, which ensures that each todo item has a unique identifier.
The command object is created by calling the PutCommand constructor with an object that has two properties: TableName and Item. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The Item property is an object with three properties: id, title, and iscompleted. These properties are set to the values extracted from the request.payload object and the string "false", respectively.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to add the item to the table. If the request is successful, the function returns an HTTP response with a status code of 201 and the response from DynamoDB as the response body. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
2. Create Get All Todo Items Handler
const getTodos = async (request, h) => {
try {
const command = new ScanCommand({
TableName,
});
const response = await docClient.send(command);
return h
.response({
status: "success",
message: response.Items
})
.code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named getTodos that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function creates a new ScanCommand object with an object that has two properties: TableName and Limit. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The Limit property is set to 1, which means that only one item will be returned in the response.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to scan the table for items. If the request is successful, the function returns an HTTP response with a status code of 200 and an object containing the items returned by the scan as the response body. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
3. Create Get Todo Item by Id Handler
const getTodo = async (request, h) => {
const { id } = request.params;
try {
const command = new QueryCommand({
TableName,
KeyConditionExpression: "id = :id",
ExpressionAttributeValues: {
":id": id,
},
});
const response = (await docClient.send(command)).Items[0];
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named getTodo that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function extracts the id property from the request.params object, which contains the parameters of the HTTP request. The id property is used as the value of the :id placeholder in the KeyConditionExpression.
The command object is created by calling the QueryCommand constructor with an object that has several properties. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The KeyConditionExpression property is a string that specifies the condition that the item must meet to be returned. In this case, the id attribute must be equal to the value of the :id placeholder. The ExpressionAttributeValues property is an object that maps placeholder names to the values that will be substituted for them in the KeyConditionExpression. In this case, the :id placeholder is mapped to the value extracted from the request.params object.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to query the table for items that meet the specified condition. If the request is successful, the function returns an HTTP response with a status code of 200 and the first item that matches the query as the response body. If no items match the query, the response body will be undefined. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
4. Create Update Todo Item Handler
const updateTodo = async (request, h) => {
const { id } = request.params;
const { title } = request.payload;
try {
const command = new UpdateCommand({
TableName,
Key: {
id: id,
},
UpdateExpression: "set title = :title",
ExpressionAttributeValues: {
":title": title,
},
ReturnValues: "UPDATED_NEW",
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named updateTodo that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function extracts the id property from the request.params object, which contains the parameters of the HTTP request. The id property is used as the value of the Key.id property in the object passed to the UpdateCommand constructor. The title property is extracted from the request.payload object, which contains the payload of the HTTP request. This property is used as the value of the :title placeholder in the UpdateExpression.
The command object is created by calling the UpdateCommand constructor with an object that has several properties. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The Key property is an object with one property: id. This property is set to the value extracted from the request.params object. The UpdateExpression property is a string that specifies the update to be made to the item. In this case, the title attribute is set to the value of the title property extracted from the request.payload object. The ExpressionAttributeValues property is an object that maps placeholder names to the values that will be substituted for them in the UpdateExpression. In this case, the :title placeholder is mapped to the value of the title property extracted from the request.payload object. The ReturnValues property specifies which attributes of the updated item should be returned in the response.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to update the item with the specified id value. If the request is successful, the function returns an HTTP response with a status code of 200 and the updated item as the response body. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
5. Create Update Todo Item Status IsCompleted Handler
const updateCompleted = async (request, h) => {
const { id } = request.params;
try {
const command = new UpdateCommand({
TableName,
Key: {
id: id,
},
UpdateExpression: "set iscompleted = :iscompleted",
ExpressionAttributeValues: {
":iscompleted": true,
},
ReturnValues: "UPDATED_NEW",
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named updateCompleted that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function extracts the id property from the request.params object, which contains the parameters of the HTTP request. The id property is used as the value of the Key.id property in the object passed to the UpdateCommand constructor.
The command object is created by calling the UpdateCommand constructor with an object that has several properties. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The Key property is an object with one property: id. This property is set to the value extracted from the request.params object. The UpdateExpression property is a string that specifies the update to be made to the item. In this case, the iscompleted attribute is set to true. The ExpressionAttributeValues property is an object that maps placeholder names to the values that will be substituted for them in the UpdateExpression. In this case, the :iscompleted placeholder is mapped to the value true. The ReturnValues property specifies which attributes of the updated item should be returned in the response.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to update the item with the specified id value. If the request is successful, the function returns an HTTP response with a status code of 200 and the updated item as the response body. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
6. Create Delete Todo Item Handler
const deleteTodo = async (request, h) => {
const { id } = request.params;
const command = new DeleteCommand({
TableName,
Key: {
id: id,
},
});
try {
const response = await docClient.send(command);
return h.response({
status: "success",
message: "Delete Todo Success"
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
This code defines an asynchronous function named deleteTodo that takes a request object and an h object as arguments. The request object contains information about the HTTP request that was made, and the h object contains methods for constructing an HTTP response.
The function extracts the id property from the request.params object, which contains the parameters of the HTTP request. The id property is used as the value of the Key.id property in the object passed to the DeleteCommand constructor.
The command object is created by calling the DeleteCommand constructor with an object that has two properties: TableName and Key. The TableName property is set to the value of the TableName constant, which is defined elsewhere in the code. The Key property is an object with one property: id. This property is set to the value extracted from the request.params object.
The docClient.send() method is called with the command object, which sends a request to DynamoDB to delete the item with the specified id value. If the request is successful, the function returns an HTTP response with a status code of 200 and a message indicating that the todo has been successfully deleted. If an error occurs during the execution of the code, the error message is returned as an HTTP response.
7. Export all the functions
module.exports = {
addTodo,
getTodos,
getTodo,
updateTodo,
deleteTodo,
updateCompleted,
};
This code exports an object with several functions that can be used to handle HTTP requests. The functions are named addTodo, getTodos, getTodo, updateTodo, deleteTodo, and updateCompleted.
The complete code of
handlers.jswill look like this.
const client = require("./client").client;
const {
DynamoDBDocumentClient,
PutCommand,
UpdateCommand,
QueryCommand,
DeleteCommand,
ScanCommand,
} = require("@aws-sdk/lib-dynamodb");
const docClient = DynamoDBDocumentClient.from(client);
const TableName = process.env.TABLE_NAME;
const addTodo = async (request, h) => {
const { title } = request.payload;
try {
const command = new PutCommand({
TableName,
Item: {
id: Date.now().toString(),
title: title,
iscompleted: false,
},
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(201);
} catch (error) {
return h.response({
message: error.message,
});
}
};
const getTodos = async (request, h) => {
try {
const command = new ScanCommand({
TableName,
});
const response = await docClient.send(command);
return h
.response({
status: "success",
message: response
})
.code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
const getTodo = async (request, h) => {
const { id } = request.params;
try {
const command = new QueryCommand({
TableName,
KeyConditionExpression: "id = :id",
ExpressionAttributeValues: {
":id": id,
},
});
const response = (await docClient.send(command)).Items[0];
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
const updateTodo = async (request, h) => {
const { id } = request.params;
const { title } = request.payload;
try {
const command = new UpdateCommand({
TableName,
Key: {
id: id,
},
UpdateExpression: "set title = :title",
ExpressionAttributeValues: {
":title": title,
},
ReturnValues: "UPDATED_NEW",
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
const updateCompleted = async (request, h) => {
const { id } = request.params;
try {
const command = new UpdateCommand({
TableName,
Key: {
id: id,
},
UpdateExpression: "set iscompleted = :iscompleted",
ExpressionAttributeValues: {
":iscompleted": true,
},
ReturnValues: "UPDATED_NEW",
});
const response = await docClient.send(command);
return h.response({
status: "success",
message: response
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
const deleteTodo = async (request, h) => {
const { id } = request.params;
const command = new DeleteCommand({
TableName,
Key: {
id: id,
},
});
try {
const response = await docClient.send(command);
return h.response({
status: "success",
message: "Delete Todo Success"
}).code(200);
} catch (error) {
return h.response({
message: error.message,
});
}
};
module.exports = {
addTodo,
getTodos,
getTodo,
updateTodo,
deleteTodo,
updateCompleted,
};
Register Handlers to Routes
Now, let's register the handlers to Hapi.js routes.
Create a routes.js file and include the following code:
const todoHandler = require("./handlers");
exports.routes = [
{
method: "POST",
path: "/todos",
handler: todoHandler.addTodo,
},
{
method: "GET",
path: "/todos",
handler: todoHandler.getTodos,
},
{
method: "GET",
path: "/todos/{id}",
handler: todoHandler.getTodo,
},
{
method: "PUT",
path: "/todos/{id}",
handler: todoHandler.updateTodo,
},
{
method: "DELETE",
path: "/todos/{id}",
handler: todoHandler.deleteTodo,
},
{
method: "PUT",
path: "/todos/{id}/completed",
handler: todoHandler.updateCompleted,
},
];
This codes exports an array of objects that define the routes for a RESTful API. Each object in the array represents a single route and contains the following properties:
-
method: The HTTP method for the route (e.g. GET, POST, PUT, DELETE). -
path: The URL path for the route. -
handler: The function that will handle the request for the route.
The handler property is set to a function that is imported from a module named todoHandler.
Create Server and Register Route
To create the Hapi.js server and register the routes, follow these steps:
- Create an index.js file in your project and include the following code:
require("dotenv").config();
const Hapi = require("@hapi/hapi");
const { routes } = require("./routes");
(async () => {
const server = Hapi.server({
port: process.env.PORT,
host: process.env.HOST,
});
server.route(routes);
await server.start();
console.log("Server running on %s", server.info.uri);
})();
This code sets up a Hapi server that listens for incoming HTTP requests. It first loads environment variables from a
.envfile using thedotenvpackage. TheHapipackage is then imported, which provides a framework for building HTTP servers in Node.js.The
routesobject is imported from a separate file namedroutes.js. This object contains the routes that the server will handle.An asynchronous function is defined using an immediately invoked function expression (IIFE) that creates a new instance of the
Hapi.serverclass. Theportandhostproperties of the server are set to the values of thePORTandHOSTenvironment variables, respectively.The
server.route()method is called with theroutesobject, which sets up the routes that the server will handle.The
server.start()method is called to start the server. If the server starts successfully, a message is logged to the console indicating the URL that the server is listening on.
Test the API
To test the API, you can follow these steps:
- Run the server
node index.js
- Add Todo
$ curl -X POST http://localhost:8080/todos -H 'Content-Type: application/json' -d '{"title":"Build API with DynamoDB"}'
{"$metadata":{"httpStatusCode":200,"requestId":"cb51867a-2e1e-4d62-8ce6-f709825d2505","attempts":1,"totalRetryDelay":0}}
- Get All Todo Item
$ curl -X GET http://localhost:8080/todos -H 'Content-Type: application/json'
{"response":[{"title":"Build API with DynamoDB","iscompleted":false,"id":"1697484371663"}]}
- Get Todo Item by Id
$ curl -X GET http://localhost:8080/todos/1697484371663 -H 'Content-Type: application/json'
{"title":"Build API with DynamoDB","iscompleted":false,"id":"1697484371663"}
- Update Todo Item status IsCompleted
$ curl -X PUT http://localhost:8080/todos/1697484371663/completed -H 'Content-Type: application/json'
{"$metadata":{"httpStatusCode":200,"requestId":"b13129b1-1087-4e07-b205-60e067938327","attempts":1,"totalRetryDelay":0},"Attributes":{"iscompleted":true}}
- Delete Todo Item
$ curl -X DELETE http://localhost:8080/todos/1697484371663 -H 'Content-Type: application/json'
Delete Todo Success
Cleanup
For cleanup, we can delete our DynamoDB table that we created before by typing this command in our terminal.
$ aws dynamodb delete-table --table-name your_table_name
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"TableName": "your_table_name",
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
Conclusion
In this blog post, we learn about building a Todo API using the powerful Hapi.js framework and harnessing the capabilities of Amazon DynamoDB. Throughout this tutorial, we've seen how these two technologies can be seamlessly integrated to create a robust and scalable solution for managing tasks and to-do lists.
Hapi.js, with its ease of use and extensive ecosystem of plugins, provided us with a solid foundation to build our API. Its routing, validation, and error-handling capabilities make it an excellent choice for developing RESTful services. Moreover, we appreciated the emphasis on configuration-driven development, which simplifies the setup process and enhances maintainability.
DynamoDB, Amazon's fully managed NoSQL database, proved to be a valuable asset in our project. Its scalability, high availability, and serverless architecture ensured that our Todo API can handle both small-scale tasks and grow seamlessly to accommodate larger workloads. The performance and durability of DynamoDB, combined with features like automatic backups and global tables, give us peace of mind when it comes to data integrity.
In conclusion, building a Todo API with Hapi.js and DynamoDB is a rewarding experience that equips you with the tools to create powerful, scalable, and resilient applications.
Top comments (0)