# Cleverbrush Framework > Schema-first TypeScript libraries for validation, HTTP APIs, typed clients, > database access, forms and object mapping. Libraries compose without codegen. Start with schemas, share a browser-safe API contract, implement its handlers on the server, then create a client from that same contract. Database schemas and public response schemas can differ; select and map fields explicitly. ## Package map | Area | Package | Documentation | | --- | --- | --- | | Validation and extensions | `@cleverbrush/schema` | [Schema](https://schema.cleverbrush.com) | | JSON Schema conversion | `@cleverbrush/schema-json` | [README](https://github.com/cleverbrush/framework/tree/development/libs/schema-json) | | HTTP contracts and handlers | `@cleverbrush/server` | [Server](/server) | | HTTP clients and React hooks | `@cleverbrush/client` | [Client](/client/getting-started) | | API specifications | `@cleverbrush/server-openapi` | [OpenAPI / AsyncAPI](/server-openapi) | | Authentication and authorization | `@cleverbrush/auth` | [Auth](/auth) | | Dependency injection | `@cleverbrush/di` | [DI](/di) | | Environment configuration | `@cleverbrush/env` | [Env](/env) | | SQL queries and schema metadata | `@cleverbrush/knex-schema` | [Queries](/knex-schema) | | Entities, relations and tracking | `@cleverbrush/orm` | [ORM](/orm) | | Database migrations | `@cleverbrush/orm-cli` | [README](https://github.com/cleverbrush/framework/tree/development/libs/orm-cli) | | Object transformations | `@cleverbrush/mapper` | [Mapper](/mapper) | | Schema-driven React forms | `@cleverbrush/react-form` | [Forms](/react-form) | | Worker-thread jobs | `@cleverbrush/scheduler` | [Scheduler](/scheduler) | ## Core workflow This small example uses an in-memory collection, not persistent storage. Keep `contracts.ts` shared; do not import server implementations into browser bundles. ### 1. Define schemas and the shared contract ```typescript // contracts.ts import { defineApi, endpoint } from '@cleverbrush/server/contract'; import { array, boolean, number, object, string } from '@cleverbrush/schema'; export const Todo = object({ id: number(), title: string(), completed: boolean() }); const CreateTodo = object({ title: string().minLength(1).maxLength(200) }); export const api = defineApi({ todos: { list: endpoint.get('/api/todos').responses({ 200: array(Todo) }), create: endpoint.post('/api/todos') .body(CreateTodo).responses({ 201: Todo }) } }); ``` ### 2. Implement the handlers ```typescript // todos.ts import { ActionResult, implement } from '@cleverbrush/server'; import type { InferType } from '@cleverbrush/schema'; import { api, Todo } from './contracts.js'; const rows: InferType[] = []; let nextId = 1; export const todosModule = implement(api).group('todos').withHandlers({ list: () => rows, create: ({ body }) => { const todo = { id: nextId++, title: body.title, completed: false }; rows.push(todo); return ActionResult.created(todo); } }); ``` For large APIs, export a configured scope and define each handler in its own file using `Handler`. Compose feature modules at the root. See the [multi-file example](https://github.com/cleverbrush/framework/blob/development/libs/server/README.md#large-apis-and-shared-error-handling). ### 3. Register the complete implementation ```typescript // server.ts import { createServer, implement } from '@cleverbrush/server'; import { api } from './contracts.js'; import { todosModule } from './todos.js'; const server = createServer().withHealthcheck(); server.handleAll(implement(api).use(todosModule).complete()); await server.listen(3000); ``` `complete()` checks contract coverage. Server request validation and response types come from the endpoint schemas. Application persistence, permissions and business rules remain application code. ### 4. Call the API from a typed client ```typescript // client.ts import { createClient } from '@cleverbrush/client'; import { timeout } from '@cleverbrush/client/timeout'; import { api } from './contracts.js'; export const client = createClient(api, { baseUrl: 'http://localhost:3000', middlewares: [timeout({ timeout: 10_000 })] }); const todos = await client.todos.list(); const created = await client.todos.create({ body: { title: 'Buy milk' } }); ``` ## Schemas and extension authoring Schemas are immutable. Capture the returned builder when adding properties, validators or modifiers. `number()` validates integers by default; `.clearIsInteger()` permits fractional values. Use `.coerce()` explicitly when parsing strings such as query parameters. `InferType` derives the value type. Named schemas and `schemaRef()` support composition across packages. Define custom methods with `defineExtension()` and apply them with `withExtensions()`. Metadata is available from `schema.introspect().extensions`. For metadata-only methods whose values must also survive in TypeScript, use `defineMetadataMethod(key).argument()`, `.value(value)` or `.compute(fn)` and `InferExtensionMetadata`. These do not alter global builder prototypes or interpret application-specific metadata. See [schema extensions](https://github.com/cleverbrush/framework/blob/development/libs/schema/README.md#extensions). ## HTTP APIs and supporting services - **Server:** endpoint builders describe request schemas, response statuses, injection and authorization. `route()` supports schema-typed path parameters. `implement()` binds modular handlers; `errorMap()` / `withErrors()` translate known exceptions only into responses declared by the endpoint. `ActionResult` supplies status-specific, file and streaming results. Unexpected errors retain the server's Problem Details handling. [Server reference](/server). - **Subscriptions:** use `endpoint.subscription()` with `.incoming()` and `.outgoing()` schemas and async-generator handlers. Subscriptions participate in implementation coverage but do not use HTTP error policies. [Subscriptions](https://github.com/cleverbrush/framework/blob/development/libs/server/README.md#websocket-subscriptions). - **Client:** `createClient(contract, options)` infers calls from the contract. Middleware is imported from subpaths such as `/retry`, `/timeout`, `/dedupe`, `/cache` and `/batching`. Authentication, retry and cache policy must match the application. The `/react` client adds TanStack Query hooks including `.useQuery()` and `.useMutation()`; query state uses `isLoading`. `decodeValidationIssues()` extracts structured request-validation issues. [Client reference](/client/getting-started). - **OpenAPI / AsyncAPI:** configure `serveOpenApi({ server, info })` or `serveAsyncApi({ server, info })` as middleware before listening. For generation without middleware, pass registration metadata to `generateOpenApiSpec()` or `generateAsyncApiSpec()`. Named schemas become reusable components. [Specification generation](/server-openapi). - **Authentication:** configure authentication schemes and authorization policies on the server; declare endpoint principal requirements with `.authorize()`. JWT, cookies, OAuth2 and OIDC are supported. Keep signing keys and server-only configuration out of shared contracts. [Authentication](/auth). - **Dependency injection:** schemas act as typed service tokens. Register values or factories with singleton/scoped/transient lifetimes. Endpoint `.inject()` declares dependencies supplied as the handler's second argument. [Dependency injection](/di). - **Environment:** `env(name, schema)` binds leaves in a nested configuration; `parseEnv()` validates and resolves it. `parseEnvFlat()` supports flat configs. Use explicit coercion and `envBoolean()` for environment boolean conventions. [Environment configuration](/env). ## Database queries, entities and result mapping Import database-capable schema factories from `@cleverbrush/knex-schema` or `@cleverbrush/orm`, not plain `@cleverbrush/schema`. Table and column metadata belong to these packages. Knex is a peer dependency; PostgreSQL also needs `pg`. - **Queries:** `query(knex, schema)` / `createQuery(knex)` provide typed selectors, CRUD, scopes, named projections, eager loading and transactions. Aliases express flat joins; aggregate expressions support summaries and explicit output schemas. Composite cursors require a deterministic, non-null sort with a unique tie-breaker. [Query guide](/knex-schema). - **ORM:** `defineEntity(schema)` declares relations; `createDb(knex, entities)` exposes typed DbSets. Ordinary entity queries support identity tracking, change persistence, optimistic concurrency and STI/CTI variants. Use `@cleverbrush/orm-cli` for migrations. [ORM guide](/orm). - **Immutable queries:** every Framework query exposes `rowSchema` automatically. Retain returned builders. `rowSchema` describes the decoded selection, and accessing it never executes SQL. Full ORM entities remain tracked when enabled; projected, grouped, distinct and raw rows are detached. SQL nulls stay `null`, dates decode to `Date` throughout nested graphs, and declared decimal/bigint columns retain exact strings. Input defaults and preprocessors are not replayed. [Read schemas](/knex-schema#row-schemas), [detached ORM reads](/orm#detached-read-schemas). - **Shape-preserving predicates:** ordinary/aliased readers and ordinary ORM reads support grouped `where`/`andWhere`/`orWhere`, null checks, IN/NOT IN value arrays or SELECT subqueries, EXISTS/NOT EXISTS, and OR variants. Use `whereRaw` / `orWhereRaw` / `orderByRaw` with positional bindings and `ref(selector)` for quoted mapped columns, including generated child aliases. Group callbacks run synchronously once and receive only predicate methods; retained groups are immutable too. Return the configured group, never void or a Promise. Subquery SQL/bindings are captured without executing SQL. These operations retain rowSchema identity; assign their returned reader. Raw fragments are trusted application SQL, not a sandbox. Keep authorization outside grouped search ORs. Opaque SELECTs use `apply(configure, { output })` or `selectRaw(sql, bindings, { output })` with a synchronous Framework object schema. Numbered pagination retains ordering; composite cursors replace it with their explicit orderBy specification. [Read predicates](/knex-schema#read-predicates). - **Nested and polymorphic reads:** child customizers return their configured queries. Nested includes execute in one SQL statement. STI/CTI readers expose a union `rowSchema` and per-variant `variantRowSchemas`; DTO selection and discriminator dispatch remain explicit application code. - **Mapping:** `mapper().configure(Source, Target, configure)` checks that every target property is mapped, auto-mapped or ignored. `.from()` copies values; `.compute()` transforms them. `getMapper()` returns an asynchronous function. `getSyncMapper()` returns ordinary values when all final steps and nested mappings are synchronous; known async mappings are rejected and disguised thenables throw when invoked. Configuration does not probe callbacks. Reuse schema instances and configured mappings; the registry uses schema identity. Mapping does not fetch data or authorize access. [Mapper guide](/mapper), [synchronous mapping](/mapper#synchronous-mapping). For separate query, mapping and service files, storage representation rules and supported read operations, see the [complete query README](https://github.com/cleverbrush/framework/blob/development/libs/knex-schema/README.md#projection-aware-reads). ## Forms and background work - **React Form:** schema-driven fields, validation and submission state. Structured `FormIssue` values carry server validation errors to nested fields; application business errors require explicit paths. [Forms](/react-form). - **Scheduler:** `JobScheduler` runs configured jobs in worker threads with scheduling, retry, timeout and lifecycle events. Configure job files and timezone explicitly. [Scheduler](/scheduler). ## Further reading - [Getting Started](/getting-started): full-stack tutorial - [Examples](/examples): Todo application - [Why Cleverbrush?](/why): design goals - [Schema library](https://schema.cleverbrush.com): validation and playground - [GitHub](https://github.com/cleverbrush/framework): source and tests