Express Router Best Practices for Large Apps
As your Express app grows, router organization matters. Here are best practices for large apps.
Express Router Best Practices for Large Apps
As your Express app grows, putting every route in app.js becomes a mess. Here are router best practices for large apps.
1. One Router Per Feature
Create a router per feature: routes/auth.js, routes/users.js, routes/feed.js, routes/chat.js, routes/payments.js. Each file owns its routes.
2. Mount Under a Path
app.use('/auth', require('./routes/auth'));
app.use('/users', require('./routes/users'));
app.use('/feed', require('./routes/feed'));
app.use('/chat', require('./routes/chat'));
app.use('/payments', require('./routes/payments'));
The router's paths are relative to the mount path. Clean and discoverable.
3. Version Your API
app.use('/api/v1/auth', require('./routes/v1/auth'));
app.use('/api/v1/users', require('./routes/v1/users'));
When you make breaking changes, add v2. Old clients keep using v1.
4. Apply Per-Router Middlewares
// routes/admin.js
router.use(auth);
router.use(requireRole('admin'));
router.get('/stats', getStats);
Every route in admin.js requires auth and admin. No need to repeat per route.
5. Keep Public Routes Outside Auth Routers
// routes/auth.js
router.post('/signup', signup); // public
router.post('/login', login); // public
router.use(auth); // everything below requires auth
router.post('/logout', logout);
router.get('/me', getMe);
router.patch('/me', updateMe);
6. Use a Service Layer
Routes bind to controllers; controllers call services. Keep logic out of routes.
// routes/users.js
const { listUsers, createUser, getUser } = require('../controllers/userController');
router.get('/', listUsers);
router.post('/', createUser);
router.get('/:id', getUser);
7. Use Validation Middleware Per Route
router.post('/', validate(createUserSchema), createUser);
router.patch('/:id', validate(updateUserSchema), updateUser);
8. Use Async Handlers
Wrap async controllers with asyncHandler so rejections go to the error handler:
const asyncHandler = fn => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
router.get('/', asyncHandler(listUsers));
9. Nested Routers for Nested Resources
// routes/users.js
const postsRouter = require('./posts');
router.use('/:userId/posts', postsRouter);
Now /users/:userId/posts/... hits postsRouter. Limit nesting to one level.
10. Keep app.js Thin
app.js should only: create the app, apply global middlewares (json, cors, helmet), mount routers, register the error handler. Everything else lives in feature folders.
The Takeaway
For large Express apps: one router per feature, mount under a path, version your API, apply per-router middlewares, keep public routes outside auth routers, use a service layer, validate per route, use async handlers, nest routers for nested resources, and keep app.js thin.
One router per feature (routes/auth.js, routes/users.js, etc.). Mount each under a path in app.js. Apply per-router middlewares. Keep public routes outside auth routers. Keep app.js thin: only global middlewares, router mounts, and the error handler.
Mount routers under /api/v1, /api/v2, etc. Each version has its own router. Bump the version on breaking changes. Non-breaking changes (adding fields) do not need a new version.
Use router.use(middleware) at the top of the router file. Every route in the router now runs that middleware. Useful for auth and role checks across a feature.
Yes for nested resources (e.g., /users/:userId/posts). Use router.use('/:userId/posts', postsRouter). Limit nesting to one level. For deeper resources, use a separate top-level router with a filter.
Only: create the app, apply global middlewares (json, cors, helmet), mount routers with app.use, and register the error handler. Everything else lives in feature folders. A thin app.js is a sign of a well-organized app.
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.

