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-USERNAME
with 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
todoapp
folder. - 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
.env
file 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.js
file 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
CreateTableCommand
class from the@aws-sdk/client-dynamodb
package and theclient
object from a localclient.js
file. Theclient
object is an instance of theDynamoDBClient
class, which is used to interact with DynamoDB.The code then defines a new
CreateTableCommand
object, passing in an object with several properties. TheTableName
property is set to the value of theTABLE_NAME
environment variable. TheAttributeDefinitions
property 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. TheKeySchema
property is an array of objects that define the primary key of the table. In this case, the primary key is theid
attribute. TheBillingMode
property is set to"PAY_PER_REQUEST"
, which means that the table will be billed on a per-request basis.The code then sends the
CreateTableCommand
to 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.js
will 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
.env
file using thedotenv
package. TheHapi
package is then imported, which provides a framework for building HTTP servers in Node.js.The
routes
object 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.server
class. Theport
andhost
properties of the server are set to the values of thePORT
andHOST
environment variables, respectively.The
server.route()
method is called with theroutes
object, 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)