Simplified Guide to Building CRUD APIs in Express with Project 🧑💻
Photo by Douglas Lopes on Unsplash
Table of contents
Introduction :-
Express.js, with its minimalist framework and robust features, has become a go-to choice for building APIs. Among the fundamental operations in API development is CRUD—Create, Read, Update, and Delete. In this article, we'll delve into how to implement CRUD operations effectively in Express.js, empowering you to build powerful and scalable APIs. We'll base our guide on a bookstore project, demonstrating how to create, read, update, and delete books from the inventory.
Setting Up Your Express.js Application : -
Before diving into CRUD operations for the bookstore project, let's set up our Express.js application. Start by installing Express.js And Other Dependencies using npm or yarn:
npm i express nodemon dotenv mongooose
Create an index.js
file and initialize your Express application and Database Connection : -
//Index.js
import dotenv from "dotenv";
import connectDB from "./db/index.js";
import { app } from "./app.js";
dotenv.config({
path: "./env",
});
connectDB()
.then(() => {
app.listen(process.env.PORT || 8000, () => {
console.log(`⚙️ Server is Running at Port : ${process.env.PORT}`);
});
})
.catch((err) => {
console.log("MONGO db connection failed !!! ", err);
});
Create A Folder Utils in src : -
//src/utils/asyncHandler.js
const asyncHandler = (requestHandler) => {
return (req, res, next) => {
Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err))
}
}
export { asyncHandler }
//src/utils/ApiError
class ApiError extends Error {
constructor(
statusCode,
message= "Something went wrong",
errors = [],
stack = ""
){
super(message)
this.statusCode = statusCode
this.data = null
this.message = message
this.success = false;
this.errors = errors
if (stack) {
this.stack = stack
} else{
Error.captureStackTrace(this, this.constructor)
}
}
}
export {ApiError}
//src/utils/ApiResponse
class ApiResponse {
constructor(statusCode, data, message = "Success"){
this.statusCode = statusCode
this.data = data
this.message = message
this.success = statusCode < 400
}
}
export { ApiResponse }
Creating Book : -
This addBook
function serves as a route handler for adding new books to a database. It ensures that essential fields like the book's title, author, and publish year are provided in the request body. If any of these fields are missing, it throws a 400 error. Assuming all required fields are present, it constructs a new book object and creates a record in the database using Book.create()
. Finally, it responds with a status of 201, indicating successful creation, and returns the newly added book details. This function thus facilitates the seamless addition of books to the database via an API endpoint in an Express.js application.
const addBook = asyncHandler(async (req, res) => {
if (!req.body.title || !req.body.author || !req.body.publishYear) {
throw new ApiError(400, "book title,author and publish year required");
}
const newBook = {
title: req.body.title,
author: req.body.author,
publishYear: req.body.publishYear,
};
const book = await Book.create(newBook);
return res.status(201, "books registered successfully").send(book);
});
Reading Books : -
The getBooks
function is an asynchronous route handler designed to fetch all books stored in the database. Using the Book.find({})
method, it retrieves all book records. Upon successful retrieval, it constructs a JSON response using the ApiResponse
class, containing a status code of 200 (indicating success), the fetched book data, and a message confirming successful retrieval. In case no books are found, it catches potential errors and throws a custom ApiError
with a status code of 400, signaling a bad request, along with an appropriate error message. This function enables users to efficiently access and retrieve all available books through an Express.js API endpoint.
const getBooks = asyncHandler(async (_, res) => {
try {
const books = await Book.find({});
return res
.status(201)
.json(new ApiResponse(200, books, "books fetched succesfully"));
} catch (error) {
throw new ApiError(400, "no books found");
}
});
Update Book : -
The updateBook
function is a middleware in an Express.js application designed to handle the updating of book records. Upon receiving a request, it verifies the presence of essential fields such as the book's title, author, and publish year. If any of these fields are absent, it responds with a 400 error, indicating a bad request. Subsequently, it extracts the book's unique identifier from the request parameters and utilizes it to locate the corresponding book in the database. Upon successful retrieval, it updates the book's details with the data provided in the request body. If the update is successful, it sends a response acknowledging the operation. However, if no book is found with the specified identifier, it returns a 401 error, signifying an unauthorized request. Any encountered errors are caught and passed to the next middleware in the Express.js middleware chain for handling. Overall, this function facilitates the seamless modification of book records within the application's database, enhancing the efficiency of book management operations.
const updateBook = asyncHandler(async (req, res, next) => {
try {
if (!req.body.title || !req.body.author || !req.body.publishYear) {
throw new ApiError(400, "fields required");
}
const { id } = req.params;
const result = await Book.findByIdAndUpdate(id, req.body);
if (!result) {
throw new ApiError(401, "invalid book id");
}
return res.status(201).send("book updated successfully");
} catch (error) {
// Pass the error to the next middleware
next(error);
}
});
Delete Book : -
The deleteBook
function, as a route handler in Express.js, manages the deletion of book records from the database. It begins by extracting the unique identifier of the book to be deleted from the request parameters. Utilizing this identifier, it attempts to find and delete the corresponding book using the Book.findByIdAndDelete()
method. Should the specified book not exist, it throws a custom ApiError
with a status code of 401, indicating that the book was not found. Upon successful deletion, it responds with a status of 201 and a message confirming the successful deletion of the book. However, if any errors occur during the process, it throws a generic 500 error, signaling that something went wrong. In essence, this function provides a reliable means to remove book records from the database through an Express.js API endpoint, while also ensuring robust error handling.
const deleteBook = asyncHandler(async (req, res) => {
try {
const { id } = req.params;
const result = await Book.findByIdAndDelete(id);
if (!result) {
throw new ApiError(401, "book not found");
}
return res.status(201).send("book deleted successfully");
} catch (error) {
throw new ApiError(500, "something went wrong");
}
});
Testing Endpoints Using Postman : -
Postman serves as an invaluable tool for testing API endpoints created in Express.js. With Postman, you can send requests to your API endpoints, inspect responses, and validate functionality. Whether it's testing the creation of new books, retrieving book details, updating existing records, or deleting entries from your database, Postman provides a user-friendly interface to interact with your API. By simulating various scenarios and edge cases, you can ensure that your endpoints function as expected, ultimately contributing to the reliability and efficiency of your Express.js application.
Conclusion : -
Implementing CRUD operations for a bookstore project in Express.js enables you to efficiently manage your book inventory. By following the steps outlined in this guide, you can seamlessly create, read, update, and delete books from your system, facilitating a smooth user experience. Harness the power of Express.js to build robust APIs for your projects.