AI & Dev All Articles

πŸ› οΈ How to Build a Fullstack App with Node.js, Express & MongoDB in 2025

<p> </p> <p>In 2025, building fullstack applications is easier, faster, and more scalable th...

πŸ› οΈ How to Build a Fullstack App with Node.js, Express & MongoDB in 2025
Author
Manasissotechy admin
πŸ“… May 12, 2025 ⏱ 1 min read πŸ‘ 0 views

<p> </p>
<p>In 2025, building fullstack applications is easier, faster, and more scalable than ever. Thanks to the powerful trio of <strong>Node.js</strong>, <strong>Express.js</strong>, and <strong>MongoDB</strong>, you can build modern web applications with speed and efficiency.</p>
<p>Whether you're creating a blog, SaaS tool, or e-commerce store, this tech stack provides the flexibility and power you need.</p>
<hr>
<h2>πŸ”§ What You’ll Build</h2>
<p>For this tutorial, we’ll build a simple <strong>"Task Manager" app</strong> where users can:</p>
<ul>
<li>
<p>Register/Login</p>
</li>
<li>
<p>Create, Read, Update, and Delete (CRUD) tasks</p>
</li>
<li>
<p>View their tasks on a dashboard</p>
</li>
</ul>
<hr>
<h2>πŸš€ Tools & Tech Stack</h2>
<ul>
<li>
<p><strong>Node.js</strong> – JavaScript runtime for backend</p>
</li>
<li>
<p><strong>Express.js</strong> – Lightweight web framework for Node.js</p>
</li>
<li>
<p><strong>MongoDB</strong> – NoSQL database</p>
</li>
<li>
<p><strong>Mongoose</strong> – ODM for MongoDB</p>
</li>
<li>
<p><strong>JWT</strong> – For authentication</p>
</li>
<li>
<p><strong>Tailwind CSS</strong> or basic HTML for the frontend</p>
</li>
<li>
<p><strong>Postman</strong> – For testing APIs</p>
</li>
<li>
<p><strong>Vite/React (optional)</strong> – If you want a modern frontend</p>
</li>
</ul>
<hr>
<h2>πŸ—‚οΈ Folder Structure</h2>
<pre><code class="language-bash">task-manager/
β”‚
β”œβ”€β”€ backend/
β”‚ β”œβ”€β”€ config/
β”‚ β”œβ”€β”€ controllers/
β”‚ β”œβ”€β”€ models/
β”‚ β”œβ”€β”€ routes/
β”‚ β”œβ”€β”€ middleware/
β”‚ β”œβ”€β”€ server.js
β”‚ └── .env
β”‚
β”œβ”€β”€ frontend/ (optional React/Vite setup)
β”‚
└── README.md
</code></pre>
<hr>
<h2>1. βœ… Initialize Your Project</h2>
<pre><code class="language-bash">mkdir task-manager
cd task-manager
mkdir backend && cd backend
npm init -y
</code></pre>
<p>Install dependencies:</p>
<pre><code class="language-bash">npm install express mongoose dotenv cors jsonwebtoken bcryptjs
npm install --save-dev nodemon
</code></pre>
<p>Add this to <code>package.json</code> for hot reloading:</p>
<pre><code class="language-json">"scripts": {
"dev": "nodemon server.js"
}
</code></pre>
<hr>
<h2>2. 🌐 Setup Express Server</h2>
<p><code>backend/server.js</code></p>
<pre><code class="language-js">const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const cors = require('cors');

dotenv.config();
const app = express();

app.use(cors());
app.use(express.json());

// Routes
app.use('/api/auth', require('./routes/authRoutes'));
app.use('/api/tasks', require('./routes/taskRoutes'));

// MongoDB Connection
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('MongoDB Connected'))
.catch((err) => console.error(err));

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
</code></pre>
<hr>
<h2>3. 🧱 Create User & Task Models</h2>
<p><code>models/User.js</code></p>
<pre><code class="language-js">const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, unique: true },
password: { type: String, required: true }
});

// Encrypt password before saving
userSchema.pre('save', async function () {
if (!this.isModified('password')) return;
this.password = await bcrypt.hash(this.password, 12);
});

module.exports = mongoose.model('User', userSchema);
</code></pre>
<p><code>models/Task.js</code></p>
<pre><code class="language-js">const mongoose = require('mongoose');

const taskSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
title: String,
completed: { type: Boolean, default: false },
}, { timestamps: true });

module.exports = mongoose.model('Task', taskSchema);
</code></pre>
<hr>
<h2>4. πŸ” Authentication with JWT</h2>
<p><code>routes/authRoutes.js</code></p>
<pre><code class="language-js">const express = require('express');
const { register, login } = require('../controllers/authController');
const router = express.Router();

