Developer tooling for Danet, the NestJS-inspired backend framework for Deno.
Inspired by NestJS Devtools, it introspects your application and serves interactive tooling you can explore in the browser, entirely locally (no cloud account, no data leaves your machine):
- a dependency graph visualizer — modules, controllers and providers, wired
by their
import/declares/injectsrelationships; - a routes explorer — every entrypoint your controllers expose (HTTP routes and WebSocket handlers), grouped by controller and filterable by path or verb. Click an entrypoint to see its execution flow — the middlewares, guards and filters bound to it.
![modules → controllers → providers, wired by import / declares / injects edges]
// deno.json
{
"imports": {
"@danet/core": "jsr:@danet/core@^2.11.0",
"@danet/devtools": "jsr:@danet/devtools@^0.1.0"
}
}Mount the devtools on your app before listening:
import { DanetApplication } from '@danet/core';
import { setupDevtools } from '@danet/devtools';
import { AppModule } from './app.module.ts';
const app = new DanetApplication();
await app.init(AppModule);
setupDevtools(app); // mounts GET /_devtools
await app.listen(3000);
// dependency graph: http://localhost:3000/_devtools
// routes explorer: http://localhost:3000/_devtools/routesThen open http://localhost:3000/_devtools and use the nav to switch between
the Dependency Graph and Routes views.
setupDevtools(app, { path: '/_devtools' });| Option | Default | Description |
|---|---|---|
path |
'/_devtools' |
Base path for the UIs and their JSON routes. |
Four routes are registered:
GET {path}— the interactive dependency graph UI.GET {path}/graph.json— the raw graph as JSON.GET {path}/routes— the routes explorer UI.GET {path}/routes.json— the raw route map as JSON.
Tip: only call
setupDevtoolsin development, e.g.if (Deno.env.get('NODE_ENV') !== 'production') setupDevtools(app);
You can build the graph yourself (e.g. to snapshot it, diff it in CI, or feed it to another tool). It's a pure metadata introspection — no app boot required:
import { buildDependencyGraph } from '@danet/devtools/graph';
import { AppModule } from './app.module.ts';
const graph = buildDependencyGraph(AppModule);
// { nodes: GraphNode[], edges: GraphEdge[] }- Nodes —
module,controller,provider. Providers carry their DIscope(GLOBAL/REQUEST/TRANSIENT) and flags for token-/value-based providers. - Edges —
import(module → module),declares(module → controller / provider) andinjects(constructor dependency).
The walk mirrors the framework's own bootstrap: it starts at the entry module,
recurses through imports, and reads constructor design:paramtypes plus
@Inject(token) metadata to discover injection edges.
The route map is likewise pure metadata introspection — handy for snapshotting your API surface or diffing it in CI:
import { buildRouteMap } from '@danet/devtools/routes';
import { AppModule } from './app.module.ts';
const map = buildRouteMap(AppModule, { prefix: '/api' });
// { prefix: '/api', controllers: RouteController[] }
// each route: { method, path, handler, kind, bindings, sse?, statusCode? }It reconstructs the exact path Danet registers for every handler — controller
base path + method path + optional global prefix — and reports the HTTP verb
(@All handlers surface as ALL), @SSE streams and @HttpCode status codes.
Pass prefix to match an app-wide base path set with app.registerBasePath
(setupDevtools wires this automatically).
It also captures, per entrypoint:
kind—'http'or'ws'. WebSocket controllers (@WebSocketController) and their@OnWebSocketMessagetopics are listed alongside HTTP routes.bindings— the execution pipeline bound to the route via decorators:@Middlewaremiddlewares,@UseGuardguards and@UseFilterfilters, each tagged with itsstageand whether it's bound atcontrollerormethodscope. (Globally-registered ones live in the injector, not in metadata, so they aren't included.)
Dependency graph
- Color-coded nodes (modules, controllers, providers) with distinct shapes.
- Request-/transient-scoped providers are outlined so non-singletons stand out.
- Click a node to inspect it and highlight its immediate neighborhood.
- Filter nodes, switch layouts (force / hierarchy / concentric / grid), fit.
Routes explorer
- Every entrypoint grouped by controller, showing its declaring module and base path; HTTP routes and WebSocket handlers side by side.
- Verb-colored badges; path parameters (
:id) highlighted. - Live filtering by path/handler text and by HTTP method (or
WS). SSEand custom status-code tags surfaced inline.- Click an entrypoint to see its execution-flow graph:
Middlewares → Guards → Handler → Filters, with controller-/method-scope bindings labelled — handy for understanding the request lifecycle or troubleshooting why a guard/filter isn't running.
deno task test # run the test suite
deno task start:example # boot the example app + devtools on :3000
deno fmt && deno lint # format & lintMIT