<p>&nbsp;</p>
<p>In 2025, building fullstack applications is easier, faster, and more scalable than ever. Thanks to the powerful trio of&nbsp;<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&rsquo;ll Build</h2>
<p>For this tutorial, we&rsquo;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 &amp; Tech Stack</h2>
<ul>
<li>
<p><strong>Node.js</strong> &ndash; JavaScript runtime for backend</p>
</li>
<li>
<p><strong>Express.js</strong> &ndash; Lightweight web framework for Node.js</p>
</li>
<li>
<p><strong>MongoDB</strong> &ndash; NoSQL database</p>
</li>
<li>
<p><strong>Mongoose</strong> &ndash; ODM for MongoDB</p>
</li>
<li>
<p><strong>JWT</strong> &ndash; For authentication</p>
</li>
<li>
<p><strong>Tailwind CSS</strong> or basic HTML for the frontend</p>
</li>
<li>
<p><strong>Postman</strong> &ndash; For testing APIs</p>
</li>
<li>
<p><strong>Vite/React (optional)</strong> &ndash; 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 &amp;&amp; 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(() =&gt; console.log('MongoDB Connected'))
.catch((err) =&gt; console.error(err));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =&gt; console.log(`Server running on port ${PORT}`));
</code></pre>
<hr>
<h2>3. π§± Create User &amp; 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) =&gt; jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '7d' });
exports.register = async (req, res) =&gt; {
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) =&gt; {
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) =&gt; {
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 &amp; 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) =&gt; {
const tasks = await Task.find({ user: req.user._id });
res.json(tasks);
};
exports.addTask = async (req, res) =&gt; {
const task = await Task.create({ ...req.body, user: req.user._id });
res.status(201).json(task);
};
exports.updateTask = async (req, res) =&gt; {
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) =&gt; {
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 &lt;token&gt;</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>