Skip to Content
Schema-first · Type-safe · Transport-agnostic

routar

Define your API schema once — get end-to-end type safety and runtime validation across any HTTP client and any environment.

npm install @routar/core
import { z } from 'zod'
import { endpoint, defineRouter, createApi, createFetchExecutor } from '@routar/core'

const TodoSchema = z.object({ id: z.number(), title: z.string(), done: z.boolean() })

const api = createApi(createFetchExecutor('https://api.example.com'), defineRouter('/todos', {
  list:   endpoint({ method: 'GET',  path: '/',    response: z.array(TodoSchema) }),
  detail: endpoint({ method: 'GET',  path: '/:id', response: TodoSchema,
                     request: { path: z.object({ id: z.number() }) } }),
  create: endpoint({ method: 'POST', path: '/',    response: TodoSchema,
                     request: { body: z.object({ title: z.string() }) } }),
}))

const todos = await api.list()                              // Todo[]
const todo  = await api.detail({ path: { id: 1 } })          // Todo
const next  = await api.create({ body: { title: 'buy milk' } }) // Todo

The contrast

AI generates fast. But the growing context is still yours to manage.

AI can write all 6 files. But when the API changes, it has to find and update every one — or ship drift.

Without routar6 files · ~120 lines
Manually typed — server drift is invisible until runtime
With routar1 file · ~30 lines
services/todo.ts
types · client · query keys · mutations · MSW handlers — all derived. No sync needed.
6× fewer files · ~4× fewer lines · one place to change

Composable by design

Mix and match anything

Swap executors, stack plugins, combine routers, bind TanStack Query. Every control is one architecture layer — the types assemble themselves.

L1Routers
L2Create with
L3Plugins
L4Executor
L5Query bindings

@routar/react-query

Options, not hooks

Most wrappers generate a useTodos() hook — callable only inside a component, one way. routar derives queryOptions and mutationOptions straight from your router: plain objects you reuse anywhere.

import { useQuery, useSuspenseQuery, useMutation } from '@tanstack/react-query'
import { createQueries } from '@routar/react-query'

// Keys + queryFn + mutationFn — all derived from `api`. One source of truth.
export const todoQuery = createQueries(todoApi)

// It returns options objects, not hooks — reuse them anywhere:
useQuery(todoQuery.getList())                              // client component
useSuspenseQuery(todoQuery.getDetail({ path: { id: 1 } }))
queryClient.prefetchQuery(todoQuery.getList())            // server / RSC
queryClient.ensureQueryData(todoQuery.getDetail({ path: { id: 1 } }))

// Compose & override per call — they're just objects
useQuery({ ...todoQuery.getList(), staleTime: 60_000 })

// Mutations are options too
useMutation(todoQuery.create())                           // POST → mutationOptions
Reusable
One options object drives useQuery, useSuspenseQuery, prefetchQuery, and ensureQueryData — client or server.
Lightweight
No generated hooks, no codegen step. Keys and queryFn are inferred from your schema — nothing to keep in sync.
Composable
Spread and override per call: { ...todoQuery.getList(), staleTime }. Your defaults, your way.
Keys · queryFn · mutationFn — one source of truth, derived from api.
🔒
End-to-end types
Request params and response shape — all inferred without any.
Runtime validation
Zod, Valibot, Yup, or any .parse() — validates both sides.
🔌
Transport agnostic
Swap fetch or Axios in one line. Schema stays the same.
🧱
Middleware
Retry, timeout, logging — stackable, composable functions.
🗂️
Nested routers
URL structure mirrors the type system naturally.
🌐
SSR / CSR ready
Same spec, different executor per environment.

Packages

@routar/core
Endpoint definitions, router, API client factory, fetch executor, and middleware.
npm install @routar/core
@routar/axios
Axios executor. Works with existing instances and interceptors.
npm install @routar/axios
@routar/ky
ky executor. Lightweight fetch wrapper with hooks support.
npm install @routar/ky
@routar/msw
MSW v2 handler factory — generate typed mock handlers from your RouterDef for testing.
npm install @routar/msw
@routar/react-query
TanStack Query bindings for routar — queryOptions/mutationOptions factories from a routar router
npm install @routar/react-query