Your folder structure
is your API.
Drop a file, get a route. RouteGraph turns a routes/ directory into a fully validated, self-documenting REST API — on Express, Hono, Fastify, Elysia, or Koa, unchanged. No router file. No boilerplate you wrote at 2am and never touched again.
You’ve written this router by hand before
router.get('/users', listUsers)
router.get('/users/:id', getUser)
router.post('/users', createUser)
router.delete('/users/:id', deleteUser)
router.get('/users/:id/posts', listUserPosts)
// …repeat for every resource, keep it in sync with the handlers,
// keep the docs in sync with the validation, keep the validation
// in sync with whatever the frontend actually sends this week.Every new endpoint means touching a file that has nothing to do with the endpoint. RouteGraph deletes that file.
How it resolves
Drop a file
routes/users/[id]/GET.tsIt’s a route
export const config = {
request: { params: z.object({ id: z.string() }) },
response: { 200: UserSchema }
} satisfies RouteConfig
export default async (req, res) => {
res.status(200).json(findUser(req.params.id))
}It runs on whatever you’re using today
Same routes. Same middleware. Same types. One line changes — your server’s entry point.
Everything a router should have shipped with
Folder path in, route table out. [id] becomes :id. Static beats dynamic. No Router() object anywhere in your code.
Add a request schema, get runtime validation and a uniform 400 response — before your handler ever runs.
createDocsMiddleware(graph) renders every route’s schema into a searchable, try-it-out UI. One line, zero YAML.
createClient<AppRouteMap>() gives you compile-time-checked fetch calls inferred straight from your schemas.
Add, edit, or delete a route file. RouteWatcher reloads it into the running graph — no server restart.
init, dev, build, validate, generate-client, export-docs. Falls back to plain output in CI automatically.
The same routes/ directory runs on five different servers. Migrate frameworks without migrating routes.
Same Zod schemas, one more output. Import straight into Postman, Insomnia, or Swagger UI.
Not a framework. A routing layer.
RouteGraph doesn’t want to replace Express, Hono, Fastify, Elysia, or Koa —
it wants to stop you from writing the part of them that was never
interesting. No decorators. No dependency-injection container. No modules
that exist to hold other modules. Just files, functions, and a RouteGraph
that walks your routes/ directory once at startup and hands the result to
whichever adapter you picked.