Express API Structure Roadmap: From Spaghetti to Modular
A roadmap for structuring an Express API, from spaghetti to modular.
Express API Structure Roadmap: From Spaghetti to Modular
A roadmap for structuring an Express API, from spaghetti to modular. Each step is small and shippable.
Step 1: Start With app.js and a Few Routes
For a tiny app, put routes in app.js. app.get('/users', ...), app.post('/users', ...). Fine for prototypes.
Step 2: Add a Folder for Routes
When you have more than ~10 routes, move them into a routes/ folder. Each feature gets a file: routes/auth.js, routes/users.js.
Step 3: Mount Routers in app.js
app.use('/auth', require('./routes/auth'));
app.use('/users', require('./routes/users'));
app.js becomes thin: global middlewares, router mounts, error handler.
Step 4: Add Controllers
Move logic out of routes into controllers/. Routes only declare endpoints and bind to controllers.
Step 5: Add a Services Layer
When controllers get long, move logic into services/. Controllers parse, call, respond. Services do the work.
Step 6: Add Middlewares
Create a middlewares/ folder. auth.js, validate.js, error.js. Apply per route or per router.
Step 7: Add Validations
Create a validations/ folder with Zod schemas. Apply with a validate(schema) middleware per route.
Step 8: Add a Models Folder
Each Mongoose model in its own file: models/user.js, models/post.js.
Step 9: Add a Utils Folder
Shared helpers: ApiError, asyncHandler, logger, db connection.
Step 10: Add a Config Folder
Centralize env var loading and app-level config. No other file reads process.env directly.
Step 11: Version the API
Mount routers under /api/v1. When you make breaking changes, add v2. Old clients keep using v1.
Step 12: Move to the Module Pattern (For Large Apps)
Group each feature into a module: src/modules/users/ with router, controller, service, model, validations. Mount modules in app.js. Use for medium to large apps.
The Takeaway
The Express API structure roadmap: start with app.js, add a routes folder, mount routers, add controllers, add services, add middlewares, add validations, add models and utils folders, add a config folder, version the API, and move to the module pattern for large apps. Each step is small and shippable.
Start with app.js, add a routes folder, mount routers in app.js, add controllers, add a services layer, add middlewares, add validations, add models and utils folders, add a config folder, version the API, and move to the module pattern for large apps.
When you have more than ~10 routes. Move them into a routes/ folder with one file per feature. Mount each router in app.js with app.use('/path', router). app.js becomes thin.
When controllers get long. Move logic into services/. Controllers parse the request, call a service, send the response. Services do the work and are reusable and testable without an HTTP context.
For medium to large apps. Group each feature into a module (src/modules/users/ with router, controller, service, model, validations). Mount modules in app.js. For tiny apps, a flat structure is fine.
Mount routers under /api/v1, /api/v2, etc. app.use('/api/v1/users', require('./routes/v1/users')). Bump the version on breaking changes; non-breaking changes (adding fields) do not need a new version.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

