Sign in
Topics
Create web and mobile apps using modular code and simple, natural prompts.
This article provides a practical guide to best practices for the Express.js folder structure. It explains organizing routes, models, middleware, business logic, and configuration files for better clarity and collaboration. With real examples and clear reasoning, it shows how a structured layout can simplify scaling and speed up development.
How often do you waste time hunting for the right file in your Express app?
When the folder structure isnβt clear, the codebase becomes harder to follow. This makes debugging, testing, and team collaboration more difficult, making scaling the project challenging.
This article covers the best practices for the Express.js folder structure to help you build clean, organized projects. Youβll learn where to place routes, models, middleware, business logic , and config filesβwith examples and reasons behind each choice.
Letβs see how structure brings order to your project.
Build a well-organized folder structure for large Express apps
Separate business logic from route handling and configuration
Use a src folder to group core backend logic
Group middleware, helpers, and utility functions in their folders
Define environment variables for database connection and auth configuration
Express.js follows a minimalist architecture, often starting with a single file entry point like app.js
or server.js
. From there, the app listens for incoming requests, processes them using middleware functions, and responds based on defined routes. Developers often create multiple files and folders to keep things clean and scalable.
Routes Layer β Manages endpoints and maps them to functions
Controllers/Handlers Layer β Processes logic for route requests
Services Layer β Contains business logic or calls to data stores
Model Layer β Interfaces with the database
Middlewares Layer β Handles authentication, logging, validation, etc.
Configuration Layer β Loads configuration variables and setup
Here's a Mermaid diagram to visualize the architecture:
Poor structure leads to tangled code, duplication, and hard-to-track bugs. As apps grow, a well-organized folder structure supports modularity and helps developers create multiple files with clarity. You also improve the separation of concerns, which is key for testing and reusability.
Faster debugging and onboarding
Cleaner integration tests and routing logic
Easier handling of static files and view files
Improved scalability by abstracting core logic
Skip manual setupβRocket generates a complete, well-structured app from a single prompt, including routes, business logic, and configuration.
Hereβs a standard and scalable Express.js project folder structure often used in production apps.
project-root/
β
βββ src/
β βββ config/
β β βββ database.js
β β βββ app.config.js
β βββ controllers/
β β βββ user.controller.js
β βββ models/
β β βββ user.model.js
β βββ routes/
β β βββ user.routes.js
β βββ middlewares/
β β βββ auth.middleware.js
β βββ services/
β β βββ user.service.js
β βββ utils/
β β βββ hash.util.js
β βββ index.js
β
βββ public/
β βββ static files, css files
β
βββ views/
β βββ view files, multiple layouts
β
βββ .env
βββ package.json
βββ server.js
Folder Name | Purpose |
---|---|
src | Houses all business logic and application code |
routes | Define all app related routes and create multiple route files |
controllers | Handle incoming requests and send responses |
models | Define database schema like user model |
middlewares | Auth, error handling, attaching custom headers |
services | Core business logic and database actions |
utils | Helper functions, utility functions, reusable code |
config | Manage configuration and environment variables |
Use a standalone model file per entity, and a corresponding controller and service layer to create users or handle other actions.
The src folder keeps app logic separated from configs and static files. It acts as the core engine of your Express app, reducing clutter in your root project folder.
Avoid putting all routes in a single route file. Instead, create multiple route files based on domains. Use const router = express.Router()
to modularize them.
Keep environment-related configuration files like env.config.js
or app.config.js
in one folder. Use a configuration structure that loads database keys and other variables.
Never mix route logic with data operations. Use services to manage critical API related actions. This separation helps in unit testing and allows better reuse.
Put CSS files , JavaScript, and other static files inside /public
. Keep multiple layouts and view files for templating engines like EJS or Pug in the views folder.
βHow to Structure Your Backend Code in Node.js (Express.js) β by Muhammadβ―Mubeenβ―Yasin β Decemberβ―2024.
Keep all configuration variables in a .env
file. Use libraries like dotenv to load them into your config layer. This includes database connection strings, API keys, and port settings.
Define a sample configuration file like .env.example
Always store relevant keys, default values, and auth configuration
Manage configuration changes using separate files per environment (dev, prod)
Example:
1// config/database.js 2const mongoose = require('mongoose'); 3const dbURI = process.env.DB_URI; 4 5const connectDB = async () => { 6 await mongoose.connect(dbURI); 7}; 8module.exports = connectDB;
This helps abstract database-specific configurations to one file and simplifies maintenance.
Donβt catch errors inside every route. Instead, define global error handlers in middleware. This centralizes logic and keeps the route code clean.
1// middlewares/error.middleware.js 2module.exports = (err, req, res, next) => { 3 res.status(err.status || 500).json({ message: err.message }); 4};
Handle user-posted payload and acceptable request payload size using custom middleware or libraries like express-validator.
Place helper functions and utility functions under /utils
. Unlike utility methods, use helpers for project-specific formatting or validations. Keep commonly used logic in separate files to avoid rewriting code.
Common examples:
Data formatting
Password hashing
Token generation
Attaching custom headers
Use an tests
folder inside src
for integration tests. This helps isolate test logic from app logic. Always document the project structure and API endpoints .
Don't forget to use:
framework build tools like Nodemon
ESLint and Prettier for code formatting
Swagger for documenting routes and user inputs
Before wrapping up any express.js project, confirm these:
β Are middleware functions isolated?
β Is business logic kept in services?
β Do configuration files manage all environment variables?
β Is the database connection centralized?
β Do you use a src folder for app logic?
β Are all static files under /public?
β Have you created integration tests?
Organizing your project with a clear structure helps avoid delays and confusion as your app grows. Separate your routes, business logic, and configuration files to keep everything easy to find and update.
Following the express.js folder structure best practices supports cleaner code and faster teamwork. Apply them to new or existing projects to improve clarity, speed, and long-term stability.