260

I have a web application built using Node.js and Express. Now I would like to list all registered routes with their appropriate methods.

E.g., if I have executed

app.get('/', function (...) { ... });
app.get('/foo/:id', function (...) { ... });
app.post('/foo/:id', function (...) { ... });

I would like to retrieve an object (or something equivalent to that) such as:

{
  get: [ '/', '/foo/:id' ],
  post: [ '/foo/:id' ]
}

Is this possible, and if so, how?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Golo Roden
  • 140,679
  • 96
  • 298
  • 425
  • 1
    All of the solutions I tried, do not work when you have Routers defined. It only works per route - which does not give me the entire url for that route in my app... – guy mograbi Jan 07 '19 at 22:06
  • 1
    @guymograbi you might want to look at https://stackoverflow.com/a/55589657/6693775 – NBSamar Feb 07 '20 at 07:17
  • 1
    I created [collect-express-routes](https://www.npmjs.com/package/collect-express-routes) which works on both express app and express.Router – Beeno Tung Oct 23 '21 at 03:46

33 Answers33

305

express 3.x

Okay, found it myself ... it's just app.routes :-)

express 4.x

Applications - built with express()

app._router.stack

Routers - built with express.Router()

router.stack

Note: The stack includes the middleware functions too, it should be filtered to get the "routes" only.

laconbass
  • 17,080
  • 8
  • 46
  • 54
Golo Roden
  • 140,679
  • 96
  • 298
  • 425
  • I am using node 0.10 and it was `app.routes.routes` - which means I could do JSON.stringify(app.routes.routes) – guy mograbi Feb 01 '14 at 15:33
  • 9
    Only works for Express 3.x, not 4.x. In 4.x, you should check `app._router.stack` – avetisk Apr 28 '14 at 11:53
  • 27
    This did not work as expected for me. app._router doesn't seem to include routes from app.use('/path', otherRouter); – Michael Cole Nov 07 '14 at 15:53
  • Is there some way that this could be integrated with a command-line script that would pull in exactly the same route files that the live app does without actually starting a web app? – Lawrence I. Siden Nov 28 '14 at 21:39
  • 7
    At least in express 4.13.1 `app._router.stack` is undefined. – levigroker Sep 07 '15 at 16:20
  • app.router.stack for express 5 – Erdi Apr 07 '16 at 22:31
  • With my tests in Express 4.13.4 `app._router.stack` seems to be defined (again?) – RyanWilcox Nov 19 '16 at 20:52
  • The OP wanted the HTTP methods (GET or POST). Where are those HTTP methods in the stack object? I only see the paths (at `stack[0].route.path`). – dcorking Oct 11 '18 at 09:30
  • Pls edit your answer to add a way to get the methods. For example, this works for me: `router.stack.forEach(l => console.log(l.route.path, l.route.methods))` – dcorking Oct 11 '18 at 09:32
  • I am wiriting a test case and want all routes to be displayed. I am importing the app file and then in that I am trying to get the app._router.stack. But it says _router does not exist on type app. I am using express 4.15.3. – Saurabh Ghewari Mar 16 '21 at 15:30
  • Commented because I am finding many others are still looking for solution. If you are still looking for solution(one env var), you might find this useful: https://stackoverflow.com/questions/14934452/how-to-get-all-registered-routes-in-express/55589657#55589657 – NBSamar Apr 25 '21 at 22:07
73
app._router.stack.forEach(function(r){
  if (r.route && r.route.path){
    console.log(r.route.path)
  }
})
aug
  • 11,138
  • 9
  • 72
  • 93
eychu
  • 863
  • 7
  • 3
  • 2
    Note that if you are using something like Express Router (or other middleware) you should see @Caleb slightly longer answer that expands on this approach. – Iain Collins May 13 '16 at 20:25
56

DEBUG=express:* node index.js

If you run your app with the above command, it will launch your app with DEBUG module and gives routes, plus all the middleware functions that are in use.

You can refer: ExpressJS - Debugging and debug.

NBSamar
  • 904
  • 12
  • 20
53

Hacky copy/paste answer courtesy of Doug Wilson on the express github issues. Dirty but works like a charm.

function print (path, layer) {
  if (layer.route) {
    layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path))))
  } else if (layer.name === 'router' && layer.handle.stack) {
    layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp))))
  } else if (layer.method) {
    console.log('%s /%s',
      layer.method.toUpperCase(),
      path.concat(split(layer.regexp)).filter(Boolean).join('/'))
  }
}

function split (thing) {
  if (typeof thing === 'string') {
    return thing.split('/')
  } else if (thing.fast_slash) {
    return ''
  } else {
    var match = thing.toString()
      .replace('\\/?', '')
      .replace('(?=\\/|$)', '$')
      .match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
    return match
      ? match[1].replace(/\\(.)/g, '$1').split('/')
      : '<complex:' + thing.toString() + '>'
  }
}

app._router.stack.forEach(print.bind(null, []))

Produces

screengrab

AlienWebguy
  • 76,997
  • 17
  • 122
  • 145
51

This gets routes registered directly on the app (via app.VERB) and routes that are registered as router middleware (via app.use). Express 4.11.0

//////////////
app.get("/foo", function(req,res){
    res.send('foo');
});

//////////////
var router = express.Router();

router.get("/bar", function(req,res,next){
    res.send('bar');
});

app.use("/",router);


//////////////
var route, routes = [];

app._router.stack.forEach(function(middleware){
    if(middleware.route){ // routes registered directly on the app
        routes.push(middleware.route);
    } else if(middleware.name === 'router'){ // router middleware 
        middleware.handle.stack.forEach(function(handler){
            route = handler.route;
            route && routes.push(route);
        });
    }
});

// routes:
// {path: "/foo", methods: {get: true}}
// {path: "/bar", methods: {get: true}}
Caleb
  • 2,303
  • 1
  • 20
  • 21
  • 1
    Excellent, thanks for an example that shows how to get display routes set via middleware like the Express router. – Iain Collins May 13 '16 at 20:24
  • In my case this does not work with esbuild minified build added fix ` const endpoints: any[] = []; this.app._router.stack.forEach(function (middleware: any) { if (middleware.route) { // routes registered directly on the app endpoints.push(middleware.route); } else if (middleware.handle && middleware.handle.stack) { // router middleware middleware.handle.stack.forEach(function (handler: any) { const route = handler.route; route && endpoints.push(route); }); } });` – Maks Slesarenko Oct 20 '21 at 21:57
  • 1
    But what will happen when `app.use("/foo",router);` ? the path of handler.route its only sub match. – dovid Mar 25 '22 at 09:55
42

Here's a little thing I use just to get the registered paths in express 4.x

app._router.stack          // registered routes
  .filter(r => r.route)    // take out all the middleware
  .map(r => r.route.path)  // get all the paths
corvid
  • 10,733
  • 11
  • 61
  • 130
36

I have adapted an old post that is no longer online for my needs. I've used express.Router() and registered my routes like this:

var questionsRoute = require('./BE/routes/questions');
app.use('/api/questions', questionsRoute);

I renamed the document.js file in apiTable.js and adapted it like this:

module.exports =  function (baseUrl, routes) {
    var Table = require('cli-table');
    var table = new Table({ head: ["", "Path"] });
    console.log('\nAPI for ' + baseUrl);
    console.log('\n********************************************');

    for (var key in routes) {
        if (routes.hasOwnProperty(key)) {
            var val = routes[key];
            if(val.route) {
                val = val.route;
                var _o = {};
                _o[val.stack[0].method]  = [baseUrl + val.path];    
                table.push(_o);
            }       
        }
    }

    console.log(table.toString());
    return table;
};

then i call it in my server.js like this:

var server = app.listen(process.env.PORT || 5000, function () {
    require('./BE/utils/apiTable')('/api/questions', questionsRoute.stack);
});

The result looks like this:

Result example

It's just an example but might be of use.. i hope..

marco.marinangeli
  • 899
  • 14
  • 29
  • 2
    This does not work for nested routes as identified here: https://stackoverflow.com/questions/25260818/rest-with-express-js-nested-router –  Mar 07 '18 at 22:15
  • 2
    Beware of the link in this answer! It redirected me to a random website and forced a download to my computer. – Tyler Bell Feb 21 '19 at 03:01
29

https://www.npmjs.com/package/express-list-endpoints works pretty well.

Example

Usage:

const all_routes = require('express-list-endpoints');
console.log(all_routes(app));

Output:

[ { path: '*', methods: [ 'OPTIONS' ] },
  { path: '/', methods: [ 'GET' ] },
  { path: '/sessions', methods: [ 'POST' ] },
  { path: '/sessions', methods: [ 'DELETE' ] },
  { path: '/users', methods: [ 'GET' ] },
  { path: '/users', methods: [ 'POST' ] } ]
aercolino
  • 2,193
  • 1
  • 22
  • 20
24

json output

function availableRoutes() {
  return app._router.stack
    .filter(r => r.route)
    .map(r => {
      return {
        method: Object.keys(r.route.methods)[0].toUpperCase(),
        path: r.route.path
      };
    });
}

console.log(JSON.stringify(availableRoutes(), null, 2));

looks like this:

