In this section, we will build a simple e-commerce API using Node.js and Express.js. This API will allow users to perform CRUD (Create, Read, Update, Delete) operations on products and manage user authentication.
Objectives
- Set up a basic Express.js application.
- Implement CRUD operations for products.
- Implement user authentication using JWT.
- Handle errors and validate data.
Prerequisites
- Basic knowledge of Node.js and Express.js.
- Understanding of MongoDB and Mongoose.
- Familiarity with JWT for authentication.
Step-by-Step Guide
- Setting Up the Project
1.1 Initialize the Project
First, create a new directory for your project and initialize it with npm.
1.2 Install Dependencies
Install the necessary dependencies.
1.3 Project Structure
Create the following directory structure:
ecommerce-api/ ├── config/ │ └── db.js ├── controllers/ │ ├── authController.js │ └── productController.js ├── models/ │ ├── product.js │ └── user.js ├── routes/ │ ├── authRoutes.js │ └── productRoutes.js ├── middleware/ │ └── authMiddleware.js ├── .env ├── app.js └── server.js
- Configuring the Database
2.1 Database Configuration
Create a config/db.js
file to set up the MongoDB connection.
// config/db.js const mongoose = require('mongoose'); const dotenv = require('dotenv'); dotenv.config(); const connectDB = async () => { try { await mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, }); console.log('MongoDB connected'); } catch (error) { console.error('MongoDB connection failed:', error.message); process.exit(1); } }; module.exports = connectDB;
2.2 Environment Variables
Create a .env
file to store environment variables.
- Creating Models
3.1 User Model
Create a models/user.js
file for the User model.
// models/user.js const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); const userSchema = new mongoose.Schema({ name: { type: String, required: true, }, email: { type: String, required: true, unique: true, }, password: { type: String, required: true, }, }); userSchema.pre('save', async function (next) { if (!this.isModified('password')) { next(); } const salt = await bcrypt.genSalt(10); this.password = await bcrypt.hash(this.password, salt); }); userSchema.methods.matchPassword = async function (enteredPassword) { return await bcrypt.compare(enteredPassword, this.password); }; const User = mongoose.model('User', userSchema); module.exports = User;
3.2 Product Model
Create a models/product.js
file for the Product model.
// models/product.js const mongoose = require('mongoose'); const productSchema = new mongoose.Schema({ name: { type: String, required: true, }, description: { type: String, required: true, }, price: { type: Number, required: true, }, countInStock: { type: Number, required: true, }, }); const Product = mongoose.model('Product', productSchema); module.exports = Product;
- Creating Controllers
4.1 Auth Controller
Create a controllers/authController.js
file for user authentication.
// controllers/authController.js const User = require('../models/user'); const jwt = require('jsonwebtoken'); const dotenv = require('dotenv'); dotenv.config(); const generateToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d', }); }; exports.registerUser = async (req, res) => { const { name, email, password } = req.body; const userExists = await User.findOne({ email }); if (userExists) { return res.status(400).json({ message: 'User already exists' }); } const user = await User.create({ name, email, password, }); if (user) { res.status(201).json({ _id: user._id, name: user.name, email: user.email, token: generateToken(user._id), }); } else { res.status(400).json({ message: 'Invalid user data' }); } }; exports.authUser = async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (user && (await user.matchPassword(password))) { res.json({ _id: user._id, name: user.name, email: user.email, token: generateToken(user._id), }); } else { res.status(401).json({ message: 'Invalid email or password' }); } };
4.2 Product Controller
Create a controllers/productController.js
file for product operations.
// controllers/productController.js const Product = require('../models/product'); exports.getProducts = async (req, res) => { const products = await Product.find({}); res.json(products); }; exports.getProductById = async (req, res) => { const product = await Product.findById(req.params.id); if (product) { res.json(product); } else { res.status(404).json({ message: 'Product not found' }); } }; exports.createProduct = async (req, res) => { const { name, description, price, countInStock } = req.body; const product = new Product({ name, description, price, countInStock, }); const createdProduct = await product.save(); res.status(201).json(createdProduct); }; exports.updateProduct = async (req, res) => { const { name, description, price, countInStock } = req.body; const product = await Product.findById(req.params.id); if (product) { product.name = name; product.description = description; product.price = price; product.countInStock = countInStock; const updatedProduct = await product.save(); res.json(updatedProduct); } else { res.status(404).json({ message: 'Product not found' }); } }; exports.deleteProduct = async (req, res) => { const product = await Product.findById(req.params.id); if (product) { await product.remove(); res.json({ message: 'Product removed' }); } else { res.status(404).json({ message: 'Product not found' }); } };
- Creating Routes
5.1 Auth Routes
Create a routes/authRoutes.js
file for authentication routes.
// routes/authRoutes.js const express = require('express'); const { registerUser, authUser } = require('../controllers/authController'); const router = express.Router(); router.post('/register', registerUser); router.post('/login', authUser); module.exports = router;
5.2 Product Routes
Create a routes/productRoutes.js
file for product routes.
// routes/productRoutes.js const express = require('express'); const { getProducts, getProductById, createProduct, updateProduct, deleteProduct, } = require('../controllers/productController'); const { protect } = require('../middleware/authMiddleware'); const router = express.Router(); router.route('/').get(getProducts).post(protect, createProduct); router .route('/:id') .get(getProductById) .put(protect, updateProduct) .delete(protect, deleteProduct); module.exports = router;
- Middleware
6.1 Auth Middleware
Create a middleware/authMiddleware.js
file for authentication middleware.
// middleware/authMiddleware.js const jwt = require('jsonwebtoken'); const User = require('../models/user'); const dotenv = require('dotenv'); dotenv.config(); exports.protect = async (req, res, next) => { let token; if ( req.headers.authorization && req.headers.authorization.startsWith('Bearer') ) { try { token = req.headers.authorization.split(' ')[1]; const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id).select('-password'); next(); } catch (error) { res.status(401).json({ message: 'Not authorized, token failed' }); } } if (!token) { res.status(401).json({ message: 'Not authorized, no token' }); } };
- Setting Up the Server
7.1 App Configuration
Create an app.js
file to configure the Express application.
// app.js const express = require('express'); const connectDB = require('./config/db'); const authRoutes = require('./routes/authRoutes'); const productRoutes = require('./routes/productRoutes'); const dotenv = require('dotenv'); dotenv.config(); connectDB(); const app = express(); app.use(express.json()); app.use('/api/auth', authRoutes); app.use('/api/products', productRoutes); app.use((err, req, res, next) => { res.status(500).json({ message: err.message }); }); module.exports = app;
7.2 Starting the Server
Create a server.js
file to start the server.
// server.js const app = require('./app'); const dotenv = require('dotenv'); dotenv.config(); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });
- Running the Application
8.1 Start the Server
Use nodemon
to start the server.
- Testing the API
9.1 Register a User
Send a POST request to /api/auth/register
with the following JSON body:
{ "name": "John Doe", "email": "[email protected]", "password": "password123" }
9.2 Login a User
Send a POST request to /api/auth/login
with the following JSON body:
{ "email": "[email protected]", "password": "password123" }
9.3 Create a Product
Send a POST request to /api/products
with the following JSON body and the token in the Authorization header:
{ "name": "Sample Product", "description": "This is a sample product", "price": 100, "countInStock": 10 }
9.4 Get All Products
Send a GET request to /api/products
.
9.5 Get a Product by ID
Send a GET request to /api/products/:id
.
9.6 Update a Product
Send a PUT request to /api/products/:id
with the updated product data and the token in the Authorization header.
9.7 Delete a Product
Send a DELETE request to /api/products/:id
with the token in the Authorization header.
Conclusion
In this section, we have built a simple e-commerce API using Node.js and Express.js. We covered setting up the project, creating models, controllers, and routes, implementing user authentication, and handling CRUD operations for products. This API can be further extended with more features such as order management, payment integration, and more.
Node.js Course
Module 1: Introduction to Node.js
Module 2: Core Concepts
Module 3: File System and I/O
Module 4: HTTP and Web Servers
Module 5: NPM and Package Management
- Introduction to NPM
- Installing and Using Packages
- Creating and Publishing Packages
- Semantic Versioning
Module 6: Express.js Framework
- Introduction to Express.js
- Setting Up an Express Application
- Middleware
- Routing in Express
- Error Handling
Module 7: Databases and ORMs
- Introduction to Databases
- Using MongoDB with Mongoose
- Using SQL Databases with Sequelize
- CRUD Operations
Module 8: Authentication and Authorization
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with Mocha and Chai
- Integration Testing
- Debugging Node.js Applications
Module 10: Advanced Topics
Module 11: Deployment and DevOps
- Environment Variables
- Using PM2 for Process Management
- Deploying to Heroku
- Continuous Integration and Deployment