MongoDB Project: Product API with Express
This MongoDB project puts everything together: a small REST API for products built with Express and Mongoose. It covers the CRUD operations, query operators, sorting, pagination and validation you learned in this tutorial.
Project Setup
mkdir product-api && cd product-api
npm init -y
npm install express mongooseadded 85 packages in 4s
The Server
One file defines the model and five routes. Query parameters drive filtering and paging.
import express from 'express'
import mongoose from 'mongoose'
await mongoose.connect(process.env.MONGODB_URI ?? 'mongodb://127.0.0.1:27017/shop')
const Product = mongoose.model('Product', new mongoose.Schema({
name: { type: String, required: true },
category: String,
price: { type: Number, min: 0 },
stock: { type: Number, default: 0 },
}))
const app = express()
app.use(express.json())
app.get('/products', async (req, res) => {
const { category, maxPrice, page = 1 } = req.query
const filter = {}
if (category) filter.category = String(category)
if (maxPrice) filter.price = { $lte: Number(maxPrice) }
const items = await Product.find(filter).sort({ price: 1 })
.skip((page - 1) * 10).limit(10).lean()
res.json(items)
})
app.post('/products', async (req, res) => {
try { res.status(201).json(await Product.create(req.body)) }
catch (e) { res.status(400).json({ error: e.message }) }
})
app.patch('/products/:id', async (req, res) => {
const p = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true })
p ? res.json(p) : res.sendStatus(404)
})
app.delete('/products/:id', async (req, res) => {
const p = await Product.findByIdAndDelete(req.params.id)
res.sendStatus(p ? 204 : 404)
})
app.listen(3000, () => console.log('API on http://localhost:3000'))API on http://localhost:3000
Trying the API
curl -X POST localhost:3000/products -H "Content-Type: application/json" \
-d '{"name":"Green Tea","category":"Beverages","price":6,"stock":300}'
curl "localhost:3000/products?category=Beverages&maxPrice=10"{"name":"Green Tea","category":"Beverages","price":6,"stock":300,"_id":"6720b1...","__v":0}
[{"_id":"6720b1...","name":"Green Tea","category":"Beverages","price":6,"stock":300,"__v":0}]Ideas to Extend It
- Add an index on { category: 1, price: 1 } and check it with explain()
- Add an /stats route using an aggregation pipeline grouped by category
- Add an Order model with populate() and a transaction that reduces stock
- Validate ObjectId params with mongoose.isValidObjectId before querying
Frequently asked questions
What is the MERN stack?
MongoDB, Express, React and Node.js. This project is the M and E part: the API that a React front end would call.
Why use String() and Number() on query parameters?
Query values come from users. Converting them prevents operator injection and ensures MongoDB compares the right types.
Where should I deploy a MongoDB project?
Use MongoDB Atlas for the database and a Node host such as Render, Railway, Fly.io or a VPS for the API, keeping the URI in an environment variable.