Introduction
In today’s web development landscape, building RESTful APIs has become a crucial skill for developers. Whether you’re creating a simple application or a complex system, REST APIs provide a standardized way for different software components to communicate with each other over the web. In this tutorial, we’ll walk through the process of building a RESTful API using Node.js, Express, and MongoDB, focusing on CRUD operations (Create, Read, Update, Delete) for managing products.
For more information on REST API, check this blog : Designing APIs Using REST Specifications: A Comprehensive Guide – Learn Code Camp
Technologies Used
- Node.js: A JavaScript runtime built on Chrome’s V8 JavaScript engine.
- Express: A minimalist web framework for Node.js, which simplifies the process of building web applications and APIs.
- MongoDB: A NoSQL database that stores data in flexible, JSON-like documents.
Setting Up the Project
First, let’s set up our project structure and install the necessary dependencies. Create a new directory for your project and navigate into it.
mkdir node-express-mongodb-api
cd node-express-mongodb-api
Initialize a new Node.js project and install Express, Mongoose (for MongoDB integration), and any other dependencies needed.
npm install express mongodb mongoose
npm install --save-dev nodemon
Creating the MongoDB Database
To install the mongo db atlas locally run these commands
brew install mongodb-atlas
atlas deployments setup
To see the collections in mongo db from mongosh, run these commands
1. show collections; // Display all collections
2. show tables // Display all collections
3. db.getCollectionNames(); // Return array of collection. Example :[ "orders", "system.profile" ]
Some other useful commands
show dbs; // to show all the db present
db.products.find() // to list 20 documents from collection products.
Setting Up the Server
Now, let’s create the main file index.js
where we’ll set up our Express server and define the routes for our API.
// index.js
const express = require("express");
const mongoose = require("mongoose");
const productRoute = require("./routes/product.route.js");
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// Routes
app.use("/api/products", productRoute);
// Home route
app.get("/", (req, res) => {
res.send("Hello from Node API Server");
});
// Connect to MongoDB and start the server
mongoose
.connect("mongodb://localhost:63233/test?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.5", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to MongoDB");
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
})
.catch((error) => {
console.error("Connection to MongoDB failed:", error);
});
In the above code:
- We import Express and Mongoose, and define our Express app.
- Middleware functions are added to parse incoming requests with JSON payloads and URL-encoded bodies.
- Routes are defined using the
/api/products
prefix, which delegates further handling toproductRoute
. - A simple home route is set up to verify that the server is running.
- We connect to the MongoDB database using Mongoose and start the Express server on port 3000.
Defining the Product Model
Next, let’s define the product model that represents the structure of our data in MongoDB.
// product.model.js
const mongoose = require("mongoose");
const ProductSchema = mongoose.Schema(
{
name: {
type: String,
required: [true, "Please enter product name"],
},
quantity: {
type: Number,
required: true,
default: 0,
},
price: {
type: Number,
required: true,
default: 0,
},
image: {
type: String,
required: false,
},
},
{
timestamps: true,
}
);
const Product = mongoose.model("Product", ProductSchema);
module.exports = Product;
In the ProductSchema
:
- We define the fields for our product model (name, quantity, price, image).
- Field validation rules are specified using Mongoose schema types and options.
- We enable timestamps to automatically add
createdAt
andupdatedAt
fields to each document.
Creating CRUD Operations
Now, let’s create the controller functions for handling CRUD operations on products.
// product.controller.js
const Product = require("../models/product.model");
const getProducts = async (req, res) => {
try {
const products = await Product.find({});
res.status(200).json(products);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
const getProduct = async (req, res) => {
try {
const { id } = req.params;
const product = await Product.findById(id);
if (!product) {
return res.status(404).json({ message: "Product not found" });
}
res.status(200).json(product);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
const createProduct = async (req, res) => {
try {
const product = await Product.create(req.body);
res.status(201).json(product);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
const updateProduct = async (req, res) => {
try {
const { id } = req.params;
const product = await Product.findByIdAndUpdate(id, req.body, {
new: true,
});
if (!product) {
return res.status(404).json({ message: "Product not found" });
}
res.status(200).json(product);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
const deleteProduct = async (req, res) => {
try {
const { id } = req.params;
const product = await Product.findByIdAndDelete(id);
if (!product) {
return res.status(404).json({ message: "Product not found" });
}
res.status(200).json({ message: "Product deleted successfully" });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
module.exports = {
getProducts,
getProduct,
createProduct,
updateProduct,
deleteProduct,
};
In the product.controller.js
:
- Controller functions are defined for handling various CRUD operations on products.
- These functions use asynchronous syntax with
async/await
for working with MongoDB queries. - Error handling is implemented to catch any potential errors and return appropriate HTTP status codes and error messages.
Setting Up Product Routes
Finally, let’s define the routes for our products API in product.route.js
.
// product.route.js
const express = require("express");
const router = express.Router();
const {
getProducts,
getProduct,
createProduct,
updateProduct,
deleteProduct,
} = require("../controllers/product.controller");
router.get("/", getProducts);
router.get("/:id", getProduct);
router.post("/", createProduct);
router.put("/:id", updateProduct);
router.delete("/:id", deleteProduct);
module.exports = router;
Here, we define routes for fetching all products, fetching a single product by ID, creating a new product, updating an existing product, and deleting a product.
Conclusion
In this tutorial, we’ve learned how to build a RESTful API with Node.js, Express, and MongoDB. We’ve covered setting up the server, defining the database model, implementing CRUD operations, and setting up routes to handle API requests. With this foundation, you can extend the API further by adding more features, implementing authentication and authorization, and optimizing performance for production use. Happy coding!
References: haris-bit/simple-crud-app-backend (github.com)