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
DocsTyped Client

Typed Client

Generate the RouteMap

routegraph generate-client

Walks your loaded routes and writes AppRouteMap — a plain TypeScript type, not a runtime artifact. No codegen step runs at request time; no .d.ts to hand-maintain when a route changes.

routemap.ts (generated — don't hand-edit)
export interface AppRouteMap {
  '/users': {
    GET: { query: { role?: string }; response: User[] }
    POST: { body: { name: string; email: string }; response: User }
  }
  '/users/:id': {
    GET: { params: { id: string }; response: User }
    DELETE: { params: { id: string }; response: { ok: true } }
  }
}

createClient

import { createClient } from '@routegraph/client'
import type { AppRouteMap } from './routemap.js'
 
const api = createClient<AppRouteMap>({
  baseUrl: 'http://localhost:3000/api',
  headers: { Authorization: `Bearer ${token}` },
  timeout: 5000,
})
OptionTypeDescription
baseUrlstringrequired
headersRecord<string,string> | () => Record<string,string>sent on every request
fetchtypeof fetchinject your own — great for testing
onError(err: ClientError) => voidcalled before the error is thrown
timeoutnumber (ms)aborts the request automatically

Making requests

const users = await api['/users'].GET({ query: { role: 'admin' } })
console.log(users.data)   // typed from the route's response schema
 
await api['/users'].POST({ body: { name: 'Ada' } })
//                                 ^^^^^^^^^^^^^^^ TS error
//                                 the route's body schema also requires `email`

Wrong params, missing body fields, and mistyped query keys are all compile errors — before you ever hit send.

ClientResponse / ClientError

interface ClientResponse<T> {
  data: T
  status: number
  headers: Headers
}
 
class ClientError extends Error {
  status: number
  issues?: ValidationIssue[]   // present on 400s from RouteGraph's own validation
}
⚠️

The client’s type safety is compile-time only. There is no runtime response validation on the client side — data is typed as T, not re-parsed through Zod on the way in. If a server response doesn’t actually match its schema, the client will not catch that at runtime.

Cancellation

const controller = new AbortController()
api['/users'].GET({ signal: controller.signal })
controller.abort()

Testing with an injected fetch

const api = createClient<AppRouteMap>({
  baseUrl: 'http://localhost:3000/api',
  fetch: mockFetch,   // any function matching the global fetch signature
})