[ The sdxc command ]
List routes
Print every route your contracts declare with its controller and the middleware that runs for it, narrowed like rails routes, as a table or as JSON for an agent.
Last updated 2026-10-19
sdxc routes
sdxc routes prints every route your contracts in routes/ declare: its method, full pattern
and name, the controller the router maps it to, and the middleware that runs for it. It answers
"what serves this URL, and what runs before it" without opening the router.
Read the table
Router app/initializers/router.tsx
asyncContext(), log(logger), trace(), serverTiming(), noWWW(), trailingSlash(), wellKnown(…),
getClientIP(), userAgent(), rateLimit(…), database(openDatabase()), models(…),
mail({ transport, from: MAIL_FROM })
routes/web.ts
METHOD PATTERN NAME CONTROLLER MIDDLEWARE
GET / home app/http/controllers/home.tsx page
GET /robots.txt robots app/http/controllers/robots.ts
GET /sitemap.xml sitemap app/http/controllers/sitemap.ts
ANY /*path notFound app/http/controllers/not-found.tsx page
routes/api.ts
METHOD PATTERN NAME CONTROLLER MIDDLEWARE
GET /api/health api.health app/http/controllers/api/health.tsx api
GET /api/openapi.json api.openapi app/http/controllers/api/openapi.tsx api
ANY /api/*path api.notFound app/http/controllers/api/not-found.tsx api
POST /mcp mcp app/http/controllers/mcp.tsx api
Each router's global middleware is listed once at the top, since every route it maps runs it first, in that order. Then each contract gets a table:
METHODis the method the route answers;ANYanswers every one.NAMEis the route's key path in its contract, the oneroutes.posts.show.href()reads.CONTROLLERis the file the router maps the route to. A route no router maps shows(not mapped).MIDDLEWAREholds the guards inlazy()'s second argument, then the controller's own chain, such aspage.
routes reads your source and runs none of it, so it needs no bindings, no install and no
build. Anything it cannot follow, such as a route mapped through a helper, is reported on stderr
and the rest of the listing still prints.
Narrow the listing
The options follow rails routes, and compose with each other and with --json:
-g, --grep <pattern>keeps routes whose name, pattern, method or controller contains the text, ignoring case. A regular expression works too:sdxc routes -g '^posts\.', and text that is no valid expression, such as/posts/(:id), searches as plain text.-c, --controller <path>keeps the routes of one controller file or folder, with or withoutapp/http/controllers/and the extension;-c postslists every controller underapp/http/controllers/posts/, and-c posts/showthat one file.--method <method>keeps routes answering that method, in either case; a route declared without a method answers all of them, so it stays.--contract <name>keeps routes declared in one contract inroutes/, written asapi,routes/apiorroutes/api.ts.--unusedkeeps routes no router maps, and prints on stderr each controller file nothing loads.-E, --expandedprints one block per route with every field, for a narrow terminal.
sdxc routes -g posts
sdxc routes -c admin/users --method post
sdxc routes --contract api -E
A narrowed table lists only the routers that map one of its routes. A filter that matches
nothing prints No routes match., and --unused with nothing to report prints
No unused routes found.; both exit 0.
The expanded view
--[ Route 1 ]-----------------------------------------------------------------
Method | GET
Pattern | /robots.txt
Name | robots
Contract | routes/web.ts
Router | app/initializers/router.tsx
Controller | app/http/controllers/robots.ts
Router middleware | asyncContext(), log(logger), trace(), …
Guards |
Controller middleware |
Find what is left to wire
--unused answers two questions after a refactor or a hand-written route: which routes the
contract declares and no router maps, and which controller files nothing loads.
sdxc routes --unused
routes/web.ts
METHOD PATTERN NAME CONTROLLER MIDDLEWARE
GET /pricing pricing (not mapped)
app/http/controllers/old-pricing.tsx: no route maps this controller
Map the route or delete it, and map or delete the file.
sdxc make controller declares and maps a route in one step, so
it leaves nothing for --unused to find.
As JSON
sdxc routes --json
--json prints the same routes as one array, each with method, pattern, contract, name,
router, controller ({ file, export }) and middleware (router, guards and controller,
each a list of { name, from }), so an agent learns what serves a URL and where each middleware
comes from:
{
"method": "GET",
"pattern": "/robots.txt",
"contract": "routes/web.ts",
"name": "robots",
"router": "app/initializers/router.tsx",
"controller": { "file": "app/http/controllers/robots.ts", "export": "default" },
"middleware": {
"router": [{ "name": "log(logger)", "from": "@sdxc/logger/middleware" }, …],
"guards": [],
"controller": []
}
}
Diagnostics and --unused file reports go to stderr, so stdout is always one JSON array.
sdxc routes --unused --json gives an agent the mappings still to write.
Where to go next
Make a controller — add a controller and its route.
Check a project —
sdxc doctorwarns about unmapped routes in CI.Wire the router — the router and middleware chains
routesreads.