How to Structure an Express Project: Best Practices and Example
routes for URL handlers, controllers for business logic, and middleware for reusable functions. Keep your app.js or server.js file simple by importing these modules, which makes your project easier to maintain and scale.Syntax
An Express project typically has a main file like app.js or server.js that sets up the server and imports routes. Routes define URL paths and link to controller functions that handle requests. Middleware functions run before routes to process requests or handle errors.
- app.js/server.js: Entry point to start the server.
- routes/: Folder for route definitions.
- controllers/: Folder for request handling logic.
- middleware/: Folder for reusable functions like authentication.
- models/: (optional) Folder for database models.
const express = require('express'); const app = express(); const userRoutes = require('./routes/userRoutes'); app.use(express.json()); app.use('/users', userRoutes); app.listen(3000, () => { console.log('Server running on port 3000'); });
Example
This example shows a simple Express project structure with separate files for routes and controllers. The app.js file imports the user routes and starts the server. The routes/userRoutes.js file defines URL paths and connects them to controller functions. The controllers/userController.js file contains the logic to handle requests.
// app.js const express = require('express'); const app = express(); const userRoutes = require('./routes/userRoutes'); app.use(express.json()); app.use('/users', userRoutes); app.listen(3000, () => { console.log('Server running on port 3000'); }); // routes/userRoutes.js const express = require('express'); const router = express.Router(); const { getUsers, getUserById } = require('../controllers/userController'); router.get('/', getUsers); router.get('/:id', getUserById); module.exports = router; // controllers/userController.js const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]; exports.getUsers = (req, res) => { res.json(users); }; exports.getUserById = (req, res) => { const user = users.find(u => u.id === parseInt(req.params.id)); if (!user) return res.status(404).json({ message: 'User not found' }); res.json(user); };
Common Pitfalls
Common mistakes include putting all code in app.js, which makes the project hard to read and maintain. Another pitfall is mixing route definitions with business logic instead of separating them into controllers. Forgetting to use express.json() middleware can cause issues parsing JSON request bodies.
// Wrong: All code in app.js const express = require('express'); const app = express(); app.get('/users', (req, res) => { res.json([{ id: 1, name: 'Alice' }]); }); app.listen(3000); // Right: Separate routes and controllers // routes/userRoutes.js const express = require('express'); const router = express.Router(); const { getUsers } = require('../controllers/userController'); router.get('/', getUsers); module.exports = router; // controllers/userController.js exports.getUsers = (req, res) => { res.json([{ id: 1, name: 'Alice' }]); };
Quick Reference
- app.js/server.js: Start server, import routes.
- routes/: Define URL paths, connect to controllers.
- controllers/: Handle request logic.
- middleware/: Functions for request processing.
- models/: Database schemas (optional).
- Use
express.json()to parse JSON bodies. - Keep files small and focused on one task.