How can I get all the routes (from all the modules and controllers available on each module) in Nestjs?

Viewed 15351

Using Nestjs I'd like to get a list of all the available routes (controller methods) with http verbs, like this:

API:
      POST   /api/v1/user
      GET    /api/v1/user
      PUT    /api/v1/user

It seems that access to express router is required, but I haven found a way to do this in Nestjs. For express there are some libraries like "express-list-routes" or "express-list-endpoints".

Thanks in advance!

6 Answers

main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
  const server = app.getHttpServer();
  const router = server._events.request._router;

  const availableRoutes: [] = router.stack
    .map(layer => {
      if (layer.route) {
        return {
          route: {
            path: layer.route?.path,
            method: layer.route?.stack[0].method,
          },
        };
      }
    })
    .filter(item => item !== undefined);
  console.log(availableRoutes);
}
bootstrap();

I just found that Nestjs app has a "getHttpServer()" method, with this I was able to access the "router stack", here's the solution:

// main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as expressListRoutes from 'express-list-routes';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(3000);


  const server = app.getHttpServer();
  const router = server._events.request._router;
  console.log(expressListRoutes({}, 'API:', router));

}
bootstrap();

Nestjs available routes!

import { Controller, Get, Request } from "@nestjs/common";
import { Request as ExpressRequest, Router } from "express";

...

@Get()
root(@Request() req: ExpressRequest) {
    const router = req.app._router as Router;
    return {
        routes: router.stack
            .map(layer => {
                if(layer.route) {
                    const path = layer.route?.path;
                    const method = layer.route?.stack[0].method;
                    return `${method.toUpperCase()} ${path}`
                }
            })
            .filter(item => item !== undefined)
    }
}

...
{
    "routes": [
        "GET /",
        "GET /users",
        "POST /users",
        "GET /users/:id",
        "PUT /users/:id",
        "DELETE /users/:id",
    ]
}

In Nest every native server is wrapped in an adapter. For those, who use Fastify:

// main.ts

app
    .getHttpAdapter()
    .getInstance()
    .addHook('onRoute', opts => {
      console.log(opts.url)
    })

More about fastify hooks here.

It works perfectly:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as expressListRoutes from 'express-list-routes';

async function getApp() {
  const app = await NestFactory.create(AppModule);

  await app.listen(3000);

  expressListRoutes(app.getHttpServer()._events.request._router);

  return app;
}

getApp();

Output:

[Nest] 11619  - 10/03/2022 19:53:56     LOG [NestApplication] Nest application successfully started +3ms
GET      /ui
GET      /ui-json
GET      /
GET      /api/v1/auth

For some reason express-list-routes did not work with NestJS for me. But there is an alternative solution: you can use @nestjs/swagger module to form an OpenAPI document. It has a paths field which basically has all the routes listed. It uses NestJS decorator metadata instead of underlying server module.

Example

import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
import type { INestApplication } from '@nestjs/common';

export function listNestJSApplicationRoutes(app: INestApplication): Array<{ method: string; path: string }> {
  const doc = SwaggerModule.createDocument(app, new DocumentBuilder().build());
  return Object.entries(doc.paths).flatMap(([path, pathContent]) =>
    Object.keys(pathContent).map((method) => ({ method, path })),
  );
}

This solution is independent from the underlying server module - express or fastify. It also automatically accounts for RouterModule, API versioning and global prefix. It makes sense to expect that @nestjs/swagger will support any changes to NestJS internal APIs and also any new ways to modify API routes because it is maintained by Kamil Mysliwiec himself.

If you're wondering about internal APIs of NestJS used by @nestjs/swagger, you can start reading SwaggerModule.createDocument.

Related