Created
November 14, 2025 15:38
-
-
Save RoyBellingan/47973aa6651af8a851659485be3dc3cf to your computer and use it in GitHub Desktop.
Router Introspection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var finalhandler = require('finalhandler') | |
| var http = require('http') | |
| var Router = require('router') | |
| var url = require('url') | |
| // Helper to parse URL pathname (similar to parseurl) | |
| function parseUrl(req) { | |
| try { | |
| return url.parse(req.url || '').pathname | |
| } catch (e) { | |
| return undefined | |
| } | |
| } | |
| var router = Router() | |
| // Introspection middleware - capture router processing information | |
| function introspectRouter(layerPath) { | |
| return function (req, res, next) { | |
| // Store original request info before any modifications | |
| if (!req._routerInfo) { | |
| req._routerInfo = { | |
| original: { | |
| url: req.url, | |
| pathname: parseUrl(req).pathname | |
| }, | |
| processing: [] | |
| } | |
| } | |
| // Capture current state at this middleware layer | |
| var currentPathname = parseUrl(req).pathname | |
| var info = { | |
| layer: { | |
| path: layerPath || 'root', | |
| isRoute: !!req.route | |
| }, | |
| request: { | |
| url: req.url, | |
| originalUrl: req.originalUrl || req.url, | |
| baseUrl: req.baseUrl || '', | |
| pathname: currentPathname, | |
| route: req.route ? { | |
| path: req.route.path, | |
| methods: Object.keys(req.route.methods || {}).filter(function (m) { | |
| return req.route.methods[m] | |
| }) | |
| } : null | |
| }, | |
| remaining: { | |
| // What's left in req.url for the next handler | |
| remainingUrl: req.url, | |
| remainingPathname: currentPathname, | |
| // The prefix that was stripped (difference between original and current) | |
| strippedPrefix: req.baseUrl || '', | |
| // Calculate what remains after this layer | |
| remainingAfterPrefix: req.baseUrl ? | |
| req.url.replace(new RegExp('^' + req.baseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')), '') || '/' : | |
| req.url | |
| } | |
| } | |
| // Store this layer's processing info | |
| req._routerInfo.processing.push(info) | |
| // Log to console for debugging | |
| console.log('\n=== Router Introspection ===') | |
| console.log('Layer Path:', info.layer.path) | |
| console.log(' Is Route Handler:', info.layer.isRoute) | |
| if (info.layer.isRoute && req.route) { | |
| console.log(' Route Path:', req.route.path) | |
| console.log(' Allowed Methods:', info.request.route.methods.join(', ')) | |
| } | |
| console.log('\nRequest State:') | |
| console.log(' Original URL:', info.request.originalUrl) | |
| console.log(' Current URL:', info.request.url) | |
| console.log(' Base URL:', info.request.baseUrl) | |
| console.log(' Pathname:', info.request.pathname) | |
| console.log('\nRemaining for Next Handler:') | |
| console.log(' Remaining URL:', info.remaining.remainingUrl) | |
| console.log(' Remaining Pathname:', info.remaining.remainingPathname) | |
| console.log(' Stripped Prefix:', info.remaining.strippedPrefix) | |
| console.log(' Remaining After Prefix:', info.remaining.remainingAfterPrefix) | |
| console.log('===========================\n') | |
| next() | |
| } | |
| } | |
| // Enhanced introspection that also exposes router stack information | |
| function getRouterStackInfo(router) { | |
| return router.stack.map(function (layer, index) { | |
| return { | |
| index: index, | |
| path: layer.path || (layer.regexp ? layer.regexp.toString() : 'unknown'), | |
| name: layer.name || '<anonymous>', | |
| keys: layer.keys || [], | |
| isRoute: !!layer.route, | |
| routeInfo: layer.route ? { | |
| path: layer.route.path, | |
| methods: Object.keys(layer.route.methods || {}).filter(function (m) { | |
| return layer.route.methods[m] | |
| }) | |
| } : null | |
| } | |
| }) | |
| } | |
| // Middleware to attach router introspection utilities to request | |
| function attachIntrospection(routerInstance) { | |
| return function (req, res, next) { | |
| // Attach utility function to access router stack | |
| req.getRouterStack = function () { | |
| return getRouterStackInfo(routerInstance) | |
| } | |
| // Attach utility to get current processing info | |
| req.getRouterInfo = function () { | |
| return req._routerInfo || null | |
| } | |
| // Attach utility to see what's remaining | |
| req.getRemainingPath = function () { | |
| return { | |
| url: req.url, | |
| pathname: parseUrl(req).pathname, | |
| baseUrl: req.baseUrl || '', | |
| originalUrl: req.originalUrl || req.url | |
| } | |
| } | |
| next() | |
| } | |
| } | |
| // Apply introspection middleware | |
| router.use(attachIntrospection(router)) | |
| router.use('/', introspectRouter('/')) | |
| // Example: nested router composition | |
| var apiRouter = Router() | |
| apiRouter.use(attachIntrospection(apiRouter)) | |
| apiRouter.use('/', introspectRouter('/api')) | |
| apiRouter.get('/users/:id', function (req, res) { | |
| var info = req.getRouterInfo() | |
| var remaining = req.getRemainingPath() | |
| res.setHeader('Content-Type', 'application/json; charset=utf-8') | |
| res.end(JSON.stringify({ | |
| message: 'User endpoint', | |
| userId: req.params.id, | |
| introspection: { | |
| currentProcessing: info, | |
| remainingPath: remaining, | |
| routerStack: req.getRouterStack() | |
| } | |
| }, null, 2)) | |
| }) | |
| // Mount the nested router | |
| router.use('/api', apiRouter) | |
| router.use('/api/', attachIntrospection(apiRouter)) | |
| router.use('/api/', introspectRouter('/api/')) | |
| router.get('/api/', function (req, res) { | |
| res.setHeader('Content-Type', 'application/json; charset=utf-8') | |
| res.end(JSON.stringify({ | |
| message: '/api/ endpoint', | |
| introspection: { | |
| currentProcessing: req.getRouterInfo(), | |
| remainingPath: req.getRemainingPath(), | |
| routerStack: req.getRouterStack() | |
| } | |
| }, null, 2)) | |
| }) | |
| router.get('/', function (req, res) { | |
| res.setHeader('Content-Type', 'text/plain; charset=utf-8') | |
| res.end('Hello World!\n\nUse /api/users/123 to see introspection in action.') | |
| }) | |
| // Expose router introspection endpoint | |
| router.get('/introspect', function (req, res) { | |
| var stackInfo = getRouterStackInfo(router) | |
| res.setHeader('Content-Type', 'application/json; charset=utf-8') | |
| res.end(JSON.stringify({ | |
| routerStack: stackInfo, | |
| message: 'This shows all registered routes and middleware in the router stack' | |
| }, null, 2)) | |
| }) | |
| var server = http.createServer(function (req, res) { | |
| router(req, res, finalhandler(req, res)) | |
| }) | |
| server.listen(3000) | |
| console.log('Server running on http://localhost:3000') | |
| console.log('Try:') | |
| console.log(' - http://localhost:3000/') | |
| console.log(' - http://localhost:3000/api/users/123') | |
| console.log(' - http://localhost:3000/introspect') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment