RouteGraph v1.0.0 is out. It's stable, it's typed, it's not going to rewrite your framework of choice into a decorator soup. → Try it in 90 seconds

Integrations

Five adapters. One contract: take a loaded RouteGraph, hand back something your framework already knows how to mount. Same routes/ folder underneath every single one — examples/with-express/ proves it, booting the exact same routes, middleware, and shared modules from five different entry points. Pick a framework the way you’d pick a coffee order, not the way you’d pick a religion.

Runtime coverage

AdapterFrameworkRuns onDocs UI auto-mount via routegraph dev
@routegraph/expressExpress v4/v5Node.js
@routegraph/fastifyFastify v4/v5Node.js
@routegraph/koaKoa v2Node.js
@routegraph/honoHono v4Node.js Bun Deno Workersmanual wiring
@routegraph/elysiaElysiaBun Node (@elysiajs/node)manual wiring
⚠️

Honest gap, not a footnote: routegraph dev mounts the docs UI automatically for Express, Fastify, and Koa today. Hono and Elysia need one extra line — see the examples below. It’s on the roadmap to close, not quietly ignored.

The adapter contract

Every adapter does the same three things:

  1. Takes your loaded graph and returns a router/plugin native to that framework.
  2. Normalizes that framework’s request/response into NormalizedRequest / NormalizedResponse before your handler sees them — so your handler code is identical across all five.
  3. Exposes .raw on both, an escape hatch to the underlying framework object when you need something adapter-specific.

Pick one

index.ts
import express from 'express'
import { RouteGraph } from '@routegraph/core'
import { createExpressRouter } from '@routegraph/express'
import { createDocsMiddleware } from '@routegraph/docs'
 
const graph = new RouteGraph({ routesDir: './routes' })
await graph.load()
 
const app = express()
app.use(express.json())
app.use('/api', createExpressRouter(graph))
app.use('/_routegraph', createDocsMiddleware(graph))
app.listen(3000)

The reference adapter. If a bug report only reproduces on one framework, it’s usually not this one.

Switching frameworks, side by side

- import express from 'express'
+ import { Hono } from 'hono'
+ import { serve } from '@hono/node-server'
  import { RouteGraph } from '@routegraph/core'
- import { createExpressRouter } from '@routegraph/express'
+ import { createHonoRouter } from '@routegraph/hono'
 
  const graph = new RouteGraph({ routesDir: './routes' })
  await graph.load()
 
- const app = express()
- app.use(express.json())
- app.use('/api', createExpressRouter(graph))
- app.listen(3000)
+ const app = new Hono()
+ app.route('/api', createHonoRouter(graph))
+ serve({ fetch: app.fetch, port: 3000 })

Your routes/ folder didn’t change. Not one file. Middleware, Zod schemas, handler bodies — untouched. Only the server entry point moved.