In this tutorial, we’ll walk through building a full-stack CRUD (Create, Read, Update, Delete) application that also includes JWT token-based authentication, a login system, and a protected dashboard option. The project will be built using Node.js, Express, MongoDB, Redux Toolkit, and JWT.
What You’ll Learn
- How to set up a RESTful API with Express and MongoDB.
- How to implement JWT token authentication for login and access control.
- How to create React components that connect with the backend.
- How to manage state using Redux Toolkit for handling user data.
- How to implement CRUD operations (Create, Read, Update, Delete) for user management.
Why These Technologies?
- Node.js: Server-side JavaScript runtime for building scalable web apps.
- Express: A minimal, flexible Node.js web framework for building RESTful APIs.
- MongoDB: A NoSQL database that allows flexible data storage.
- Redux Toolkit: A library that simplifies state management in React applications.
- JWT (JSON Web Token): A secure method for transmitting information as a JSON object between client and server.
Backend: Building the REST API with JWT Authentication
We will start by setting up the backend with Node.js, Express, and MongoDB. Additionally, we will implement JWT authentication for securing routes.
Step 1: Install Dependencies
Run the following commands to install the required dependencies in your backend project folder:
Step 2: Create .env File
In your project’s root directory, create a .env file to manage sensitive information like your JWT secret key and MongoDB URI.
.env:
Step 3: Set Up the Server and Middleware
server.js: The core of your backend application where we initialize the Express server, connect to MongoDB, and set up routes.middleware/authMiddleware.js: A middleware to verify JWT tokens.
Create middleware/authMiddleware.js
Create server.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const dotenv = require('dotenv');
const authenticateToken = require('./middleware/authMiddleware');
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(bodyParser.json());
// MongoDB Connection
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
// User Schema
const userSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: { type: String, unique: true },
password: String,
});
const User = mongoose.model('User', userSchema);
// Register Route
app.post('/register', async (req, res) => {
const { firstName, lastName, email, password } = req.body;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const newUser = new User({ firstName, lastName, email, password: hashedPassword });
try {
await newUser.save();
res.status(201).json(newUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// Login Route
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) return res.status(400).send('User not found');
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).send('Invalid credentials');
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
// Protected Dashboard Route
app.get('/dashboard', authenticateToken, (req, res) => {
res.send(`Hello, ${req.user.firstName}! Welcome to your dashboard.`);
});
// CRUD Routes for Users
app.get('/users', authenticateToken, async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.post('/users', authenticateToken, async (req, res) => {
const { firstName, lastName, email, password } = req.body;
const newUser = new User({ firstName, lastName, email, password });
try {
await newUser.save();
res.status(201).json(newUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
app.put('/users/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
const { firstName, lastName, email, password } = req.body;
try {
const updatedUser = await User.findByIdAndUpdate(id, { firstName, lastName, email, password }, { new: true });
res.json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
app.delete('/users/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
try {
await User.findByIdAndDelete(id);
res.json({ message: 'User deleted successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Start Server
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
Frontend: React with Redux Toolkit and JWT Authentication
Now, let’s move on to the frontend. We’ll use React, Redux Toolkit, and Axios for state management and communication with the backend.
Step 1: Install Frontend Dependencies
Run the following command in your React project folder:
Step 2: Set Up Redux Store
Conclusion
In this tutorial, we’ve built a full-stack CRUD application with JWT authentication, using Node.js, Express, MongoDB, Redux Toolkit, and JWT. We’ve learned how to set up secure login, create a protected dashboard, and implement Create, Read, Update, and Delete operations for user data.
Now you can expand upon this setup, including adding features like role-based authentication, user profiles, or improving the UI to make it even more interactive!
