Schema-first full-stack TypeScript framework

One schema.
Full stack.

Define your data shapes once with @cleverbrush/schema. Get type-safe servers, auto-typed clients, OpenAPI docs, dependency injection, auth, and React forms — all from that single definition. Zero duplication. Zero drift.

Get StartedWhy Cleverbrush?GitHub
Contract-firstZero codegenOpenAPI 3.1Built-in auth & DIClient resilienceReact integration

Define once, use everywhere

A single contract drives your server, client, and OpenAPI docs. Types flow automatically — no duplication, no drift.

step 1contract.ts
import { defineApi, endpoint, route } from '@cleverbrush/server/contract';
import { object, string, number, array } from '@cleverbrush/schema';

const User = object({
  id:    string().uuid(),
  name:  string().minLength(2),
  email: string().email(),
  age:   number().min(0).max(150)
});

export const api = defineApi({
  users: {
    list:   endpoint.get('/api/users').returns(array(User)),
    create: endpoint.post('/api/users')
              .body(object({ name: string(), email: string(), age: number() }))
              .returns(User),
    get:    endpoint
              .get('/api/users', route({ id: string() })`/${t => t.id}`)
              .returns(User)
  }
});
step 2server.ts
import { createServer, mapHandlers } from '@cleverbrush/server';
import { api } from './contract';

const server = createServer();

// mapHandlers gives a compile error if any endpoint is missing
mapHandlers(server, api, {
  users: {
    list:   async () => db.users.findAll(),
    create: async ({ body }) => db.users.insert(body),
    get:    async ({ params }) => db.users.findById(params.id)
    // ^ forget one? TypeScript error: Property 'get' is missing
  }
});

await server.listen(3000);
step 3app.ts
import { createClient } from '@cleverbrush/client';
import { api } from './contract';

const client = createClient(api, { baseUrl: 'http://localhost:3000' });

// Fully typed — no codegen, no manual annotations
const users = await client.users.list();
//    ^ User[]

const alice = await client.users.create({
  body: { name: 'Alice', email: 'alice@example.com', age: 30 }
});
//    ^ User — wrong shape is a compile error

How it compares

Key differences from popular TypeScript frameworks at a glance.

FeatureCleverbrushtRPCts-restHono
Standard REST endpointsYesNo (RPC only)YesYes
Typed client from contractYes (zero codegen)YesYesVia hc helper
OpenAPI 3.1 generationFull (links, callbacks, webhooks)Via pluginYesVia Zod OpenAPI
Exhaustive handler mappingmapHandlers() compile errorNoYes (Express/Fastify)No
Built-in client resilienceRetry, timeout, dedup, cache, batchingNo (DIY)No (DIY)No client
Integrated auth & DIJWT, RBAC, DI containerNoNoAuth middleware
WebSocket subscriptionsTyped, bidirectionalYesNoBasic
Schema-driven forms@cleverbrush/react-formNoNoNo

Detailed comparisons with code examples →

Everything you need, nothing you don't

Each library works standalone, but they compose into a seamless full-stack platform.

Powered by @cleverbrush/schema

The schema library is the foundation — zero dependencies, full type inference, runtime introspection, and Standard Schema compatibility. Try the interactive playground or read the docs.

Help us build the future of typed web development

Cleverbrush libraries are open source and community-driven. Whether it's fixing a bug, improving docs, suggesting a feature, or building a new library — every contribution matters.