
Creating Your Own ExpressJS from Scratch (Part 4) - Modular Router and Global Middlewares
In the previous part, we learned how to use the middleware pattern to decorate and enhance request
and response
objects. Now it's time to go deeper by creating a modular router system and supporting global middlewares for all routes.
By the end of this part, you'll be able to:
- Add global middleware to all routes
- Apply middleware to specific paths regardless of HTTP method
- Create and use modular router instances — just like
express.Router()
🌍 Global Middlewares
1. Store Global Middlewares
const middlewaresForAll = [];
We need a place to store all global middlewares that apply to every route.
2. Define Middleware for Specific Paths
const use = (path, ...middlewares) => {
const possiblePaths = [path + '/GET', path + '/POST', path + '/PUT', path + '/PATCH', path + '/DELETE'];
possiblePaths.forEach(route => {
const current = routes.get(route) || [];
if (current.length) {
routes.set(route, [...middlewares, ...current]);
}
});
};
This function allows you to register middleware for a specific path, regardless of the HTTP method.
3. Define Middleware for All Routes
const useAll = (...middlewares) => {
middlewaresForAll.push(...middlewares);
};
This is how we store global middlewares that apply to every route.
4. Update the Server Handler
const serverHandler = async (req, res) => {
const sanitizedUrl = sanitizeUrl(req.url, req.method);
const match = matchUrl(sanitizedUrl);
if (match) {
const handlers = routes.get(match);
await dispatchChain(req, res, [
requestDecorator.bind(null, routes.keys()),
responseDecorator,
...middlewaresForAll,
...handlers
]);
} else {
res.statusCode = 404;
res.end('Not found');
}
};
Now every route can include decorators and global middlewares before the route-specific ones.
🧱 Modular Routers
Modular routers let you organize route logic into reusable units.
src/router.js
const Router = () => {
const routes = new Map();
const middlewaresForAll = [];
const getRoutes = () => routes;
const getMiddlewaresForAll = () => middlewaresForAll;
const useAll = (...middlewares) => {
middlewaresForAll.push(...middlewares);
};
const use = (path, ...middlewares) => {
const possiblePaths = [path + '/GET', path + '/POST', path + '/PUT', path + '/PATCH', path + '/DELETE'];
possiblePaths.forEach(route => {
const current = routes.get(route) || [];
if (current.length) {
routes.set(route, [...middlewares, ...current]);
}
});
};
const register = (method) => (path, ...handlers) => {
const key = `${path}/${method}`;
const current = routes.get(key) || [];
routes.set(key, [...current, ...handlers]);
};
return {
get: register('GET'),
post: register('POST'),
put: register('PUT'),
patch: register('PATCH'),
del: register('DELETE'),
use,
useAll,
getRoutes,
getMiddlewaresForAll
};
};
module.exports = Router;
This is a complete standalone router object that stores its own routes and middlewares.
🔗 Integrating a Router into the App
In your app.js
, add the following method:
const useRouter = (basePath, router) => {
const routerRoutes = router.getRoutes();
const routerMiddleware = router.getMiddlewaresForAll();
for (const [key, handlers] of routerRoutes) {
const newKey = `${basePath}${key}`;
routes.set(newKey, [...routerMiddleware, ...handlers]);
}
};
This allows you to attach a mini-router at any path in your main app.
🧪 Testing It All Together
const App = require('./src/app');
const Router = require('./src/router');
const app = App();
const router = Router();
router.get('/users', (req, res) => res.end('User route'));
router.get('/admins', (req, res) => res.end('Admin route'));
router.useAll((req, res, next) => {
console.log('router global middleware');
next();
});
router.use('/users', (req, res, next) => {
console.log('users route middleware');
next();
});
app.useRouter('', router);
app.use('/admins', (req, res, next) => {
console.log('admin route middleware');
next();
});
app.useAll((req, res, next) => {
console.log('global middleware');
next();
});
app.run(3000);
Now try:
curl http://localhost:3000/admins
You’ll see logs for:
global middleware
admin route middleware
router global middleware
📚 Summary
- You’ve implemented global and route-level middlewares
- You can now modularize routes using a custom router
- Middlewares execute in the correct order, just like Express!
👉 Next Up:
In Part 5, we’ll add static file serving capabilities to our framework.
See you soon! 🚀