Building a full-stack CRUD (Create, Read, Update, Delete) application is a foundational skill for web developers. In this tutorial, we’ll create a complete application using Node.js for the backend and MongoDB for the database. This app will also include user login and logout functionalities using JSON Web Tokens (JWT).
Prerequisites
Before you begin, ensure you have the following:
- Node.js installed on your system.
- MongoDB installed locally or access to a MongoDB Atlas cluster.
- A basic understanding of JavaScript and REST APIs.
- A text editor like Visual Studio Code.
Step 1: Setting Up the Project
1.1 Initialize the Project
Open your terminal and create a new project directory:
mkdir fullstack-crud-nodejs-mongodb
cd fullstack-crud-nodejs-mongodb
Initialize a new Node.js project:
npm init -y
Install the required dependencies:
npm install express mongoose bcryptjs jsonwebtoken dotenv cors body-parser
npm install --save-dev nodemon
1.2 Project Structure
Create the following folder structure:
fullstack-crud-nodejs-mongodb/
|-- models/
|-- routes/
|-- controllers/
|-- config/
|-- .env
|-- server.js
Step 2: Setting Up the Backend
2.1 Configure Environment Variables
Create a .env file in the root directory:
PORT=5000
MONGO_URI=mongodb://localhost:27017/crud-app
JWT_SECRET=your_jwt_secret
2.2 Connect to MongoDB
Create a config/database.js file:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('MongoDB connected');
} catch (err) {
console.error(err.message);
process.exit(1);
}
};
module.exports = connectDB;
Update server.js to include the database connection:
require('dotenv').config();
const express = require('express');
const connectDB = require('./config/database');
const cors = require('cors');
const app = express();
connectDB();
app.use(cors());
app.use(express.json());
app.listen(process.env.PORT, () => {
console.log(`Server running on port ${process.env.PORT}`);
});
2.3 Create the User Model
In the models/ folder, create User.js:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
UserSchema.pre('save', async function (next) {
if (!this.isModified('password')) return next();
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
module.exports = mongoose.model('User', UserSchema);
2.4 Create Authentication Routes
In the routes/ folder, create auth.js:
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const User = require('../models/User');
const router = express.Router();
// Register
router.post('/register', async (req, res) => {
const { username, email, password } = req.body;
try {
const newUser = new User({ username, email, password });
await newUser.save();
res.status(201).json({ message: 'User registered successfully' });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Login
router.post('/login', async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) return res.status(404).json({ message: 'User not found' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
module.exports = router;
Add this route to server.js:
const authRoutes = require('./routes/auth');
app.use('/api/auth', authRoutes);
2.5 Create CRUD Operations for a Resource
In the models/ folder, create Post.js:
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
});
module.exports = mongoose.model('Post', PostSchema);
In the routes/ folder, create posts.js:
const express = require('express');
const Post = require('../models/Post');
const router = express.Router();
// Create Post
router.post('/', async (req, res) => {
const { title, content, userId } = req.body;
try {
const newPost = new Post({ title, content, user: userId });
await newPost.save();
res.status(201).json(newPost);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Read All Posts
router.get('/', async (req, res) => {
try {
const posts = await Post.find().populate('user', 'username email');
res.json(posts);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Update Post
router.put('/:id', async (req, res) => {
try {
const updatedPost = await Post.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(updatedPost);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Delete Post
router.delete('/:id', async (req, res) => {
try {
await Post.findByIdAndDelete(req.params.id);
res.json({ message: 'Post deleted successfully' });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
module.exports = router;
Add this route to server.js:
const postRoutes = require('./routes/posts');
app.use('/api/posts', postRoutes);
Step 3: Testing the API
Use a tool like Postman to test the following endpoints:
Register User: POST /api/auth/register
Login User: POST /api/auth/login
Create Post: POST /api/posts
Get All Posts: GET /api/posts
Update Post: PUT /api/posts/:id
Delete Post: DELETE /api/posts/:id
Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.
Post a Comment for "Building a Full-Stack CRUD Application with Node.js and MongoDB"