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!