router.post('/register', register);
router.post('/login', login);

module.exports = router;
</code></pre>
<p><code>controllers/authController.js</code></p>
<pre><code class="language-js">const User = require('../models/User');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

const generateToken = (id) => jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '7d' });

exports.register = async (req, res) => {
const { name, email, password } = req.body;
const userExists = await User.findOne({ email });
if (userExists) return res.status(400).json({ msg: 'User already exists' });

const user = await User.create({ name, email, password });
const token = generateToken(user._id);
res.status(201).json({ token, user: { id: user._id, name: user.name } });
};

exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).json({ msg: 'Invalid credentials' });
}
const token = generateToken(user._id);
res.json({ token, user: { id: user._id, name: user.name } });
};
</code></pre>
<hr>
<h2>5. πŸ›‘οΈ Middleware: Protect Routes</h2>
<p><code>middleware/authMiddleware.js</code></p>
<pre><code class="language-js">const jwt = require('jsonwebtoken');
const User = require('../models/User');

const protect = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ msg: 'Not authorized' });

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select('-password');
next();
} catch {
res.status(401).json({ msg: 'Token failed' });
}
};

module.exports = protect;
</code></pre>
<hr>
<h2>6. πŸ“‹ Task Routes & Controllers</h2>
<p><code>routes/taskRoutes.js</code></p>
<pre><code class="language-js">const express = require('express');
const protect = require('../middleware/authMiddleware');
const { getTasks, addTask, updateTask, deleteTask } = require('../controllers/taskController');
const router = express.Router();

router.route('/').get(protect, getTasks).post(protect, addTask);
router.route('/:id').put(protect, updateTask).delete(protect, deleteTask);

module.exports = router;
</code></pre>
<p><code>controllers/taskController.js</code></p>
<pre><code class="language-js">const Task = require('../models/Task');

exports.getTasks = async (req, res) => {
const tasks = await Task.find({ user: req.user._id });
res.json(tasks);
};

exports.addTask = async (req, res) => {
const task = await Task.create({ ...req.body, user: req.user._id });
res.status(201).json(task);
};

exports.updateTask = async (req, res) => {
const task = await Task.findById(req.params.id);
if (task.user.toString() !== req.user._id.toString()) return res.status(401).send("Unauthorized");
const updated = await Task.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(updated);
};

exports.deleteTask = async (req, res) => {
const task = await Task.findById(req.params.id);
if (task.user.toString() !== req.user._id.toString()) return res.status(401).send("Unauthorized");
await task.deleteOne();
res.json({ msg: 'Task removed' });
};
</code></pre>
<hr>
<h2>7. πŸ§ͺ Testing APIs with Postman</h2>
<p>Use Postman to:</p>
<ul>
<li>
<p>Register a new user (<code>POST /api/auth/register</code>)</p>
</li>
<li>
<p>Login (<code>POST /api/auth/login</code>)</p>
</li>
<li>
<p>Get JWT and use it in Headers as:<br><code>Authorization: Bearer <token></code></p>
</li>
<li>
<p>Create, fetch, update, and delete tasks via respective endpoints</p>
</li>
</ul>
<hr>
<h2>8. 🎨 Add a Frontend (Optional)</h2>
<p>You can use <strong>React + Vite</strong> or any frontend stack:</p>
<ul>
<li>
<p>Fetch tasks from <code>/api/tasks</code></p>
</li>
<li>
<p>Use Axios or Fetch API to handle login/register</p>
</li>
<li>
<p>Store token in <code>localStorage</code> or <code>Cookies</code></p>
</li>
<li>
<p>Protect frontend routes using the token</p>
</li>
</ul>
<hr>
<h2>πŸ“¦ Deployment</h2>
<ul>
<li>
<p><strong>Backend</strong>: Deploy on <a href="https://render.com/">Render</a>, <a href="https://railway.app/">Railway</a>, or <a href="https://vercel.com/">Vercel</a></p>
</li>
<li>
<p><strong>Database</strong>: MongoDB Atlas</p>
</li>
<li>
<p><strong>Frontend</strong>: Vercel or Netlify</p>
</li>
</ul>
<p>Make sure to set environment variables like <code>MONGO_URI</code> and <code>JWT_SECRET</code>.</p>
<hr>
<h2>🧠 Final Thoughts</h2>
<p>In 2025, building fullstack applications with Node.js, Express, and MongoDB is still an excellent and in-demand approach. With rapid development, RESTful APIs, and easy deployment options, this stack empowers you to build scalable, real-world apps quickly.</p>