Kritim Yantra
Aug 02, 2025
Ever opened an Express project and felt like you were lost in a maze of random files? π§ Maybe youβve seen tutorials where everything gets dumped into a single app.js
, leaving you wondering:
βWhat happens when this grows to 10,000 lines of code?β
A messy structure leads to:
The good news? Setting up a clean, scalable Express.js project structure isnβt rocket science. In this guide, Iβll walk you through a battle-tested folder setup that works for small APIs and large-scale apps.
Letβs build this the right wayβfrom the start. π
If you're building a simple API, this minimal structure keeps things tidy:
project/
βββ .env # Environment variables
βββ app.js # Main Express app setup
βββ routes/
β βββ users.js # User-related routes
β βββ products.js # Product-related routes
βββ controllers/ # Business logic
β βββ userController.js
β βββ productController.js
βββ models/ # Database models (if using MongoDB/Mongoose)
β βββ User.js
βββ package.json
Key Points:
β Separation of concerns β Routes only handle HTTP, controllers handle logic.
β No chaos in app.js
β Keep it clean for middleware and setup only.
When your app grows, youβll need more organization. Hereβs a scalable setup:
project/
βββ config/
β βββ db.js # Database connection
β βββ passport.js # Auth strategies
βββ middleware/ # Custom middleware
β βββ auth.js
βββ models/ # Database models
β βββ User.js
βββ routes/
β βββ api/ # API routes (v1, v2...)
β β βββ v1/
β β β βββ user.routes.js
β β β βββ product.routes.js
β β βββ v2/ # Future API version
βββ services/ # Business logic (if controllers get too big)
β βββ userService.js
βββ utils/ # Helpers, utilities
β βββ logger.js
βββ tests/ # Tests (Jest/Mocha)
β βββ user.test.js
βββ app.js # Express setup
βββ server.js # Starts the app (with error handling)
β
Easy to scale β Adding new features wonβt break existing code.
β
Better team workflow β Developers know exactly where to add code.
β
Testing-friendly β Isolated modules = easier unit tests.
app.js
β The Heart of Your Express AppThis should only handle:
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(helmet());
// Routes
app.use('/api/v1/users', require('./routes/api/v1/user.routes'));
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
module.exports = app; // For testing
server.js
β Starts the AppSeparating server startup from app.js
helps with testing and deployment.
const app = require('./app');
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT} π`);
});
Bad: Putting logic inside routes (hard to test & reuse).
Good: Routes only define endpoints, controllers handle logic.
Example (user.routes.js
):
const express = require('express');
const router = express.Router();
const { getAllUsers, createUser } = require('../../controllers/userController');
router.get('/', getAllUsers);
router.post('/', createUser);
module.exports = router;
Example (userController.js
):
exports.getAllUsers = async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
index.js
Files for Clean ImportsInstead of:
const userRoutes = require('./routes/user.routes');
const productRoutes = require('./routes/product.routes');
Do this:
routes/index.js
: module.exports = {
userRoutes: require('./user.routes'),
productRoutes: require('./product.routes')
};
const { userRoutes, productRoutes } = require('./routes');
Create an errorHandler
middleware:
// middleware/errorHandler.js
module.exports = (err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Internal Server Error' });
};
config
Folder for Environment-Based SettingsExample (config/db.js
):
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('MongoDB Connected!');
} catch (err) {
console.error('DB Connection Error:', err.message);
process.exit(1); // Exit on failure
}
};
module.exports = connectDB;
A well-structured Express project:
β Saves time (no more "where does this code go?")
β Makes teamwork smoother (consistent structure = fewer conflicts)
β Prepares you for scaling (adding features wonβt break everything)
Now, over to you!
π Whatβs your biggest struggle with Express project structure?
π Any tips I missed? Drop them in the comments!
Happy coding! ππ₯
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google