Skip to content

Instantly share code, notes, and snippets.

@RoyBellingan
Created November 14, 2025 15:38
Show Gist options
  • Select an option

  • Save RoyBellingan/47973aa6651af8a851659485be3dc3cf to your computer and use it in GitHub Desktop.

Select an option

Save RoyBellingan/47973aa6651af8a851659485be3dc3cf to your computer and use it in GitHub Desktop.
Router Introspection
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