[
  {
    "method": "GET",
    "path": "/api/todos"
  },
  {
    "method": "POST",
    "path": "/api/todos"
  },
  {
    "method": "PUT",
    "path": "/api/todos/:id"
  },
  {
    "method": "DELETE",
    "path": "/api/todos/:id"
  }
]

string output

function availableRoutesString() {
  return app._router.stack
    .filter(r => r.route)
    .map(r => Object.keys(r.route.methods)[0].toUpperCase().padEnd(7) + r.route.path)
    .join("\n")
}

console.log(availableRoutesString());

looks like this:

GET    /api/todos  
POST   /api/todos  
PUT    /api/todos/:id  
DELETE /api/todos/:id

these are based on @corvid's answer

hope this helps

ezekills
  • 341
  • 2
  • 5
12

You can implement a /get-all-routes API:

const express = require("express");
const app = express();

app.get("/get-all-routes", (req, res) => {  
  let get = app._router.stack.filter(r => r.route && r.route.methods.get).map(r => r.route.path);
  let post = app._router.stack.filter(r => r.route && r.route.methods.post).map(r => r.route.path);
  res.send({ get: get, post: post });
});

const listener = app.listen(process.env.PORT, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

Here is a demo: https://glitch.com/edit/#!/get-all-routes-in-nodejs

ffmaer
  • 751
  • 9
  • 17
10

A function to log all routes in express 4 (can be easily tweaked for v3~)

function space(x) {
    var res = '';
    while(x--) res += ' ';
    return res;
}

function listRoutes(){
    for (var i = 0; i < arguments.length;  i++) {
        if(arguments[i].stack instanceof Array){
            console.log('');
            arguments[i].stack.forEach(function(a){
                var route = a.route;
                if(route){
                    route.stack.forEach(function(r){
                        var method = r.method.toUpperCase();
                        console.log(method,space(8 - method.length),route.path);
                    })
                }
            });
        }
    }
}

listRoutes(router, routerAuth, routerHTML);

Logs output:

GET       /isAlive
POST      /test/email
POST      /user/verify

PUT       /login
POST      /login
GET       /player
PUT       /player
GET       /player/:id
GET       /players
GET       /system
POST      /user
GET       /user
PUT       /user
DELETE    /user

GET       /
GET       /login

Made this into a NPM https://www.npmjs.com/package/express-list-routes

Labithiotis
  • 3,519
  • 7
  • 27
  • 47
  • 1
    This did not work as expected for me. app._router doesn't seem to include routes from app.use('/path', otherRouter); – Michael Cole Nov 07 '14 at 15:54
  • 1
    @MichaelCole Did you look at the answer below from Golo Roden? – Labithiotis Nov 11 '14 at 10:45
  • @Dazzler13 I played with this for an hour, and wasn't able to get it to work. Express 4.0. Made app, made router, app.use(path,router), router routes didn't appear in app._router. Example? – Michael Cole Nov 12 '14 at 14:07
  • The example from @Caleb below works well for routes handled with something like express.Router if that's your issue. Note that routes set with middleware (including express.Router) may not show up right away and you may need to add in a short delay before checking for them in app._router (even using the approach from @Caleb). – Iain Collins May 13 '16 at 21:26
  • Can you make it render as an HTML file? That'll be interesting – olawalejuwonm Apr 13 '23 at 16:11
  • @olawalejuwonm npm express-list-routes now returns array of routes you can use to render html – Labithiotis Apr 15 '23 at 12:33
  • @Labithiotishow do i get that? – olawalejuwonm Apr 15 '23 at 23:00
  • You could do this: ```js const routes = expressListRoutes(app); res.setHeader("Content-Type", "text/html") res.send(`${routes.map(route => `}`)}
    ${route.method}${route.path}
    `); ```
    – Labithiotis Apr 17 '23 at 07:33
  • Thank you @Labithiotis using the library doesn't return only empty array but express-list-endpoints works – olawalejuwonm Jul 12 '23 at 22:13
8

Within your app at /routes display your express route names

app.get('/routes', (req, res) => {
    res.send(app._router.stack
        .filter(r => r.route) 
        .map(r => r.route.path))
})

http://localhost:3000/routes

jasonleonhard
  • 12,047
  • 89
  • 66
7

I was inspired by Labithiotis's express-list-routes, but I wanted an overview of all my routes and brute urls in one go, and not specify a router, and figure out the prefix each time. Something I came up with was to simply replace the app.use function with my own function which stores the baseUrl and given router. From there I can print any table of all my routes.

NOTE this works for me because I declare my routes in a specific routes file (function) which gets passed in the app object, like this:

// index.js
[...]
var app = Express();
require(./config/routes)(app);

// ./config/routes.js
module.exports = function(app) {
    // Some static routes
    app.use('/users', [middleware], UsersRouter);
    app.use('/users/:user_id/items', [middleware], ItemsRouter);
    app.use('/otherResource', [middleware], OtherResourceRouter);
}

This allows me to pass in another 'app' object with a fake use function, and I can get ALL the routes. This works for me (removed some error checking for clarity, but still works for the example):

// In printRoutes.js (or a gulp task, or whatever)
var Express = require('express')
  , app     = Express()
  , _       = require('lodash')

// Global array to store all relevant args of calls to app.use
var APP_USED = []

// Replace the `use` function to store the routers and the urls they operate on
app.use = function() {
  var urlBase = arguments[0];

  // Find the router in the args list
  _.forEach(arguments, function(arg) {
    if (arg.name == 'router') {
      APP_USED.push({
        urlBase: urlBase,
        router: arg
      });
    }
  });
};

// Let the routes function run with the stubbed app object.
require('./config/routes')(app);

// GRAB all the routes from our saved routers:
_.each(APP_USED, function(used) {
  // On each route of the router
  _.each(used.router.stack, function(stackElement) {
    if (stackElement.route) {
      var path = stackElement.route.path;
      var method = stackElement.route.stack[0].method.toUpperCase();

      // Do whatever you want with the data. I like to make a nice table :)
      console.log(method + " -> " + used.urlBase + path);
    }
  });
});

This full example (with some basic CRUD routers) was just tested and printed out:

GET -> /users/users
GET -> /users/users/:user_id
POST -> /users/users
DELETE -> /users/users/:user_id
GET -> /users/:user_id/items/
GET -> /users/:user_id/items/:item_id
PUT -> /users/:user_id/items/:item_id
POST -> /users/:user_id/items/
DELETE -> /users/:user_id/items/:item_id
GET -> /otherResource/
GET -> /otherResource/:other_resource_id
POST -> /otherResource/
DELETE -> /otherResource/:other_resource_id

Using cli-table I got something like this:

┌────────┬───────────────────────┐
│        │ => Users              │
├────────┼───────────────────────┤
│ GET    │ /users/users          │
├────────┼───────────────────────┤
│ GET    │ /users/users/:user_id │
├────────┼───────────────────────┤
│ POST   │ /users/users          │
├────────┼───────────────────────┤
│ DELETE │ /users/users/:user_id │
└────────┴───────────────────────┘
┌────────┬────────────────────────────────┐
│        │ => Items                       │
├────────┼────────────────────────────────┤
│ GET    │ /users/:user_id/items/         │
├────────┼────────────────────────────────┤
│ GET    │ /users/:user_id/items/:item_id │
├────────┼────────────────────────────────┤
│ PUT    │ /users/:user_id/items/:item_id │
├────────┼────────────────────────────────┤
│ POST   │ /users/:user_id/items/         │
├────────┼────────────────────────────────┤
│ DELETE │ /users/:user_id/items/:item_id │
└────────┴────────────────────────────────┘
┌────────┬───────────────────────────────────┐
│        │ => OtherResources                 │
├────────┼───────────────────────────────────┤
│ GET    │ /otherResource/                   │
├────────┼───────────────────────────────────┤
│ GET    │ /otherResource/:other_resource_id │
├────────┼───────────────────────────────────┤
│ POST   │ /otherResource/                   │
├────────┼───────────────────────────────────┤
│ DELETE │ /otherResource/:other_resource_id │
└────────┴───────────────────────────────────┘

Which kicks ass.

5

Express 4

Given an Express 4 configuration with endpoints and nested routers

const express = require('express')
const app = express()
const router = express.Router()

app.get(...)
app.post(...)

router.use(...)
router.get(...)
router.post(...)

app.use(router)

Expanding the @caleb answer it is possible to obtain all routes recursively and sorted.

getRoutes(app._router && app._router.stack)
// =>
// [
//     [ 'GET', '/'], 
//     [ 'POST', '/auth'],
//     ...
// ]

/**
* Converts Express 4 app routes to an array representation suitable for easy parsing.
* @arg {Array} stack An Express 4 application middleware list.
* @returns {Array} An array representation of the routes in the form [ [ 'GET', '/path' ], ... ].
*/
function getRoutes(stack) {
        const routes = (stack || [])
                // We are interested only in endpoints and router middleware.
                .filter(it => it.route || it.name === 'router')
                // The magic recursive conversion.
                .reduce((result, it) => {
                        if (! it.route) {
                                // We are handling a router middleware.
                                const stack = it.handle.stack
                                const routes = getRoutes(stack)

                                return result.concat(routes)
                        }

                        // We are handling an endpoint.
                        const methods = it.route.methods
                        const path = it.route.path

                        const routes = Object
                                .keys(methods)
                                .map(m => [ m.toUpperCase(), path ])

                        return result.concat(routes)
                }, [])
                // We sort the data structure by route path.
                .sort((prev, next) => {
                        const [ prevMethod, prevPath ] = prev
                        const [ nextMethod, nextPath ] = next

                        if (prevPath < nextPath) {
                                return -1
                        }

                        if (prevPath > nextPath) {
                                return 1
                        }

                        return 0
                })

        return routes
}

For basic string output.

infoAboutRoutes(app)

Console output

/**
* Converts Express 4 app routes to a string representation suitable for console output.
* @arg {Object} app An Express 4 application
* @returns {string} A string representation of the routes.
*/
function infoAboutRoutes(app) {
        const entryPoint = app._router && app._router.stack
        const routes = getRoutes(entryPoint)

        const info = routes
                .reduce((result, it) => {
                        const [ method, path ] = it

                        return result + `${method.padEnd(6)} ${path}\n`
                }, '')

        return info
}

Update 1:

Due to the internal limitations of Express 4 it is not possible to retrieve mounted app and mounted routers. For example it is not possible to obtain routes from this configuration.

const subApp = express()
app.use('/sub/app', subApp)

const subRouter = express.Router()
app.use('/sub/route', subRouter)
Daniele Orlando
  • 2,692
  • 3
  • 23
  • 26
4

Need some adjusts but should work for Express v4. Including those routes added with .use().

function listRoutes(routes, stack, parent){

  parent = parent || '';
  if(stack){
    stack.forEach(function(r){
      if (r.route && r.route.path){
        var method = '';

        for(method in r.route.methods){
          if(r.route.methods[method]){
            routes.push({method: method.toUpperCase(), path: parent + r.route.path});
          }
        }       

      } else if (r.handle && r.handle.name == 'router') {
        const routerName = r.regexp.source.replace("^\\","").replace("\\/?(?=\\/|$)","");
        return listRoutes(routes, r.handle.stack, parent + routerName);
      }
    });
    return routes;
  } else {
    return listRoutes([], app._router.stack);
  }
}

//Usage on app.js
const routes = listRoutes(); //array: ["method: path", "..."]

edit: code improvements

lcssanches
  • 995
  • 12
  • 33
3

Slightly updated and more functional approach to @prranay's answer:

const routes = app._router.stack
    .filter((middleware) => middleware.route)
    .map((middleware) => `${Object.keys(middleware.route.methods).join(', ')} -> ${middleware.route.path}`)

console.log(JSON.stringify(routes, null, 4));
SeedyROM
  • 2,203
  • 1
  • 18
  • 22
3

Initialize express router

let router = require('express').Router();
router.get('/', function (req, res) {
    res.json({
        status: `API Its Working`,
        route: router.stack.filter(r => r.route)
           .map(r=> { return {"path":r.route.path, 
 "methods":r.route.methods}}),
        message: 'Welcome to my crafted with love!',
      });
   });   

Import user controller

var userController = require('./controller/userController');

User routes

router.route('/users')
   .get(userController.index)
   .post(userController.new);
router.route('/users/:user_id')
   .get(userController.view)
   .patch(userController.update)
   .put(userController.update)
   .delete(userController.delete);

Export API routes

module.exports = router;

Output

{"status":"API Its Working, APP Route","route": 
[{"path":"/","methods":{"get":true}}, 
{"path":"/users","methods":{"get":true,"post":true}}, 
{"path":"/users/:user_id","methods": ....}
3

in all server this is how i do it

app.get('/', (req, res) => {
    console.log('home')
})
app.get('/home', (req, res) => {
    console.log('/home')
})

function list(id) {
    const path = require('path');

    const defaultOptions = {
        prefix: '',
        spacer: 7,
    };

    const COLORS = {
        yellow: 33,
        green: 32,
        blue: 34,
        red: 31,
        grey: 90,
        magenta: 35,
        clear: 39,
    };

    const spacer = (x) => (x > 0 ? [...new Array(x)].map(() => ' ').join('') : '');

    const colorText = (color, string) => `\u001b[${color}m${string}\u001b[${COLORS.clear}m`;

    function colorMethod(method) {
        switch (method) {
            case 'POST':
                return colorText(COLORS.yellow, method);
            case 'GET':
                return colorText(COLORS.green, method);
            case 'PUT':
                return colorText(COLORS.blue, method);
            case 'DELETE':
                return colorText(COLORS.red, method);
            case 'PATCH':
                return colorText(COLORS.grey, method);
            default:
                return method;
        }
    }

    function getPathFromRegex(regexp) {
        return regexp.toString().replace('/^', '').replace('?(?=\\/|$)/i', '').replace(/\\\//g, '/');
    }

    function combineStacks(acc, stack) {
        if (stack.handle.stack) {
            const routerPath = getPathFromRegex(stack.regexp);
            return [...acc, ...stack.handle.stack.map((stack) => ({ routerPath, ...stack }))];
        }
        return [...acc, stack];
    }

    function getStacks(app) {
        // Express 3
        if (app.routes) {
            // convert to express 4
            return Object.keys(app.routes)
                .reduce((acc, method) => [...acc, ...app.routes[method]], [])
                .map((route) => ({ route: { stack: [route] } }));
        }

        // Express 4
        if (app._router && app._router.stack) {
            return app._router.stack.reduce(combineStacks, []);
        }

        // Express 4 Router
        if (app.stack) {
            return app.stack.reduce(combineStacks, []);
        }

        // Express 5
        if (app.router && app.router.stack) {
            return app.router.stack.reduce(combineStacks, []);
        }

        return [];
    }

    function expressListRoutes(app, opts) {
        const stacks = getStacks(app);
        const options = {...defaultOptions, ...opts };

        if (stacks) {
            for (const stack of stacks) {
                if (stack.route) {
                    const routeLogged = {};
                    for (const route of stack.route.stack) {
                        const method = route.method ? route.method.toUpperCase() : null;
                        if (!routeLogged[method] && method) {
                            const stackMethod = colorMethod(method);
                            const stackSpace = spacer(options.spacer - method.length);
                            const stackPath = path.resolve(
                                [options.prefix, stack.routerPath, stack.route.path, route.path].filter((s) => !!s).join(''),
                            );
                            console.info(stackMethod, stackSpace, stackPath);
                            routeLogged[method] = true;
                        }
                    }
                }
            }
        }

    };

    expressListRoutes(app)
}

list(1);

if you run it then this will happen

GET C:
GET C:\home

2

This worked for me

let routes = []
app._router.stack.forEach(function (middleware) {
    if(middleware.route) {
        routes.push(Object.keys(middleware.route.methods) + " -> " + middleware.route.path);
    }
});

console.log(JSON.stringify(routes, null, 4));

O/P:

[
    "get -> /posts/:id",
    "post -> /posts",
    "patch -> /posts"
]
SeedyROM
  • 2,203
  • 1
  • 18
  • 22
prranay
  • 1,789
  • 5
  • 23
  • 33
1

On Express 3.5.x, I add this before starting the app to print the routes on my terminal :

var routes = app.routes;
for (var verb in routes){
    if (routes.hasOwnProperty(verb)) {
      routes[verb].forEach(function(route){
        console.log(verb + " : "+route['path']);
      });
    }
}

Maybe it can help...

David Manson
  • 206
  • 2
  • 3
1

For express 4.x

Here's a gross 1 liner that works with routes added to app and routes added to express.Router().

It returns this structure.

[
    {
        "path": "/api",
        "methods": {
            "get": true
        }
    },
    {
        "path": "/api/servermembers/",
        "methods": {
            "get": true
        }
    },
    {
        "path": "/api/servermembers/find/:query",
        "methods": {
            "get": true
        }
    }
]

Modules that use express.Router() would need to export like this:

module.exports = {
  router,
  path: "/servermembers",
};

And be added like this:

app.use(`/api${ServerMemberRoutes.path}`, ServerMemberRoutes.router);
app.get(
  "/api",
  /**
   * Gets the API's available routes.
   * @param {request} _req
   * @param {response} res
   */
  (_req, res) => {
    res.json(
      [app._router, ServerMemberRoutes]
        .map((routeInfo) => ({
          entityPath: routeInfo.path || "",
          stack: (routeInfo?.router?.stack || routeInfo.stack).filter(
            (stack) => stack.route
          ),
        }))
        .map(({ entityPath, stack }) =>
          stack.map(({ route: { path, methods } }) => ({
            path: entityPath ? `/api${entityPath}${path}` : path,
            methods,
          }))
        ).flat()
    );
  }
);

Of course, the /api base url prefix could be stored in a variable as well if desired.

Neil M
  • 171
  • 8
1

All of these seem overly complex. On "express": "^4.18.1"

const routes: { methods: string[], path: string }[] = [];

const parseRoute = (def) => {
  if (def.route) {
    routes.push({ path: def.route.path, methods: Object.keys(def.route.methods) });
  } else if (def.name === 'router') {
    // nested route (sub router)..
    def.handle.stack.forEach(parseRoute);
  }
}

// loop over and parse routes
app._router.stack.forEach(parseRoute);

console.log(routes);
//{ path: '/', methods: [ 'get' ] },
//{ path: '/healthcheck', methods: [ 'get' ] },
//{ path: '/assets/theme.css', methods: [ 'get' ] },
//...
nawlbergs
  • 2,820
  • 1
  • 18
  • 11
0

So I was looking at all the answers.. didn't like most.. took some from a few.. made this:

const resolveRoutes = (stack) => {
  return stack.map(function (layer) {
    if (layer.route && layer.route.path.isString()) {
      let methods = Object.keys(layer.route.methods);
      if (methods.length > 20)
        methods = ["ALL"];

      return {methods: methods, path: layer.route.path};
    }

    if (layer.name === 'router')  // router middleware
      return resolveRoutes(layer.handle.stack);

  }).filter(route => route);
};

const routes = resolveRoutes(express._router.stack);
const printRoute = (route) => {
  if (Array.isArray(route))
    return route.forEach(route => printRoute(route));

  console.log(JSON.stringify(route.methods) + " " + route.path);
};

printRoute(routes);

not the prettiest.. but nested, and does the trick

also note the 20 there... I just assume there will not be a normal route with 20 methods.. so I deduce it is all..

TacB0sS
  • 10,106
  • 12
  • 75
  • 118
0

route details are listing route for "express": "4.x.x",

import {
  Router
} from 'express';
var router = Router();

router.get("/routes", (req, res, next) => {
  var routes = [];
  var i = 0;
  router.stack.forEach(function (r) {
    if (r.route && r.route.path) {
      r.route.stack.forEach(function (type) {
        var method = type.method.toUpperCase();
        routes[i++] = {
          no:i,
          method: method.toUpperCase(),
          path: r.route.path
        };
      })
    }
  })

  res.send('<h1>List of routes.</h1>' + JSON.stringify(routes));
});

SIMPLE OUTPUT OF CODE

List of routes.

[
{"no":1,"method":"POST","path":"/admin"},
{"no":2,"method":"GET","path":"/"},
{"no":3,"method":"GET","path":"/routes"},
{"no":4,"method":"POST","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item"},
{"no":5,"method":"GET","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item"},
{"no":6,"method":"PUT","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item/:itemId"},
{"no":7,"method":"DELETE","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item/:itemId"}
]
lasitha edirisooriya
  • 793
  • 2
  • 13
  • 15
0

Just use this npm package, it will give the web-output as well as terminal output in nice formatted table view.

enter image description here

https://www.npmjs.com/package/express-routes-catalogue

Naveen DA
  • 4,148
  • 4
  • 38
  • 57
  • 2
    This other package have ton more acceptance. https://www.npmjs.com/package/express-list-endpoints . I'ts 21.111 against 34 weekly downloads. However, `express-routes-catalogue` also displays the routes as HTML, ehile the other one doesn't. – mayid Nov 09 '19 at 14:23
  • 1
    not bad, the documentation of package differs from the actual package name when requiring and this package like all the other mentioned only show single layer routes where it is included – Hamza Khan Apr 08 '20 at 07:55
  • @hamzakhan p.s thanks for the update.I'm the author, soon will be updated in the documentation. – Vijay Apr 11 '20 at 16:48
0

Static code analysis approach.

This tool analyzes source code and shows routing info without starting a server.

npx express-router-dependency-graph --rootDir=path/to/project
# json or markdown output

https://github.com/azu/express-router-dependency-graph

Example output:

File Method Routing Middlewares FilePath
user/index.ts
get /getUserById requireView user/index.ts#L1-3
get /getUserList requireView user/index.ts#L4-6
post /updateUserById requireEdit user/index.ts#L8-10
post /deleteUserById requireEdit user/index.ts#L12-20
game/index.ts
get /getGameList requireView game/index.ts#L1-3
get /getGameById requireView game/index.ts#L4-6
post /updateGameById requireEdit game/index.ts#L8-10
post /deleteGameById requireEdit game/index.ts#L12-20
azu
  • 11
  • 1
  • 1
  • Great solution! Alas did not produce any useful results for https://github.com/TryGhost/Ghost But it did find a few routes in the `node_modules`! – Heath Raftery Sep 28 '21 at 05:14
0

I recently was struggling with finding a tool that could do this well. Every solution/existing npm package I found had corner cases where it failed, and my project had those corner cases. I also wanted an output I could use for my project as well.

So I built my own tool: https://www.npmjs.com/package/express-route-parser

It also allows you to attach arbitrary meta-data to a route via a middleware. This can be used in a variety of ways, such as attaching schema's to your routes.

Check it out and see if it helps

nklisch
  • 76
  • 3
0

I did a simple method to get the routes:

First, create a interface to mount the route object:

interface Route {
    path: string;
    method: string;
    prefix?: string;
}

So, iterate over express routes

public getRoutes() {
        const routes: Route[] = []
        this.app._router.stack.map((r: any) => {
            if (r.name === 'router') {
                const prefix =  r.regexp.toString().replace(/\/\^|\/\?|\/\$/g, '').replace('(?=\\/|$)', '').replace(/\\(.)/g, '$1').replace(/\/i\n/g, '').replace(/\/i$/, '');
                r.handle.stack?.map((r: any) => {
                    const path = r.route?.path;
                    r.route?.stack?.map((r: any) => {
                        routes.push({
                            path: path,
                            method: r.method,
                            prefix: prefix
                        })
                    })
                })
            }
        })

        return routes;
    }

Output:

[
  { path: '/', method: 'get', prefix: '' },
  { path: '/2', method: 'get', prefix: '' },
  { path: '/users', method: 'get', prefix: '/api' },
  { path: '/user', method: 'post', prefix: '/api' },
  { path: '/user/:id', method: 'delete', prefix: '/api' }
]

-1

In express 4.*

//Obtiene las rutas declaradas de la API
    let listPathRoutes: any[] = [];
    let rutasRouter = _.filter(application._router.stack, rutaTmp => rutaTmp.name === 'router');
    rutasRouter.forEach((pathRoute: any) => {
        let pathPrincipal = pathRoute.regexp.toString();
        pathPrincipal = pathPrincipal.replace('/^\\','');
        pathPrincipal = pathPrincipal.replace('?(?=\\/|$)/i','');
        pathPrincipal = pathPrincipal.replace(/\\\//g,'/');
        let routesTemp = _.filter(pathRoute.handle.stack, rutasTmp => rutasTmp.route !== undefined);
        routesTemp.forEach((route: any) => {
            let pathRuta = `${pathPrincipal.replace(/\/\//g,'')}${route.route.path}`;
            let ruta = {
                path: pathRuta.replace('//','/'),
                methods: route.route.methods
            }
            listPathRoutes.push(ruta);
        });
    });console.log(listPathRoutes)
-1

This one worked for me

// Express 4.x
function getRoutes(stacks: any, routes: { path: string; method: string }[] = [], prefix: string = ''): { path: string; method: string }[] {
  for (const stack of stacks) {
    if (stack.route) {
      routes.push({ path: `${prefix}${stack.route.path}`, method: stack.route.stack[0].method });
    }
    if (stack && stack.handle && stack.handle.stack) {
      let stackPrefix = stack.regexp.source.match(/\/[A-Za-z0-9_-]+/g);

      if (stackPrefix) {
        stackPrefix = prefix + stackPrefix.join('');
      }

      routes.concat(getRoutes(stack.handle.stack, routes, stackPrefix));
    }
  }
  return routes;
}
dasdachs
  • 709
  • 10
  • 18
-1

const routes = {}
function routerRecursion(middleware, pointer, currentName) {
  if (middleware.route) { // routes registered directly on the app
    if (!Array.isArray(pointer['routes'])) {
      pointer['routes'] = []
    }
    const routeObj = {
      path: middleware.route.path,
      method: middleware.route.stack[0].method
    }
    pointer['routes'].push(routeObj)
  } else if (middleware.name === 'router') { // inside router
    const current = middleware.regexp.toString().replace(/\/\^\\\//, '').replace(/\\\/\?\(\?\=\\\/\|\$\)\/\i/, '')
    pointer[current] = {}
    middleware.handle.stack.forEach(function (handler) {
      routerRecursion(handler, pointer[current], current)
    });
  }
}
app._router.stack.forEach(function (middleware) {
  routerRecursion(middleware, routes, 'main')
});
console.log(routes);

app._router.stack.forEach(function (middleware) { routerRecursion(middleware, routes, 'main') }); console.log(routes);

-2

I published a package that prints all middleware as well as routes, really useful when trying to audit an express application. You mount the package as middleware, so it even prints out itself:

https://github.com/ErisDS/middleware-stack-printer

It prints a kind of tree like:

- middleware 1
- middleware 2
- Route /thing/
- - middleware 3
- - controller (HTTP VERB)  
ErisDS
  • 568
  • 4
  • 13
-3

Here is a one-line function to pretty-print the routes in an Express app:

const getAppRoutes = (app) => app._router.stack.reduce(
  (acc, val) => acc.concat(
    val.route ? [val.route.path] :
      val.name === "router" ? val.handle.stack.filter(
        x => x.route).map(
          x => val.regexp.toString().match(/\/[a-z]+/)[0] + (
            x.route.path === '/' ? '' : x.route.path)) : []) , []).sort();
slouchpie
  • 1
  • 2