
When building a Node.js backend with Express, it’s tempting to keep everything in a single file β especially for small apps. But as your project grows, youβll quickly need a clean, modular structure thatβs easy to scale and maintain. Understanding the best Express.js project structure can greatly help.
In this post, youβll learn how to properly structure a scalable Express.js project, ideal for real-world applications like APIs, dashboards, or microservices.
π§ Why Project Structure Matters
- β Easier to maintain and debug
- β Encourages separation of concerns
- β Works well with unit testing and version control
- β Makes onboarding other developers faster
- β Keeps your project clean as it grows
π Recommended Project Structure
Hereβs a clean and scalable Express.js folder layout:
project-name/
βββ src/
β βββ config/ # Configuration files (env, db, etc.)
β βββ controllers/ # Route logic
β βββ models/ # Mongoose or DB models
β βββ routes/ # Route definitions
β βββ services/ # Business logic (e.g. auth, email)
β βββ middlewares/ # Express middlewares (auth, error handlers)
β βββ utils/ # Helper functions/utilities
β βββ app.js # Express app configuration
β βββ server.js # Entry point
βββ .env
βββ package.json
βββ README.md
π Step-by-Step Setup
1. Initialize the Project
npm init -y
npm install express dotenv
2. Create server.js
const app = require('./app');
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
3. Create app.js
const express = require('express');
const app = express();
const routes = require('./routes');
app.use(express.json());
app.use('/api', routes);
module.exports = app;
4. Create a Route & Controller
routes/index.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.get('/users', userController.getAllUsers);
module.exports = router;
controllers/userController.js
exports.getAllUsers = (req, res) => {
res.json({ message: 'List of users' });
};
5. Add .env
and Configuration
.env
PORT=3000
DB_URI=mongodb://localhost:27017/mydb
config/db.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.DB_URI);
console.log('MongoDB connected');
} catch (err) {
console.error(err);
process.exit(1);
}
};
module.exports = connectDB;
β¨ Bonus Tips for Scalable Architecture
- Use controllers for request handling logic
- Use services for business logic (e.g. user registration, token generation)
- Keep middlewares separate (auth, error handling, etc.)
- Group features by domain (e.g.
/users
,/auth
,/posts
) - Use tools like
Jest
orSupertest
for testing your endpoints - Add Swagger or Postman collections for documentation
π Useful Resources
- Express Official Documentation
- Node.js Best Practices
- Related: Building a Simple Chat Server with Node.js and WebSocket
β Final Thoughts
A scalable Express.js project is all about clean separation and maintainability. The better your foundation, the easier it is to grow your backend and onboard collaborators.
Start small, follow good folder structure, and refactor as new features come in.