# Why Brick Was Built Why Brick exists, the architectural problems it solves, and the design principles behind the framework. Source: /docs/why-brick Most backend frameworks solve HTTP routing and leave the rest to you. Where does business logic belong? How are inputs and outputs validated? How does authorization compose? How do internal services call each other without network latency or serialization overhead? How do you prevent OpenAPI drift, maintain typed frontend clients, and expose capabilities to AI agents without maintaining duplicate glue code? Those are your problems. Express, Fastify, Hono, and Elysia don't have an opinion—they match HTTP verbs and pathnames to callback functions. ## The Architecture Problem The consequence of router-only frameworks is predictable. Every engineering team invents a custom backend architecture from scratch: - **Validation is smeared**: Body checks, parameter sanitization, and response shaping are scattered across route controllers, middleware chains, or ad-hoc schema libraries. - **Authorization is precarious**: Access controls hide inside nested middleware stacks or deep inside database queries. A single omitted middleware or misconfigured route guard silently opens security holes. - **The "Monolith vs. Microservices" false dichotomy**: Teams either build an untyped monolith—where services directly mutate each other's database tables and bypass domain logic—or jump prematurely to microservices, paying the brutal tax of network serialization, HTTP loopback latency, independent deployments, and distributed transaction failures. - **Contract and tooling drift**: OpenAPI documentation, frontend client fetchers, and AI agent tool manifests are manually maintained or assembled from fragile, multi-step codegen pipelines that fall out of sync with real production behavior. This is why clean, scalable TypeScript backend codebases are vanishingly rare. Maintaining a growing TypeScript backend usually requires Staff-level engineers to establish bespoke conventions, police them in every code review, and fight architectural decay for years. Fast HTTP routers aren't bad. They are great. They are also insufficient. ## A Fixed Answer What if there were no decisions to make about how to structure your backend application? Not because someone took choice away, but because the right answer was the only answer. What if backend architecture was solved? **That is Brick.** Every Brick application has the same architecture. Not by convention. **By design.** ```mermaid flowchart TD Client["Client (Browser SDK / HTTP / MCP Agent)"] --> Gateway["brick() HTTP Server (Elysia on Bun)"] Gateway --> Dispatch["Monomorphic Dispatcher"] Dispatch --> ServiceA["Service: Pages"] Dispatch --> ServiceB["Service: Users"] ServiceA --> Actions["Typed Actions (TypeBox)"] ServiceA --> CRUD["Declarative CRUD (@brickkit/crud)"] CRUD --> DB[("Database (Postgres / SQLite)")] ServiceA -. "In-process (createServiceProxy)" .-> ServiceB ``` ## Power Through Constraints Brick replaces fragmented conventions with foundational architectural primitives: ### 1. Isolated Service Boundaries In Brick, domain boundaries are explicit. Services are declared with `defineService(name, options)` and act as the single owner of their domain logic, database tables, and access rules: - A service owns its database connection and schema models. - Services cannot arbitrarily bypass each other's security policies or reach into foreign tables. - Cross-service communication happens through typed service proxies (`createServiceProxy()`), preserving domain boundaries whether running in the same process today or separated into independent services tomorrow. ### 2. Actions as Atomic Capabilities An action is the fundamental unit of computation in Brick. Instead of scattering route handlers, validation logic, authorization checks, and error formatting across multiple files, `defineAction()` bundles them into a single, cohesive definition: ```ts title="src/services/billing.ts" import { defineAction, defineService, t } from "@brickkit/core"; export const billingService = defineService("billing"); export const chargeInvoice = billingService.action({ name: "chargeInvoice", input: t.Object({ invoiceId: t.String(), amount: t.Integer({ minimum: 1 }), }), output: t.Object({ status: t.Union([t.Literal("succeeded"), t.Literal("pending")]), transactionId: t.String(), }), authorize: ({ user }) => Boolean(user?.canManageBilling), execute: async ({ input, ctx, error }) => { // Business logic runs only after input validation and authorization pass return { status: "succeeded", transactionId: "txn_123" }; }, }); ``` Every action declares its contract up front. If input validation fails, the action rejects early with structured errors. If authorization fails, execution halts before domain logic runs. ### 3. Monomorphic In-Process & HTTP Dispatch In traditional frameworks, if `Service A` needs to trigger logic inside `Service B`, you must choose between making an expensive HTTP network loopback call (incurring serialization, latency, and socket overhead) or directly importing an unvalidated internal function (bypassing authorization and input guarantees). Brick solves this with **monomorphic execution**: - An action is an HTTP route and a callable TypeScript function at the same time. - Calling `chargeInvoice({ input: { ... } })` executes in-process through the **exact same validation, authorization, and error handling pipeline** as an incoming HTTP `POST /api/billing/chargeInvoice` request. - Zero network serialization penalty. Zero HTTP loopback overhead. 100% guarantee that authorization rules and validation schemas are always enforced. ### 4. Deny-by-Default Declarative CRUD Most data breaches happen when internal columns (like password hashes, internal roles, or billing identifiers) leak through over-fetching or unauthorized updates. Brick's `@brickkit/crud` plugin enforces deny-by-default security: - Tables attached to a service expose **nothing** by default. - Reads, creates, updates, filters, and sorts must be explicitly opted into per field. - Access policies (`public` vs. custom `authorize` predicates) are evaluated before database queries touch the driver. ```ts title="src/services/pages.ts" pagesService.resource({ name: "page", table: pages, id: pages.id }).use( crud({ access: { authorize: ({ ctx }) => Boolean(ctx.user), }, fields: { id: { read: true, filter: ["eq"] }, title: { read: true, create: true, update: true, filter: ["contains"] }, published: { read: true, update: true }, // internalNotes is omitted: impossible to read, query, or mutate via API }, }), ); ``` ### 5. Automated, Isolated Client Contracts End-to-end type safety between frontend and backend is often fraught with friction: - Direct TypeScript type imports (like basic RPC frameworks) risk leaking backend-only modules, database drivers (`drizzle-orm`, `node:sqlite`, `pg`), and server secrets into frontend browser bundles. - Traditional codegen tools (like OpenAPI generators or GraphQL compilers) require manual CLI steps, generate bloated SDK wrappers, and constantly drift from real production behavior. Brick solves this through **automated contract extraction**: - The `@brickkit/cli/vite` plugin hooks directly into Vite's dev server and build pipeline. It extracts browser-safe transport types and route metadata directly from your `BrickApp` into a lightweight, gitignored `_brick/contract.ts`. - When backend actions or schemas change, Vite HMR regenerates the contract instantly in the background. - The browser client uses `createBrickClient({ contract })`, giving frontend developers full autocomplete, parameter validation, and typed responses without manual codegen commands or runtime bundle leaks. ## Focus on Behavior, Not Plumbing When architectural plumbing is off the table, development elevates from implementation trivia to core domain behavior: - Not _"How do we wire middleware to authenticate this endpoint and parse query params?"_ but **"What are the input boundaries and permission rules for this action?"** - Not _"Should we build microservices or a monolith?"_ but **"What service owns this capability?"** (Brick lets you develop as a logical monolith today with shared types and zero network latency, while preserving the clean physical boundaries needed to split services tomorrow). - Not _"How do we keep OpenAPI schemas, frontend SDKs, and Swagger docs in sync?"_ but **"What capabilities does our product offer?"** ## Readable by Design ### Readable to Developers Any developer can walk into any Brick codebase and immediately navigate: 1. **Services** declare the system boundaries (`defineService`). 2. **Actions** define all business operations with explicit TypeBox schemas (`defineAction`). 3. **Resources** define database persistence with declarative field policies (`service.resource`). 4. **Clients** consume typed endpoints via `createBrickClient`—with automated contract synchronization and complete browser/server bundle isolation. Because the framework enforces this structure, there is no ambiguity about where logic lives, how errors are surfaced, or how data flows. ### Readable to AI Agents The exact properties that make Brick codebases legible to developers make them exceptionally powerful for AI agents: - **Predictable mental model**: Coding agents (Cursor, Claude Code, Copilot) don't hallucinate arbitrary middleware order or complex controller layers. The pattern is always Service → Action / Resource → Handler. - **Architecture Introspection**: With `brick info src/app.ts`, Brick exports the complete service graph, registered action routes, access rules, and database schema into structured JSON that agents can inspect. - **Native Model Context Protocol (MCP)**: Every service can expose its actions and tools directly to AI workflows using `createMcpRegistry` and `createMcpHandler`, turning your backend into an agent-ready tool platform with zero manual wrapping. ## How Brick Compares | Dimension | Raw Routers (Hono, Elysia, Express) | Fullstack RPC (tRPC) | Enterprise Frameworks (NestJS) | Brick-TS | | :--------------------- | :---------------------------------- | :------------------------ | :----------------------------- | :---------------------------------- | | **Primary Focus** | Request/response routing | End-to-end type safety | Dependency injection & OOP | Complete service architecture | | **Domain Boundaries** | Ad-hoc / Unstructured | Monolithic routers | Modules & Decorators | Explicit isolated services | | **Internal Execution** | HTTP loopback or direct import | Monolith procedure call | In-memory DI services | Monomorphic in-process dispatch | | **CRUD & Data Access** | Manual ORM glue | Manual queries | Repositories & DTOs | Deny-by-default declarative CRUD | | **Client Type Safety** | Manual fetch / OpenAPI codegen | Inferred types (RPC-only) | Generated OpenAPI / SDKs | Automated contract (Vite HMR / CLI) | | **AI & MCP Support** | None (manual) | None (manual) | None (manual) | Built-in MCP registry & tools | | **Runtime Engine** | Varies (Node/Bun/Edge) | Node / Edge | Node.js | Bun + Elysia + TypeBox + Drizzle | ## Build Your Product, Not Your Architecture Backend development should be about solving business problems, not assembling repetitive infrastructure. Time spent debugging middleware precedence, hand-writing CRUD endpoints, fixing serialization bugs, and keeping client contracts in sync is pure overhead. Brick models the backend so you can model your business: - **Services, Actions, Resources, Field Policies, Type Inference, OpenAPI, and MCP Agents.** - All accounted for. - All connected. - All typed. - All in one place. Backend architecture is solved. The only question left is: **what are you going to build?** ## Next Steps - Explore the [Mental Model & Overview](/docs) for architecture details. - Build a runnable API in under 5 minutes with the [Quickstart](/docs/quickstart). - Follow the step-by-step [REST API Tutorial](/docs/tutorial). - Learn how to integrate AI tools in [For AI Agents](/docs/for-ai-agents) and [Tools & MCP](/docs/tools-and-mcp). --- # Overview & Architecture Learn the mental model, core design principles, and architecture of Brick-TS. Source: /docs Brick-TS is a TypeScript backend framework built on Bun, Elysia, TypeBox, and Drizzle that delivers monomorphic in-process execution with automatic HTTP and OpenAPI exposure. ## Minimal Example Define a database, register a service, attach a declarative CRUD resource with explicit field policies, and start the HTTP server in a single file: ```ts title="src/server.ts" import { brick, defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; // 1. Declare schema with Drizzle export const pages = sqliteTable("pages", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), content: text("content"), }); // 2. Configure database and service const database = defineDatabase({ tables: { pages } }); export const pagesService = defineService("pages", { database }); // 3. Attach CRUD with explicit access policy and field permissions pagesService.resource({ name: "page", table: pages, id: pages.id }).use( crud({ access: "public", fields: { id: { read: true, filter: ["eq"] }, title: { read: true, create: true, update: true, filter: ["eq", "contains"], sort: true, }, content: { read: true, create: true, update: true }, }, }), ); // 4. Mount the service mesh onto an Elysia HTTP server const app = brick({ services: [pagesService] }); app.listen(4000); console.log("Server listening on http://localhost:4000"); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `pages` | Field | Type | Description | | --------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `content` | `text` | Page content. Optional | ## Architectural Mental Model Brick-TS unifies monolithic developer ergonomics with distributed system boundaries: ```mermaid flowchart TD Client["HTTP Client / Browser SDK"] --> Gateway["brick() HTTP Server (Elysia)"] Gateway --> Docs["/_health, /openapi.json, /docs, /swagger"] Gateway --> Dispatch["AOT Router & Compiler"] Dispatch --> ServiceA["Service: Pages"] Dispatch --> ServiceB["Service: Users"] ServiceA --> Actions["Custom Actions (TypeBox)"] ServiceA --> CRUD["CRUD Engine (@brickkit/crud)"] CRUD --> DB[(Database: SQLite / PostgreSQL)] ServiceA -. "createServiceProxy()" .- ServiceB ``` ### 1. Logical Monolith, Physical Microservices Build your application in a single repository with shared TypeScript types and instant jump-to-definition. Services can execute actions in-process without network overhead or data serialization. If an application grows into separate microservices, actions retain identical input/output schemas and authorization rules. ### 2. Monomorphic In-Process Dispatch In Brick-TS, an action is a callable TypeScript function and an HTTP endpoint simultaneously. Calling `pagesService.getAction("page.get")({ input: { id: "1" } })` executes in-process through the exact same validation, authorization, and error handling pipeline that HTTP requests navigate, without HTTP loopback latency. ### 3. Pure TypeScript Type Inference Brick-TS eliminates code generation build steps. Type definitions flow directly from runtime TypeBox schemas (`Static`) and Drizzle database models (`$inferSelect`, `$inferInsert`). Frontend clients infer the backend contract through type imports. ### 4. Deny-by-Default Security Security policies are mandatory and explicit: - **CRUD Operations**: Reject all access unless explicitly configured with `access: "public"` or an `access: { authorize, scope }` policy. - **Field Exposure**: Columns remain hidden from reads, filters, sorting, and writes until explicitly enabled in the field policy. - **Context Isolation**: In-process service proxies execute inside the recipient service's isolated context and database connection, preventing callers from overriding security resolvers. ## Framework Packages The framework is published as three modular packages at `v0.3.0`: | Package | Purpose | Imports | | :--------------- | :-------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------ | | `@brickkit/core` | Core runtime, services, actions, database handles, HTTP server, and client SDK | `defineService`, `defineAction`, `defineDatabase`, `brick`, `createBrickClient` | | `@brickkit/crud` | Deny-by-default declarative CRUD engine with field policies, filtering, and hooks | `crud` | | `@brickkit/cli` | Development server with live reload, production build compiler, and artifact generators | `brick` CLI binary (`dev`, `start`, `build`, `gen`, `info`) | --- # Quickstart Build and run an in-memory SQLite CRUD service in less than 5 minutes. Source: /docs/quickstart This guide gets a runnable Brick-TS service running locally with Bun and SQLite in less than five minutes. ## 1. Install Dependencies Initialize a new Bun project and install `@brickkit/core`, `@brickkit/crud`, and `@brickkit/cli`: **bun** ```bash mkdir my-brick-app && cd my-brick-app bun init -y bun add @brickkit/core @brickkit/crud @brickkit/cli ``` **pnpm** ```bash pnpm add @brickkit/core @brickkit/crud @brickkit/cli ``` **npm** ```bash npm install @brickkit/core @brickkit/crud @brickkit/cli ``` ## 2. Create the Application Create `src/app.ts` with a SQLite table, a database handle, and a CRUD resource: ```ts title="src/app.ts" import { brick, defineDatabase, defineService, sqliteTable, syncSchema, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; // Define the database table with Drizzle export const pages = sqliteTable("pages", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), content: text("content"), }); // Configure an in-memory database for immediate development const database = defineDatabase({ tables: { pages } }); export const pagesService = defineService("pages", { database }); // Register the CRUD resource. CRUD requires an explicit access policy. pagesService.resource({ name: "page", table: pages, id: pages.id }).use( crud({ access: "public", fields: { id: { read: true, filter: ["eq"] }, title: { read: true, create: true, update: true, filter: ["eq", "contains"], sort: true, }, content: { read: true, create: true, update: true }, }, }), ); // Prepare the in-memory SQLite table schema syncSchema(pagesService); // Export the default application for the brick CLI or embedded runtime export const app = brick({ services: [pagesService], title: "Pages API", version: "1.0.0", }); export default app; ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `pages` | Field | Type | Description | | --------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `content` | `text` | Page content. Optional | ## 3. Start the Server Run the development server with live reload using the `brick` CLI: ```bash bunx brick dev src/app.ts --port 4000 ``` The CLI loads your entry, then prints the registered services and the route each action is mounted on: ```text [Brick-TS] Loading application entry: /path/to/my-brick-app/src/app.ts 🧱 Brick-TS Development Server Registered Services: 1 • pages (8 actions) -> POST /api/page/query -> GET /api/page/:id -> POST /api/page -> PATCH /api/page/:id -> DELETE /api/page/:id -> POST /api/page/find-one -> POST /api/page/count -> POST /api/page/exists ⚡ API Gateway running on: http://localhost:4000 🩺 Health check: http://localhost:4000/_health 🗺️ Architecture graph: http://localhost:4000/_brick/services ``` CRUD routes are mounted under the resource name (`page`), not the service name. The action names are still `page.list`, `page.get`, `page.create`, and so on. > `brick dev` imports your entry to register its services, then builds its own > server with the default `/api` prefix. Options you pass to `brick()` in the > entry (such as `title`) don't apply under `brick dev`. Without `--port`, the > port comes from the `PORT` environment variable, then defaults to `4000`. ## 4. Test the API Endpoints Execute HTTP requests against the generated CRUD endpoints. Successful calls return `200` with the JSON result. ### Create a Record ```bash curl -X POST http://localhost:4000/api/page \ -H "Content-Type: application/json" \ -d '{"title": "First Page", "content": "Welcome to Brick-TS!"}' ``` Response: ```json { "id": "e379b38c-8f19-485a-a71c-3b91ef5246a4", "title": "First Page", "content": "Welcome to Brick-TS!" } ``` ### List Records with Filtering `page.list` is a `POST` to `/api/page/query`. The body is validated against the filter operators and sortable fields you enabled in `fields`: ```bash curl -X POST http://localhost:4000/api/page/query \ -H "Content-Type: application/json" \ -d '{ "where": { "field": "title", "op": "contains", "value": "First" }, "orderBy": [{ "field": "title", "direction": "asc" }], "limit": 10 }' ``` Response: ```json { "items": [ { "id": "e379b38c-8f19-485a-a71c-3b91ef5246a4", "title": "First Page", "content": "Welcome to Brick-TS!" } ], "limit": 10, "offset": 0 } ``` ### View Interactive API Documentation Visit the interactive API documentation and spec endpoints provided out of the box. They're mounted at the root, not under the `/api` prefix: - Scalar API Docs: `http://localhost:4000/docs` - Swagger UI: `http://localhost:4000/swagger` - OpenAPI 3.1 Spec: `http://localhost:4000/openapi.json` - Service Architecture Graph: `http://localhost:4000/_brick/services` - Health Check: `http://localhost:4000/_health` --- # Tutorial: Building a REST API Build a complete, production-ready REST API end to end with database models, CRUD, access control, and custom actions. Source: /docs/tutorial This tutorial walks through building a complete blog API featuring database models, CRUD resources, context resolvers, authorization policies, and custom business actions. ### Step 1: Project Setup and Dependencies Initialize a Bun project and install the Brick-TS packages: ```bash mkdir blog-api && cd blog-api bun init -y bun add @brickkit/core @brickkit/crud @brickkit/cli ``` ### Step 2: Define Database Models Declare tables for articles and comments using Drizzle schema builders in `src/schema.ts`: ```ts title="src/schema.ts" import { integer, sqliteTable, text } from "@brickkit/core"; export const articles = sqliteTable("articles", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), slug: text("slug").notNull().unique(), body: text("body").notNull(), authorId: text("author_id").notNull(), views: integer("views").default(0).notNull(), }); export const comments = sqliteTable("comments", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), targetId: text("target_id").notNull(), authorId: text("author_id").notNull(), body: text("body").notNull(), }); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `articles` | Field | Type | Description | | ----------- | --------- | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `slug` | `text` | URL slug. Unique | | `body` | `text` | Text content. | | `author_id` | `text` | Author identifier supplied by the application. | | `views` | `integer` | Article view count. Default: 0 | #### Table Name: `comments` | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `target_id` | `text` | Logical parent article identifier; no SQL foreign-key constraint. | | `author_id` | `text` | Author identifier supplied by the application. | | `body` | `text` | Text content. | ### Step 3: Configure Database and Service Context Create `src/service.ts` to configure the database handle and a context resolver that extracts the authenticated user from the HTTP `Authorization` header: ```ts title="src/service.ts" import { defineDatabase, defineService } from "@brickkit/core"; import { articles, comments } from "./schema"; export interface User { id: string; role: "admin" | "author" | "reader"; } // 1. Create a database handle export const database = defineDatabase({ tables: { articles, comments }, }); // 2. Define the service with a typed context resolver export const blogService = defineService("blog", { database, context: async (baseCtx) => { const authHeader = baseCtx.request.headers.get("authorization"); if (!authHeader) return { user: null }; // In a real application, verify JWT or session token here const token = authHeader.replace("Bearer ", "").trim(); if (token === "admin-token") { return { user: { id: "usr_admin", role: "admin" as const } }; } if (token === "author-token") { return { user: { id: "usr_author", role: "author" as const } }; } return { user: null }; }, }); ``` ### Step 4: Add CRUD Resources with Access Policies Attach CRUD operations to `articles` with field policies, a before-create hook, and operation authorization in `src/resources.ts`. `authorId` has no `create` permission, so clients can't send it; the hook fills it in from the session: ```ts title="src/resources.ts" import { ActionExecutionError, eq } from "@brickkit/core"; import { crud } from "@brickkit/crud"; import { articles } from "./schema"; import { blogService } from "./service"; export const articleResource = blogService .resource({ name: "article", table: articles, id: articles.id, }) .use( crud({ // CRUD is deny-by-default; an explicit access policy is required access: { authorize: ({ operation, ctx }) => { // Public reads if ( operation === "list" || operation === "get" || operation === "findOne" ) { return true; } // Only authenticated authors or admins can write return !!ctx.user; }, // Row-level writes: CRUD ANDs this into the WHERE clause of update // and delete, so authors can only touch their own articles. // `authorize` above already rejected anonymous callers. scope: ({ operation, ctx }) => { if (operation !== "update" && operation !== "delete") return undefined; if (ctx.user?.role === "admin") return undefined; return eq(articles.authorId, ctx.user!.id); }, }, fields: { id: { read: true, filter: ["eq", "in"] }, title: { read: true, create: true, update: true, filter: ["contains"], sort: true, }, slug: { read: true, create: true, filter: ["eq"] }, body: { read: true, create: true, update: true }, authorId: { read: true, filter: ["eq"] }, views: { read: true, sort: true }, }, hooks: { beforeCreate: async ({ data, ctx, error }) => { if (!ctx.user) return error.UNAUTHORIZED( "Authentication required to create articles", ); // authorId isn't writable by clients; set it from the session data.authorId = ctx.user.id; }, }, }), ); ``` ### Step 5: Add a Custom Business Action Add a custom action for incrementing article views. Actions provide full TypeBox schema validation and custom error helpers: ```ts title="src/actions.ts" import { eq, sql, t } from "@brickkit/core"; import { articles } from "./schema"; import { blogService } from "./service"; export const incrementView = blogService.action({ name: "incrementView", description: "Atomically increment the view counter for an article", method: "POST", path: "/api/blog/articles/:id/view", input: t.Object({ id: t.String(), }), output: t.Object({ id: t.String(), views: t.Number(), }), execute: async ({ input, ctx, error }) => { const updated = await ctx.db .update(articles) .set({ views: sql`${articles.views} + 1` }) .where(eq(articles.id, input.id)) .returning({ id: articles.id, views: articles.views }); if (!updated.length) { error.NOT_FOUND(`Article with ID '${input.id}' not found`); } return updated[0]; }, }); ``` ### Step 6: Start the Server Create the server entry in `src/server.ts`, sync the SQLite schema, and start the application: ```ts title="src/server.ts" import { brick, syncSchema } from "@brickkit/core"; import { blogService } from "./service"; import "./resources"; import "./actions"; // Prepare SQLite database tables in development syncSchema(blogService); export const app = brick({ services: [blogService], title: "Blog API Mesh", version: "1.0.0", }); export default app; ``` Run the server with the `brick` CLI: ```bash bunx brick dev src/server.ts --port 4000 ``` `syncSchema()` creates the tables of resources registered on the service (here, `articles`) with `CREATE TABLE IF NOT EXISTS`. It's a development helper, not a migration runner. ### Step 7: Verify with Requests CRUD routes are mounted under the resource name (`/api/article`), while the custom action uses the `path` you declared. #### 1. Create an Article (Authorized) ```bash curl -X POST http://localhost:4000/api/article \ -H "Authorization: Bearer author-token" \ -H "Content-Type: application/json" \ -d '{ "title": "Getting Started with Brick-TS", "slug": "getting-started-with-brick-ts", "body": "Brick-TS makes building backend services fast and safe." }' ``` Without the `Authorization` header, `authorize` returns `false` and the request fails with `403` and code `FORBIDDEN`. #### 2. Increment Article View (Custom Action) ```bash curl -X POST http://localhost:4000/api/blog/articles/YOUR_ARTICLE_ID/view ``` The `:id` path parameter becomes `input.id`, so no body is needed. #### 3. List Articles (Public Read) ```bash curl -X POST http://localhost:4000/api/article/query \ -H "Content-Type: application/json" \ -d '{"limit": 5, "orderBy": [{"field": "views", "direction": "desc"}]}' ``` ## Related Topics [Services & Actions](/docs/services-and-actions) Explore the full action definition API and error helpers. [Access Control](/docs/access-control) Configure role-based access and row scoping for multi-tenant applications. [Relational Data Modeling](/docs/relations) Learn patterns for child tables, joins, and parent-child cascades. [Typed Client](/docs/typed-client) Consume this API with end-to-end type safety from React or Next.js. --- # For AI agents Give Cursor, Claude Code, and Copilot Brick documentation, project instructions, and MCP tools Source: /docs/for-ai-agents Give your coding agent the documentation index, then let it fetch the pages needed for the task. ## Read markdown documentation - [llms.txt](/llms.txt) lists every documentation page, grouped by section with a short description. - [llms-full.txt](/llms-full.txt) contains all pages in sidebar order. - Each page has a markdown URL, such as [quickstart.md](/docs/quickstart.md). To request markdown at the regular page URL: ```bash curl -H 'Accept: text/markdown' https://www.brickkit.dev/docs/quickstart ``` Start an agent task with this instruction: ```text Read https://www.brickkit.dev/llms.txt. Fetch the relevant markdown pages before changing this Brick project. Check package.json for the installed Brick version and follow the existing service definitions and verification scripts. ``` In Cursor, add the index URL as documentation context or include it in your chat prompt. In Claude Code, put the instruction in your project's `CLAUDE.md`. In Copilot, put it in `.github/copilot-instructions.md`. If your agent cannot fetch URLs, download the required `.md` files and add them to its context. ## Choose docs for the task Start with [Quickstart](/docs/quickstart) for a runnable CRUD service or the [REST API tutorial](/docs/tutorial) for a complete application. Fetch the focused guide when changing an existing project: - [Services and actions](/docs/services-and-actions), [Context](/docs/context), and [Validation and errors](/docs/validation-and-errors) for business logic. - [Resources](/docs/resources), [Access control](/docs/access-control), and [Relations](/docs/relations) for CRUD and database changes. - [Typed client](/docs/typed-client) for browser calls and [Tools and MCP](/docs/tools-and-mcp) for agent tools. - [API cheatsheet](/docs/reference/cheatsheet) for symbol lookup and [CLI reference](/docs/brick-cli) for commands. ## Install the Brick skill The repository includes a [Brick skill](https://github.com/Chandraprakash-Darji/brick/blob/main/skills/brick/SKILL.md) for agents that support the skills format: ```bash npx skills add Chandraprakash-Darji/brick --skill brick ``` Choose your agent and project scope in the installer. The skill covers documentation lookup, service registration, schemas, authorization, typed clients, and MCP tools. ## Add project instructions Copy this into your project's `AGENTS.md`, or adapt it for your agent's instruction file: ```md ## Brick - Read https://www.brickkit.dev/llms.txt and fetch relevant markdown pages. - Check the installed @brickkit package versions before using an API. - Follow existing defineService, action, resource, and tool definitions. - Define input and output schemas with t from @brickkit/core. - Keep authorization in action and tool definitions and resource access rules. - Import definition modules before compiling or exporting the registry. - Use generated client contracts for browser calls. - Run this project's type checks and relevant tests after changes. ``` Add your own commands, service entry path, and migration rules to make these instructions useful for your project. ## Connect to your application's MCP tools Brick provides `createMcpRegistry` and `createMcpHandler` for exposing your app's tools. Register tools with `service.tool()`, import their modules, then mount the handler in your authenticated HTTP layer. See [Tools and MCP](/docs/tools-and-mcp) for the implementation and the Pages example for an authenticated transport. Point your agent's MCP client at the endpoint your app mounts, using that app's authentication. This is an application endpoint you provide; Brick's documentation site does not run a hosted MCP server. Tool annotations describe behavior, while `authorize` enforces access. ## Generate context from your app Export the service graph from your application's entry file: ```bash bunx brick info src/app.ts > brick-architecture.json ``` Replace `src/app.ts` with your app's entry. The CLI loads its definitions before exporting the architecture. Or fetch `/_brick/services` from your running app. The export describes services, mounted action routes, resource access policies, and database metadata. See [Architecture Introspection](/docs/brick-cli/info) for its fields. The database handle is excluded, but other service options are included. Review the output for configuration values before sharing it with an agent. There is no dedicated agent-context generation command yet. Combine this architecture JSON with the relevant markdown docs and your project instructions. --- # Services and Actions Define modular backend services and typed actions with runtime schema validation. Source: /docs/services-and-actions Services structure backend domains in Brick-TS, while actions provide typed business logic executable both in-process and over HTTP. ## Minimal Example Define a service, register an action with TypeBox input/output schemas, and invoke it either directly in TypeScript or through HTTP: ```ts title="src/services/users.ts" import { defineService, t } from "@brickkit/core"; // Mock data store const users = new Map([ ["usr_1", { id: "usr_1", name: "Ada Lovelace", email: "ada@example.com" }], ]); // 1. Define the service domain export const usersService = defineService("users"); // 2. Define a typed action export const getUser = usersService.action({ name: "getUser", description: "Retrieve a user profile by unique ID", method: "GET", path: "/api/users/:id", input: t.Object({ id: t.String({ minLength: 1 }), }), output: t.Object({ id: t.String(), name: t.String(), email: t.String(), }), execute: ({ input, error }) => { const user = users.get(input.id); if (!user) { // Error helpers throw; `return` lets TypeScript narrow `user` return error.NOT_FOUND(`User with ID '${input.id}' not found`); } return user; }, }); ``` > Brick doesn't register TypeBox string formats such as `email` or `uuid`. A > schema with an unregistered `format` fails validation ("Unknown format"), so > register formats with TypeBox's `FormatRegistry` before using them. ## Services (`defineService`) A service groups a database handle, a context resolver, resources, and custom actions: ```ts title="src/services/orders.ts" import { defineDatabase, defineService } from "@brickkit/core"; import { ordersTable } from "./schema"; const database = defineDatabase({ tables: { ordersTable } }); export const ordersService = defineService("orders", { database, context: async (baseCtx) => { const token = baseCtx.request.headers.get("authorization"); return { token, tenantId: "tenant_default" }; }, }); ``` ### Service Options Table | Option | Type | Description | | :--------- | :------------------------------------------ | :---------------------------------------------------------------------------------------- | | `database` | `DatabaseHandle` | Database handle created via `defineDatabase()`. Accessible in actions as `ctx.db`. | | `context` | `(baseCtx: BaseContext) => Promise \| T` | Context resolver function. Injects user, session, or tenant properties into every action. | `ServiceOptions` also declares `auth`, `rateLimit`, `cache`, `resilience`, `telemetry`, and `lifecycle`. The runtime doesn't act on them today: no lifecycle hooks run and no rate limits or auth defaults are enforced. They're only echoed in the service's introspection output (`/_brick/services`). Enforce access with `authorize` on actions and `access` on CRUD resources. ## Actions (`service.action` / `defineAction`) Actions are the core unit of compute in Brick-TS. Every action is registered with TypeBox schemas and an execution function. ### Action Configuration Table | Property | Type | Default | Description | | :------------ | :------------------------------------------------ | :------------------------------- | :-------------------------------------------------------------------------------------------- | | `name` | `string` | _(Required)_ | Unique name within the service (e.g. `"createOrder"`). | | `description` | `string` | `undefined` | Human-readable description exported to the OpenAPI 3.1 operation. | | `input` | `TSchema` | `undefined` | TypeBox schema for validating input payload or query parameters. | | `output` | `TSchema` | `undefined` | TypeBox schema for validating return data. Mismatches throw `ValidationError` (status 500). | | `method` | `"GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE"` | `"POST"` | HTTP method exposed on the API gateway. | | `path` | `string` | `${prefix}/${service}/${action}` | Full HTTP route, including the prefix (it isn't prefixed for you). Can include `/:id`. | | `authorize` | `(params) => boolean \| Promise` | `undefined` | Guard run after input validation. Returning `false` rejects with `UNAUTHORIZED` (status 403). | | `errors` | `Record` | `undefined` | Custom error catalog added to `error` helpers. `status` defaults to `400`. | | `tags` | `string[]` | `[service name]` | OpenAPI tags for the operation; also included in introspection. | | `emits` | `string[]` | `undefined` | Metadata included in introspection. | | `execute` | `({ input, ctx, error }) => Promise \| T` | _(Required)_ | Implementation function. Receives validated input, resolved context, and error helpers. | ### Monomorphic Execution (In-Process vs HTTP) An action can be called in-process without network overhead or data serialization: ```ts title="src/example-call.ts" import { getUser } from "./services/users"; // Direct in-process execution with full validation and authorization: const user = await getUser({ input: { id: "usr_1" } }); console.log(user.name); // "Ada Lovelace" // Alternative shorthand: const result = await getUser.run({ id: "usr_1" }); ``` When the app is served via `brick()`, the gateway mounts the action as an HTTP endpoint: ```bash curl http://localhost:4000/api/users/usr_1 ``` Both invocations run the same steps in the same order: input validation, `authorize`, `execute`, then output validation. ## Path Parameters and Custom Routes When an action declares `path` with parameters (e.g., `/api/users/:id`), Brick merges path parameters into `input`. Declare them in the `input` schema so they're validated: ```ts title="src/services/users.ts" export const deleteUser = usersService.action({ name: "deleteUser", method: "DELETE", path: "/api/users/:id", input: t.Object({ id: t.String(), }), output: t.Object({ success: t.Boolean(), }), execute: async ({ input, error }) => { return { success: true }; }, }); ``` When called via HTTP `DELETE /api/users/usr_1`, Brick extracts `id` from the path and validates it against `input`. When called in-process, pass `{ id: "usr_1" }` in the `input` object. --- # Execution Context & Services Resolve request context, inject databases and users, and isolate in-process service calls. Source: /docs/context Execution context provides request metadata, database handles, logging, and user authentication state to actions and lifecycle hooks. ## Minimal Example Configure a service context resolver to parse authentication headers and inject user identity into actions: ```ts title="src/services/billing.ts" import { defineService, t } from "@brickkit/core"; // Define custom session shape interface BillingUser { id: string; plan: "free" | "pro"; } export const billingService = defineService("billing", { context: async (baseCtx): Promise<{ user: BillingUser | null }> => { const auth = baseCtx.request.headers.get("authorization"); if (!auth) return { user: null }; // Example token validation const user = auth === "Bearer pro-token" ? { id: "usr_42", plan: "pro" as const } : { id: "usr_10", plan: "free" as const }; return { user }; }, }); export const getSubscription = billingService.action({ name: "getSubscription", output: t.Object({ plan: t.String() }), execute: ({ ctx, error }) => { // Error helpers throw; `return` lets TypeScript narrow `ctx.user` if (!ctx.user) return error.UNAUTHORIZED("Sign in required"); return { plan: ctx.user.plan }; }, }); ``` ## `BaseContext` Properties Every action invocation and service resolver receives a `BaseContext` containing HTTP and runtime telemetry primitives: | Field | Type | Description | | :---------- | :------------------------ | :------------------------------------------------------------------------------------------------------- | | `request` | `Request` | Web-standard `Request` for the active HTTP call, or a placeholder `http://localhost` request in-process. | | `db` | `TDb` | Database instance from the owning service's handle. | | `logger` | `Logger` | Logger with `.info()`, `.warn()`, `.error()`, and `.debug()`. | | `traceId` | `string` | Trace ID. Over HTTP, taken from the `x-trace-id` header when present (e.g., `tr_k2l9p0`). | | `requestId` | `string` | Request ID. Over HTTP, taken from the `x-request-id` header when present (e.g., `req_k2l9p0`). | | `services` | `Record` | Reserved. Brick passes an empty object; use `createServiceProxy` to call other services. | | `metadata` | `Record` | Over HTTP, `{ headers }` with the request headers; an empty object in-process. | Over HTTP, Brick echoes `traceId` and `requestId` back as `x-trace-id` and `x-request-id` response headers. ## Service Context Resolvers Brick passes `BaseContext` to the service's `context` resolver on every HTTP request and every in-process call: ```ts title="src/services/projects.ts" export const projectsService = defineService("projects", { context: async (baseCtx) => { const tenantId = baseCtx.request.headers.get("x-tenant-id") ?? "default"; baseCtx.logger.info(`Resolving context for tenant: ${tenantId}`); return { tenantId, resolvedAt: Date.now(), }; }, }); ``` The returned object is merged with `BaseContext` to form `ActionContext`. Inside `execute({ ctx })`, TypeScript infers both `BaseContext` properties (like `ctx.db` and `ctx.logger`) and the resolved properties (like `ctx.tenantId`). ## Context Isolation Across In-Process Service Calls When calling another service in-process using `createServiceProxy`, Brick enforces strict context isolation: ```ts title="src/services/notifications.ts" import { createServiceProxy, defineService } from "@brickkit/core"; export const notificationsService = defineService("notifications"); // The type parameter is the shape you call; Brick doesn't infer it type UsersApi = { getUser: (input: { id: string; }) => Promise<{ id: string; name: string; email: string }>; }; // Proxy to the "users" service, looked up by name in the global registry. // `from` identifies the calling service, so `users` actions see `ctx.caller === "notifications"`. const users = createServiceProxy("users", { from: "notifications" }); export const sendWelcome = notificationsService.action({ name: "sendWelcome", execute: async ({ ctx }) => { // Proxy methods take (input, callCtx?) and run the action in-process const user = await users.getUser({ id: "usr_1" }); ctx.logger.info(`Sending welcome email to ${user.email}`); return { sent: true }; }, }); ``` Each proxy method takes the action's input as its first argument and an optional call context as its second. Only in-process (monolith) dispatch is implemented; `createServiceProxy(name, { isMonolith: false })` throws when called. ### Security Boundaries What the receiving service sees: 1. **Own context and database**: The receiving service runs its own `context` resolver and uses its own database. 2. **Framework fields are dropped**: `db`, `request`, `logger`, `services`, and `metadata` in the call context are discarded before reaching the receiving service. 3. **Envelope fields pass through**: Only `caller`, `user`, `traceId`, `requestId`, and `tenantId` are forwarded. The resolver receives these fields and its returned values always take precedence. Caller custom fields such as `session` and `isOwner` are discarded for direct action calls and proxy calls. Treat `tenantId` as a claim to validate, not proof of access. 4. **`ctx.caller`**: The proxy sets `ctx.caller` to `from` — the name you passed when creating the proxy, `createServiceProxy("users", { from: "notifications" })` — never the target service's name. When `from` is provided, any `caller` in the proxy's `options.context` or the per-call context is ignored, so it cannot be spoofed. Without `from`, an explicitly supplied `caller` still passes through; otherwise `caller` is left unset. ### Internal calls without cookies A proxy call receives a placeholder request without browser cookies. A resolver that supports internal callers should validate `envelope.user` and derive its own session and helpers. For example, accept a user ID only after checking it with your identity store and caller trust policy: ```ts const accounts = defineService("accounts", { context: async (envelope) => { const claim = envelope.user; const userId = typeof claim === "object" && claim !== null && "id" in claim && typeof claim.id === "string" ? claim.id : undefined; // Application functions validate the caller and load an allowed identity. const user = userId && isTrustedCaller(envelope.caller) ? await loadAllowedUser(userId, envelope.tenantId) : null; return { user, session: null, isOwner: (ownerId: string) => user?.id === ownerId, }; }, }); ``` For HTTP calls, continue resolving identity from the actual request's authentication headers or cookies. Passing `{ user }` directly cannot replace a resolver's returned `user`, even when the resolver returns `null`. Tests should provide a request or configure a resolver that explicitly validates the internal envelope. --- # Resources and CRUD Expose database tables via CRUD resources with field-level access, filtering, and security policies. Source: /docs/resources Resources link Drizzle database tables to services, while the `@brickkit/crud` plugin provides type-safe, field-level data operations. ## Minimal Example Attach a declarative CRUD resource to a table with field policies and public access: ```ts title="src/services/pages.ts" import { defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; export const pages = sqliteTable("pages", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), content: text("content"), internalNotes: text("internal_notes"), }); const database = defineDatabase({ tables: { pages } }); export const pagesService = defineService("pages", { database }); // Register resource and attach the crud() plugin export const page = pagesService .resource({ name: "page", table: pages, id: pages.id }) .use( crud({ access: "public", fields: { id: { read: true, filter: ["eq", "in"] }, title: { read: true, create: true, update: true, filter: ["eq", "contains"], sort: true, }, content: { read: true, create: true, update: true }, // internalNotes is omitted: hidden from reads, writes, and filters }, }), ); ``` ### Schema Brick does not generate owner or timestamp columns. Declare them in your table and populate them with Drizzle defaults or CRUD hooks when needed. These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `pages` | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `content` | `text` | Page content. Optional | | `internal_notes` | `text` | Internal notes, hidden by the CRUD field policy. Optional | > **CRUD is Deny-by-Default**: `access` is a required option. `access: "public"` allows every call. `access: { authorize, scope }` calls your `authorize` on every operation and adds your `scope` condition to queries. An access object without `authorize` rejects every call with `FORBIDDEN` (HTTP 403). ## Public Action Names When `crud()` installs onto `pagesService.resource({ name: "page", ... })`, actions are registered on the service under dot-separated names prefixed by the resource name. Routes are mounted under `${prefix}/${resource}`, not under the service name: | Action | Route | Input | | :------------- | :------------------------ | :-------------------------------------------------------------- | | `page.list` | `POST /api/page/query` | `{ select?, where?, orderBy?, limit?, offset?, includeTotal? }` | | `page.get` | `GET /api/page/:id` | `{ id, select? }` | | `page.findOne` | `POST /api/page/find-one` | Same as `list`; returns the first row or `null` | | `page.create` | `POST /api/page` | Fields with `create: true` | | `page.update` | `PATCH /api/page/:id` | `{ id, data }` with fields that have `update: true` | | `page.delete` | `DELETE /api/page/:id` | `{ id }`; returns `{ success: true }` | | `page.count` | `POST /api/page/count` | `{ where? }`; returns `{ count }` | | `page.exists` | `POST /api/page/exists` | `{ where? }`; returns `{ exists }` | The action names appear in architecture introspection and client contracts; OpenAPI summaries use `service.action` (for example, `pages.page.list`). `.use(crud(...))` also returns the resource with these actions attached, so you can call `page.list({ input: {} })` in-process. Use the `routes` option to change a route's `path` or `scope`, or set a route to `false` to keep the action but not mount it over HTTP. ## Field Policies Each column requires explicit permission toggles in the `fields` object: ```ts title="src/services/users.ts" crud({ access: "public", fields: { id: { read: true, filter: ["eq", "in"] }, email: { read: true, create: true, filter: ["eq"], scrub: ({ value, ctx }) => { // Redact email for unauthorized callers return ctx.user?.role === "admin" ? value : "[REDACTED]"; }, }, passwordHash: { create: true, // Write-only column read: false, }, }, }); ``` ### Field Policy Configuration Table | Option | Type | Description | | :------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `read` | `boolean` | Allows column to be returned in `list`, `findOne`, `get`, `create`, and `update` responses, and selected via `select`. | | `create` | `boolean` | Accepts column in `create` input payload. | | `update` | `boolean` | Accepts column in `update` input payload. | | `filter` | `FilterOperator[]` | Enables filtering on this column. Allowed operators: `eq`, `ne`, `in`, `notIn`, `gt`, `gte`, `lt`, `lte`, `between`, `contains`, `startsWith`, `endsWith`, `isNull`, `isNotNull`. `contains`/`startsWith`/`endsWith` require a string column. | | `sort` | `boolean` | Enables ascending/descending ordering on this column. | | `schema` | `TSchema` | Custom TypeBox schema to override automatic Drizzle column schema inference. | | `scrub` | `({ value, ctx }) => unknown` | Transforms a readable field's value before it's returned. Scrubbed fields are typed `unknown` in the output schema. | Every key in `fields` must be a column of the table, or `crud()` throws at setup. The primary key can't have `update: true`. ## Operations Selection and Limits You can turn off individual operations (or all of them with `operations: false`) and set pagination limits. `defaultLimit` defaults to `20` and `maxLimit` to `100`; a `limit` above `maxLimit` fails validation: ```ts title="src/services/readonly.ts" crud({ access: "public", operations: { delete: false, // Disables page.delete endpoint entirely update: false, // Disables page.update endpoint entirely }, defaultLimit: 25, // Default page size for list queries maxLimit: 100, // Maximum allowed limit fields: { id: { read: true }, title: { read: true }, }, }); ``` ## Performance & Prepared Statement Caching When the database client supports `prepare()` (Bun SQLite), CRUD reuses prepared statements: - Statements are cached per query shape (operation, filter structure, selected fields, ordering), up to 128 per database, with filter values bound as placeholders. - Caching is skipped when a call has a non-empty `scope` condition, and for any table with a column that uses `$defaultFn` or `$onUpdateFn` (such as the `$defaultFn` UUID in the example above). - For resources with no `scope` function and no such columns, `brick()` prepares the default `list` and `get` statements when it builds the app. --- # Access Control and Policies Secure actions and CRUD operations with role-based authorization and row scoping. Source: /docs/access-control Brick-TS provides two complementary layers of access control: operation-level authorization guards on actions and row-level scoping on CRUD resources. ## Action Authorization (`authorize`) Actions support an `authorize` guard that runs after input validation and before `execute`. It receives `{ user, input, ctx }`, where `user` is `ctx.user`: ```ts title="src/services/articles.ts" import { defineService, t } from "@brickkit/core"; interface User { id: string; role: "reader" | "editor" | "admin"; } export const articlesService = defineService("articles", { context: async (baseCtx): Promise<{ user: User | null }> => { // Resolve the user from baseCtx.request here return { user: null }; }, }); export const publishArticle = articlesService.action({ name: "publishArticle", input: t.Object({ articleId: t.String() }), output: t.Object({ published: t.Boolean() }), authorize: ({ user }) => { // Return true to allow execution, false to reject return user?.role === "editor" || user?.role === "admin"; }, execute: ({ ctx }) => { // ctx.user is typed User here, not User | null ctx.logger.info(`Article published by ${ctx.user.id}`); return { published: true }; }, }); ``` ### Automatic Type Narrowing When an action declares an `authorize` guard, Brick's types use `NarrowAuthorizedContext`: inside `execute({ ctx })`, `ctx.user` is narrowed from `User | null` to `User`. This is a type-level assumption. Brick doesn't check `ctx.user` at runtime, so your `authorize` must return `false` when there's no user. Narrowing only applies when your context resolver returns a typed `user`. ## CRUD Access Policies CRUD resources from `@brickkit/crud` are **deny-by-default**. `access` is a required option, and an access object without `authorize` rejects every operation with `FORBIDDEN` (403). CRUD `authorize` runs after input validation and receives `{ operation, input, ctx }`. ### 1. Public Access Allow unrestricted access across all CRUD operations: ```ts title="src/services/catalog.ts" crud({ access: "public", fields: { id: { read: true }, title: { read: true }, }, }); ``` ### 2. Operation Authorization (`authorize`) Verify caller identity and role for specific CRUD operations: ```ts title="src/services/posts.ts" crud({ access: { authorize: ({ operation, input, ctx }) => { // Anyone can read posts if ( operation === "list" || operation === "get" || operation === "findOne" ) { return true; } // Only authenticated users can write if (!ctx.user) return false; // Only admins can delete posts if (operation === "delete") { return ctx.user.role === "admin"; } return true; }, }, fields: { id: { read: true }, title: { read: true, create: true }, }, }); ``` `operation` discriminates the authorization input type. For example, after `operation === "list"`, `input.where`, `input.select`, and `input.limit` are available; after `operation === "update"`, `input.id` and `input.data` are available. Field policies constrain field names and filter operators, and explicit field schemas supply value types. Column-derived values and IDs remain `unknown` because the table is supplied after `crud()` is configured. Create fields are optional in this callback type because column defaults and nullability are not yet known. The package exports `CrudAuthorizeArgs` and `CrudInputs` for reusable policies and per-operation input types. ### 3. Row-Level Tenant and Owner Scoping (`scope`) Return a trusted SQL condition to isolate multi-tenant or user-owned rows. `scope` only runs when `authorize` allowed the call, and receives `{ operation, ctx }`: ```ts title="src/services/documents.ts" import { eq } from "@brickkit/core"; import { documentsTable } from "./schema"; crud({ access: { authorize: ({ ctx }) => !!ctx.user, scope: ({ operation, ctx }) => { // Admins bypass row-level isolation if (ctx.user?.role === "admin") return undefined; // A scope can't constrain an INSERT; set ownerId in a beforeCreate hook if (operation === "create") return undefined; // Regular users only see and mutate their own documents return eq(documentsTable.ownerId, ctx.user.id); }, }, fields: { id: { read: true }, title: { read: true, create: true, update: true }, ownerId: { read: true }, }, }); ``` When `scope` returns a Drizzle SQL condition, Brick combines it with the client's `where` filter and, for `get`, `update`, and `delete`, the ID match, using `and(...)`. A user can't read, update, or delete another tenant's rows even if they know the record ID; those calls fail with `NOT_FOUND` (404). If `scope` returns a condition for `create`, the call is rejected with `FORBIDDEN` (403): row-scoped creation needs an explicit create policy. Return `undefined` for `create` and assign the owner or tenant column in a [`beforeCreate` hook](/docs/hooks). ## Status Codes and Error Codes Today Understanding current runtime behavior: | Mechanism | Code | Status | Location in Code | | :------------------------------------------ | :------------- | :-------------- | :---------------------------- | | Action `authorize` rejection | `UNAUTHORIZED` | `403 Forbidden` | `packages/core/src/action.ts` | | CRUD `authorize` rejection or missing | `FORBIDDEN` | `403 Forbidden` | `packages/crud/src/index.ts` | | CRUD `scope` returned a condition on create | `FORBIDDEN` | `403 Forbidden` | `packages/crud/src/index.ts` | | `error.UNAUTHORIZED()` in your own code | `UNAUTHORIZED` | `401` | `packages/core/src/action.ts` | > **Planned Status Code Alignment**: Currently, failed action authorization > throws code `UNAUTHORIZED` with HTTP status 403. A planned update tracked in > [GitHub Issue #53](https://github.com/Chandraprakash-Darji/brick/issues/53) > will align unauthenticated requests to HTTP 401 and forbidden role/scope > rejections to HTTP 403. ### Schema The owner-scoping example expects these columns on the application-owned `documentsTable`. Brick does not generate `owner_id`; populate it in a `beforeCreate` hook from the authenticated context. #### Table Name: `documents` | Field | Type | Description | | ---------- | ------ | ------------------------------------------ | | `id` | `text` | Document identifier. PK | | `title` | `text` | Document title. | | `owner_id` | `text` | Owner identifier used by the scope policy. | --- # Validation and Errors Validate inputs and outputs with TypeBox schemas and throw structured errors with HTTP statuses. Source: /docs/validation-and-errors Brick-TS enforces strict runtime schema validation using TypeBox and provides structured error helpers for consistent error envelopes. ## Minimal Example Define an action with TypeBox schemas and use the injected `error` builder to return typed HTTP errors: ```ts title="src/services/checkout.ts" import { defineService, t } from "@brickkit/core"; export const checkoutService = defineService("checkout"); export const processPayment = checkoutService.action({ name: "processPayment", input: t.Object({ orderId: t.String({ minLength: 1 }), amount: t.Number({ minimum: 1 }), }), output: t.Object({ transactionId: t.String(), status: t.String(), }), // Declare custom action-specific error codes errors: { INSUFFICIENT_FUNDS: { status: 400, message: "Account balance is too low" }, CARD_EXPIRED: { status: 400, message: "Payment method has expired" }, }, execute: async ({ input, error }) => { if (input.amount > 10000) { error.INSUFFICIENT_FUNDS("Amount exceeds single-transaction limit", { limit: 10000, requested: input.amount, }); } return { transactionId: `tx_${crypto.randomUUID()}`, status: "succeeded", }; }, }); ``` ## Schema Validation with TypeBox TypeBox provides zero-dependency JSON Schema builders that validate at runtime and infer static TypeScript types simultaneously: ```ts title="src/schemas/user.ts" import { type Static, t } from "@brickkit/core"; export const CreateUserSchema = t.Object({ name: t.String({ minLength: 2, maxLength: 100 }), email: t.String({ pattern: "^[^@\\s]+@[^@\\s]+$" }), role: t.Union([t.Literal("admin"), t.Literal("member")]), tags: t.Optional(t.Array(t.String())), }); // Infer static TypeScript type without codegen: export type CreateUserInput = Static; ``` > TypeBox only validates string `format`s that are registered in its `FormatRegistry`. Brick-TS core registers none, so `t.String({ format: "email" })` rejects every value with `Unknown format 'email'`. Use `pattern`, or register the format yourself with `FormatRegistry.Set()` from `@sinclair/typebox`. (`@brickkit/crud` registers `date-time` only.) ### Input Validation Input validation runs before `execute()`. If an incoming payload violates the schema, Brick aborts execution and responds with `ValidationError` (HTTP status 400): ```json { "name": "ValidationError", "status": 400, "message": "Validation failed for action 'processPayment' input", "errors": [ { "path": "/amount", "message": "Expected number to be greater or equal to 1", "value": 0 } ] } ``` ### Output Validation Output validation runs after `execute()` resolves. If an action returns data that does not conform to its declared `output` schema, Brick throws `ValidationError` with HTTP status 500 and the message `Validation failed for action '' output`. ### Unexpected Errors Any other error thrown from an action is returned as HTTP 500 with `{ "name": "InternalServerError", "message": , "status": 500 }`. ## Standard Error Helpers The `error` helper is passed to `execute({ input, ctx, error })` and to every CRUD lifecycle hook (`beforeCreate`, `afterCreate`, `beforeUpdate`, `afterUpdate`, `beforeDelete`, `afterDelete`). Each helper takes an optional message override and optional `details`, and throws an `ActionExecutionError`: | Helper | HTTP Status | Default Error Code | Default Message | | :-------------------------------------------- | :---------- | :---------------------- | :------------------------ | | `error.BAD_REQUEST(msg?, details?)` | `400` | `BAD_REQUEST` | `"Bad Request"` | | `error.UNAUTHORIZED(msg?, details?)` | `401` | `UNAUTHORIZED` | `"Unauthorized"` | | `error.FORBIDDEN(msg?, details?)` | `403` | `FORBIDDEN` | `"Forbidden"` | | `error.NOT_FOUND(msg?, details?)` | `404` | `NOT_FOUND` | `"Not Found"` | | `error.CONFLICT(msg?, details?)` | `409` | `CONFLICT` | `"Conflict"` | | `error.INTERNAL_SERVER_ERROR(msg?, details?)` | `500` | `INTERNAL_SERVER_ERROR` | `"Internal Server Error"` | When an action's `authorize()` returns `false`, Brick throws `ActionExecutionError` with code `"UNAUTHORIZED"` and status `403` (not 401). CRUD access denial uses code `"FORBIDDEN"` with status `403`. ### Error Envelope Format All `ActionExecutionError` instances format automatically into consistent JSON responses: ```json { "name": "ActionExecutionError", "code": "NOT_FOUND", "status": 404, "message": "User with ID 'usr_99' not found", "details": { "entity": "user", "id": "usr_99" } } ``` ## Custom Declared Action Errors Actions can declare custom domain errors in their `errors` configuration map: ```ts title="src/services/auth.ts" export const resetPassword = authService.action({ name: "resetPassword", input: t.Object({ token: t.String(), newPassword: t.String() }), output: t.Object({ success: t.Boolean() }), errors: { TOKEN_EXPIRED: { status: 400, message: "Password reset token has expired" }, WEAK_PASSWORD: { status: 422, message: "Password does not meet complexity requirements", }, }, execute: ({ input, error }) => { if (input.newPassword.length < 8) { error.WEAK_PASSWORD("Password must be at least 8 characters long"); } return { success: true }; }, }); ``` If you omit `status` on a declared error, it defaults to `400`. A declared error with the same name as a standard helper (for example `NOT_FOUND`) overrides it. Custom error keys are typed onto the `error` argument, ensuring compile-time safety and autocompletion for error codes. --- # Lifecycle Hooks Intercept CRUD operations before and after writes to validate, transform, and log data. Source: /docs/hooks Lifecycle hooks allow you to intercept CRUD operations before and after database mutations to normalize payloads, validate business logic, or trigger side effects. ## Minimal Example Configure hooks on a CRUD resource to normalize input before saving and log after saving. `slug` has no `create` permission, so clients can't send it; the hook always derives it: ```ts title="src/services/articles.ts" import { defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; export const articles = sqliteTable("articles", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), slug: text("slug").notNull(), }); const database = defineDatabase({ tables: { articles } }); export const articlesService = defineService("articles", { database }); export const article = articlesService .resource({ name: "article", table: articles, id: articles.id }) .use( crud({ access: "public", fields: { id: { read: true }, title: { read: true, create: true, update: true }, slug: { read: true }, }, hooks: { beforeCreate: async ({ data, error }) => { // Normalize input data in place const title = String(data.title ?? "").trim(); if (!title) { return error.BAD_REQUEST("Article title cannot be blank"); } data.title = title; // Hooks may set columns clients can't write data.slug = title.toLowerCase().replace(/\s+/g, "-"); }, afterCreate: async ({ row, ctx }) => { ctx.logger.info(`Article created: ${row.id} (${row.title})`); }, }, }), ); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `articles` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `slug` | `text` | URL slug. | ## Available CRUD Hooks All hooks are declared inside the `hooks` configuration of `crud()`: Hooks run after input validation, `authorize`, and `scope`. | Hook | Arguments | Timing | Common Use Cases | | :------------- | :------------------------- | :----------------------- | :------------------------------------------------------------- | | `beforeCreate` | `{ data, ctx, error }` | Before `INSERT` query | Data trimming, hashing, slug generation, permission validation | | `afterCreate` | `{ row, ctx, error }` | After `INSERT` completes | Audit logging, notification dispatch, webhooks | | `beforeUpdate` | `{ id, data, ctx, error }` | Before `UPDATE` query | Field mutation validation, updated timestamp stamping | | `afterUpdate` | `{ id, row, ctx, error }` | After `UPDATE` completes | Cache invalidation, audit logging | | `beforeDelete` | `{ id, ctx, error }` | Before `DELETE` query | Cascade checks, preventing deletion of active resources | | `afterDelete` | `{ id, ctx, error }` | After `DELETE` completes | Cleanup associated files/assets, search index purging | `row` in `afterCreate` and `afterUpdate` is the full database row, including columns without `read` permission. The response is still limited to readable fields. ## Mutating Input in `before*` Hooks Before hooks receive `data`, a copy of the validated write payload (the `create` input, or `update`'s `data` object). You can mutate fields or add values for columns clients can't write. Every key must be a column of the table, or the call fails with a 500. An update whose `data` is empty after `beforeUpdate` is rejected with `BAD_REQUEST`. ```ts title="src/services/profiles.ts" hooks: { beforeCreate: async ({ data, ctx }) => { // Inject tenant and author from authenticated context data.tenantId = ctx.tenantId; data.createdBy = ctx.user?.id; }, beforeUpdate: async ({ data }) => { if (typeof data.displayName === "string") { data.displayName = data.displayName.trim(); } }, } ``` ## Aborting Writes with Error Helpers Hooks receive the standard `error` builder (`BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `INTERNAL_SERVER_ERROR`). Calling a helper throws an `ActionExecutionError` with that code and status. CRUD doesn't wrap hooks in a transaction. Throwing in a `before*` hook stops the write before it runs. Throwing in an `after*` hook returns an error to the client, but the row has already been written and isn't rolled back. ```ts title="src/services/comments.ts" hooks: { beforeCreate: async ({ data, ctx, error }) => { if (!ctx.user) { return error.UNAUTHORIZED("Sign in to comment"); } const trimmed = String(data.body ?? "").trim(); if (trimmed.length < 3) { error.BAD_REQUEST("Comment must be at least 3 characters long"); } }, } ``` --- # Primary Keys and Identifiers Configure custom primary keys, UUIDs, and auto-minted column IDs. Source: /docs/primary-keys Brick-TS uses whatever primary key your Drizzle table defines: autoincrement integers, UUIDs, or custom prefixed string identifiers minted with `$defaultFn()`. ## Minimal Example Define an auto-minted UUID primary key and register it with the resource: ```ts title="src/services/items.ts" import { defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; export const items = sqliteTable("items", { // Auto-minted UUID primary key id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: text("name").notNull(), }); const database = defineDatabase({ tables: { items } }); export const itemsService = defineService("items", { database }); // Register resource identity using items.id export const itemResource = itemsService .resource({ name: "item", table: items, id: items.id, // Optional here: items has a single primary key }) .use( crud({ access: "public", fields: { id: { read: true, filter: ["eq", "in"] }, name: { read: true, create: true, update: true }, }, }), ); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `items` | Field | Type | Description | | ------ | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `name` | `text` | Display name. | #### Table Name: `documents` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | #### Table Name: `users` | Field | Type | Description | | ------ | ------ | ------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: mintId("usr") (application) | | `name` | `text` | Display name. | #### Table Name: `logs` | Field | Type | Description | | --------- | --------- | ----------------------------------------- | | `id` | `integer` | Row identifier. PK Default: autoincrement | | `message` | `text` | Log message. | ## Supported Primary Key Strategies Drizzle column builders define primary key generation strategies: ### 1. Auto-Minted UUIDs Generates random RFC 4122 UUID strings on row creation: ```ts title="src/schema.ts" export const documents = sqliteTable("documents", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), }); ``` ### 2. Prefixed Identifiers (Stripe-Style) Prefix IDs with entity tags (e.g. `usr_...`, `inv_...`) to improve readability and debugging: ```ts title="src/schema.ts" function mintId(prefix: string): string { return `${prefix}_${crypto.randomUUID().replace(/-/g, "").slice(0, 16)}`; } export const users = sqliteTable("users", { id: text("id") .primaryKey() .$defaultFn(() => mintId("usr")), name: text("name").notNull(), }); ``` ### 3. Autoincrement Integers Standard sequential integer identifiers: ```ts title="src/schema.ts" import { integer, sqliteTable, text } from "@brickkit/core"; export const logs = sqliteTable("logs", { id: integer("id").primaryKey({ autoIncrement: true }), message: text("message").notNull(), }); ``` When using integer keys, Brick automatically types action parameters such as `id` in `item.get` and `item.update` as `number`. ## Resource Identity Registration `service.resource()` takes an optional `id` column. If you omit it, Brick uses the table's primary key when the table has exactly one primary-key column. Pass `id` explicitly when the table has no single primary key (for example a composite key) or when you want lookups to use a different unique column; the column must belong to the resource's table, otherwise `resource()` throws `Resource '' requires an id column belonging to its table`. ```ts title="src/services/catalog.ts" const product = service .resource({ name: "product", table: productsTable, id: productsTable.id, // Column CRUD uses for get/update/delete lookups }) .use(crud({ access: "public", fields: {/* ... */} })); ``` The CRUD plugin uses this column for single-record operations, and types `id` from the column's data type: - `product.get({ input: { id: "prod_1" } })` - `product.update({ input: { id: "prod_1", data: { name: "New Name" } } })` - `product.delete({ input: { id: "prod_1" } })` --- # Relational Data Modeling & Joins Model one-to-many, many-to-many, and singleton relationships with explicit Drizzle queries. Source: /docs/relations Brick-TS leverages standard relational database patterns and Drizzle ORM to model relations without proprietary abstractions or hidden query overhead. ## Minimal Example: One-to-Many Relationships Model parent entities and related records using standard foreign keys: ```ts title="src/services/blog.ts" import { defineDatabase, defineService, eq, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; // 1. Parent table: Articles export const articles = sqliteTable("articles", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), }); // 2. Child table: Comments (pointing to article via targetId) export const comments = sqliteTable("comments", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), targetId: text("target_id").notNull(), body: text("body").notNull(), }); const database = defineDatabase({ tables: { articles, comments } }); export const blogService = defineService("blog", { database }); // 3. Expose articles and comments as explicit CRUD resources export const articleResource = blogService .resource({ name: "article", table: articles, id: articles.id }) .use( crud({ access: "public", fields: { id: { read: true }, title: { read: true, create: true }, }, }), ); export const commentResource = blogService .resource({ name: "comment", table: comments, id: comments.id }) .use( crud({ access: "public", fields: { id: { read: true, filter: ["eq"] }, targetId: { read: true, create: true, filter: ["eq"] }, body: { read: true, create: true, filter: ["contains"] }, }, }), ); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `articles` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | #### Table Name: `comments` | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `target_id` | `text` | Logical parent article identifier; no SQL foreign-key constraint. | | `body` | `text` | Text content. | #### Table Name: `tags` | Field | Type | Description | | ------ | ------ | ------------------ | | `id` | `text` | Row identifier. PK | | `name` | `text` | Display name. | #### Table Name: `article_tags` | Field | Type | Description | | ------------ | ------ | --------------------------------------------------------- | | `article_id` | `text` | Article identifier. Part of the composite primary key. PK | | `tag_id` | `text` | Tag identifier. Part of the composite primary key. PK | #### Table Name: `app_settings` | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------------ | | `id` | `text` | Row identifier. PK Default: global_settings (application) | | `singleton_key` | `integer` | Singleton discriminator. Unique Optional Default: 1 | | `site_name` | `text` | Site display name. | | `maintenanceMode` | `boolean` | Whether maintenance mode is enabled. Optional Default: false | To fetch all comments for an article over HTTP, call the `comment.list` action. CRUD routes are scoped to the resource name, and `list` is mounted at `POST {prefix}/{resource}/query`: ```bash curl -X POST http://localhost:4000/api/comment/query \ -H "Content-Type: application/json" \ -d '{"where": {"field": "targetId", "op": "eq", "value": "ARTICLE_UUID"}}' ``` ## Relational Joins in Custom Actions When an endpoint needs composite parent-and-child data in a single round-trip, write an action using Drizzle joins: ```ts title="src/services/articles-with-comments.ts" import { eq, t } from "@brickkit/core"; import { articles, comments } from "./blog"; import { blogService } from "./blog"; export const getArticleDetails = blogService.action({ name: "getArticleDetails", method: "GET", path: "/api/blog/articles/:id/details", input: t.Object({ id: t.String() }), output: t.Object({ id: t.String(), title: t.String(), comments: t.Array( t.Object({ id: t.String(), body: t.String(), }), ), }), execute: async ({ input, ctx, error }) => { // 1. Fetch parent article const articleRows = await ctx.db .select() .from(articles) .where(eq(articles.id, input.id)) .limit(1); if (!articleRows.length) { error.NOT_FOUND("Article not found"); } // 2. Fetch associated comments const commentRows = await ctx.db .select({ id: comments.id, body: comments.body }) .from(comments) .where(eq(comments.targetId, input.id)); return { id: articleRows[0].id, title: articleRows[0].title, comments: commentRows, }; }, }); ``` ## Many-to-Many Relationships (Join Tables) Model many-to-many connections using a dedicated join table with a composite primary key: ```ts title="src/schema/tags.ts" import { primaryKey, sqliteTable, text } from "@brickkit/core"; import { articles } from "./blog"; export const tags = sqliteTable("tags", { id: text("id").primaryKey(), name: text("name").notNull(), }); // Join table linking articles and tags export const articleTags = sqliteTable( "article_tags", { articleId: text("article_id").notNull(), tagId: text("tag_id").notNull(), }, (t) => [primaryKey({ columns: [t.articleId, t.tagId] })], ); ``` ## Modeling Singleton Records When an application requires exactly one configuration row per tenant or global system, enforce the constraint in the database with a `unique()` column: ```ts title="src/services/settings.ts" import { integer, sqliteTable, text } from "@brickkit/core"; import { crud } from "@brickkit/crud"; import { defineDatabase, defineService } from "@brickkit/core"; export const appSettings = sqliteTable("app_settings", { id: text("id") .primaryKey() .$defaultFn(() => "global_settings"), // Single-row constraint: always 1 with unique index singletonKey: integer("singleton_key").unique().default(1), siteName: text("site_name").notNull(), maintenanceMode: integer({ mode: "boolean" }).default(false), }); const database = defineDatabase({ tables: { appSettings } }); export const settingsService = defineService("settings", { database }); export const settings = settingsService .resource({ name: "settings", table: appSettings, id: appSettings.id }) .use( crud({ access: "public", operations: { delete: false, // Prevent deleting singleton }, fields: { id: { read: true, filter: ["eq"] }, // findOne can only filter on declared operators siteName: { read: true, create: true, update: true }, maintenanceMode: { read: true, update: true }, }, }), ); ``` Retrieve the single record via `findOne`: ```ts const config = await settings.findOne({ input: { where: { field: "id", op: "eq", value: "global_settings" }, }, }); ``` --- # Typed Client SDK Call backend actions through a typed HTTP client with automatically generated browser contracts. Source: /docs/typed-client The Brick-TS client SDK uses a generated browser contract to infer action inputs and outputs and preserve exact HTTP routes. Use [TanStack Query options](/docs/tanstack-query) for query hooks, mutations, and typed cache keys. ## Minimal Example The client is typed by a generated contract: browser-safe route metadata and transport types derived from your app. Your app entry must default-export a `BrickApp` created by `brick()` (without calling `.listen()`). Generate the contract with the Vite plugin, which writes `_brick/contract.ts` on dev startup and build and regenerates it when the app changes: ```ts title="vite.config.ts" import { defineConfig } from "vite"; import { brickClient } from "@brickkit/cli/vite"; export default defineConfig({ plugins: [brickClient({ entry: "./src/api/app.ts" })], }); ``` Or generate it without Vite: ```bash brick gen client src/api/app.ts # default output: _brick/contract.ts brick gen client src/api/app.ts --output frontend/src/contract.ts ``` Then import `createBrickClient` from `@brickkit/core/client` (a browser-safe subpath) and pass the contract: ```ts title="src/lib/api.ts" import { createBrickClient } from "@brickkit/core/client"; import { contract } from "../../_brick/contract"; export const api = createBrickClient({ baseUrl: "http://localhost:4000", contract, }); // CRUD resources live at the root: api.. const response = await api.page.list({ where: { field: "title", op: "contains", value: "Launch" }, limit: 5, }); console.log(response.items); // Typed from the resource's read fields ``` Custom actions are grouped by service name (`api..`), and dotted action names become nested namespaces. The contract carries each action's HTTP method and path, including CRUD routes and custom `path`/`method` overrides. GET and DELETE input is sent as a query string; other methods send JSON. ## Client Configuration (`createBrickClient`) The client constructor accepts the following options: | Option | Type | Default | Description | | :---------- | :-------------------------------------------------------------------------------------------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------ | | `baseUrl` | `string` | _(Required)_ | Server origin (e.g. `"http://localhost:4000"`). Trailing slashes are trimmed. | | `contract` | `Record` | `undefined` | Generated contract. Without it, every call is `POST {prefix}//`, so CRUD and custom-path routes won't resolve. | | `prefix` | `string` | `"/api"` | Route prefix used only for calls without contract metadata. Must match `brick({ prefix })`. | | `headers` | `Record \| (() => Record \| Promise>)` | `undefined` | Static headers, or a function re-evaluated on every call. | | `fetch` | `(input, init) => Promise` | `globalThis.fetch` | Custom fetch implementation for tests, proxies, or `credentials: "include"`. | | `timeoutMs` | `number` | `undefined` | Default per-call timeout in milliseconds (uses `AbortSignal.timeout`). | | `signal` | `AbortSignal` | `undefined` | Default abort signal for every call. | ### Dynamic Authentication Headers Pass an async function to `headers` to attach dynamic authentication tokens to every outgoing request: ```ts title="src/lib/api.ts" export const api = createBrickClient({ baseUrl: "https://api.example.com", contract, headers: async (): Promise> => { const token = await getSessionToken(); return token ? { Authorization: `Bearer ${token}` } : {}; }, }); ``` ## Per-Call Options Every client invocation accepts an optional second argument for request-specific headers, an abort signal, or a timeout. Per-call headers merge over the client defaults: ```ts title="src/components/PageList.tsx" const controller = new AbortController(); const pages = await api.page.list( { limit: 10 }, { signal: controller.signal, headers: { "X-Custom-Trace": "req_123" }, }, ); ``` A per-call `signal` replaces the client's default signal. The timeout remains active alongside that signal, using per-call `timeoutMs` when supplied and client `timeoutMs` otherwise. Signal cancellation rejects with the native `AbortError`. A timeout aborts with `TimeoutError`, which the client reports as `BrickTransportError`. ## Error Handling When a request fails, the client rejects according to the response: - A body with a string `code` (declared errors, standard helpers, authorization failures) → `ActionExecutionError` with `code`, `status`, `message`, and `details`. - A body with `name: "ValidationError"` → `ValidationError` with `errors`. - Any other 5xx → `BrickServerError`; any other non-OK response, a network failure, or an unparsable body → `BrickTransportError`. `@brickkit/core/client` exports the client builder, the transport classes and guards, and the type utilities — but not `ActionExecutionError` or `ValidationError`, which live in the server entry `@brickkit/core`. In browser code, narrow them by `err.name` instead: ```ts title="src/lib/fetch-user.ts" import { type InferActionFailure, isBrickServerError, isBrickTransportError, } from "@brickkit/core/client"; import { api } from "./api"; try { const user = await api.users.getUser({ id: "usr_99" }); } catch (err) { if (err instanceof Error && err.name === "ActionExecutionError") { // Domain error from the action (declared code or e.g. NOT_FOUND, FORBIDDEN) const failure = err as InferActionFailure; console.error(`[${failure.code}] (${failure.status}): ${failure.message}`); console.error("Details:", failure.details); } else if (err instanceof Error && err.name === "ValidationError") { // TypeBox schema validation failure console.error("Validation error:", (err as any).errors); } else if (isBrickServerError(err)) { // 5xx without a Brick error shape console.error("Server failure:", err.status, err.message); } else if (isBrickTransportError(err)) { // Network failure, unparsable body, or unrecognized 4xx console.error("Transport failure:", err.status, err.message); } } ``` ## Type Utilities Extract static types directly from actions or client functions: ```ts title="src/types/client.ts" import type { InferActionErrorCodes, InferActionInput, InferActionOutput, } from "@brickkit/core/client"; import type { getUser } from "../server/users"; export type GetUserInput = InferActionInput; export type GetUserOutput = InferActionOutput; export type GetUserErrorCodes = InferActionErrorCodes; ``` ## Generate the contract automatically For Vite 8 apps, use the `brickClient` plugin shown above. It generates the contract before frontend startup and production compilation. For type checks on a clean checkout, initialize and close a Vite server before running `tsc`, as demonstrated in `examples/pages/scripts/typecheck.ts`. The plugin tracks the app entry and imported dependencies. Schema changes, HTTP method and path edits, and action additions or removals regenerate the contract. Rapid changes coalesce. The app definition graph, including imported service instances, is invalidated before evaluation so removed actions cannot persist in cached service objects. Changed contracts are replaced atomically. Implementation-only edits leave an unchanged contract's content and timestamp intact, so they do not trigger frontend contract updates. The generated output is excluded from regeneration triggers. If an edit breaks generation, Vite reports it in the terminal and browser overlay, preserves the previous contract, and retries after a valid edit. For separate backend and frontend processes, use the CLI's [coordinated development workflow](/docs/brick-cli/dev-and-start#generate-contracts-before-frontend-startup): ```bash brick dev src/api/app.ts --client frontend/src/contract.ts --frontend 'bun run --cwd frontend dev' ``` For a non-Vite frontend, put generation first in build and typecheck scripts: ```json title="package.json" { "scripts": { "build": "brick gen client src/api/app.ts --output frontend/src/contract.ts && bun run --cwd frontend build", "typecheck": "brick gen client src/api/app.ts --output frontend/src/contract.ts && bun run --cwd frontend typecheck" } } ``` All paths use the same contract generator and atomic writer. Generated browser modules contain transport types and route metadata, without backend implementations or database imports. Generation evaluates the app entry, so keep listeners, startup jobs, and other startup side effects in a separate bootstrap. Required configuration must be available during generation. --- # TanStack Query Use typed query options, mutation options, and cache keys with Brick's HTTP client. Source: /docs/tanstack-query Use `createBrickQueryClient` to call generated actions through standard TanStack Query options. It shares the transport and configuration of the [typed HTTP client](/docs/typed-client). ## Create a Query client Install the browser SDK and TanStack Query v5: ```bash bun add @brickkit/core @tanstack/query-core@^5 @tanstack/react-query@^5 ``` Configure [automatic contract generation](/docs/typed-client#generate-the-contract-automatically), then pass the generated contract to the browser entry: ```ts title="src/lib/api.ts" import { createBrickQueryClient } from "@brickkit/core/tanstack-query"; import { contract } from "../../_brick/contract"; export const api = createBrickQueryClient({ baseUrl: "http://localhost:4000", contract, fetch: (input, init) => fetch(input, { ...init, credentials: "include" }), }); ``` The examples below assume your app exposes a CRUD resource named `page`. Custom actions use `api..`, with dotted action names mapped to nested namespaces. The generated contract supplies input and output types and exact HTTP routes, including plugin actions. Wrap React consumers in TanStack Query's `QueryClientProvider`. The Brick Query client builds request options; your TanStack `QueryClient` owns the cache. ## Load data with query options Pass action input under `input` and spread native TanStack options alongside the generated options: ```tsx title="src/hooks/use-pages.ts" import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { api } from "../lib/api"; export function usePages(page: number) { return useQuery({ ...api.page.list.queryOptions({ input: { offset: page * 20, limit: 20 }, }), placeholderData: keepPreviousData, staleTime: 30_000, select: (response) => response.items, }); } ``` `queryOptions` returns `queryKey` and `queryFn`. Required action input remains required. For an action whose input includes `undefined`, you can call `.queryOptions()` without arguments. Both query and mutation helpers are available on every action. Choose a helper by how you use the action. Calling `queryOptions` for a POST action sends POST. Brick preserves GET, POST, PUT, PATCH, DELETE, custom paths, and URL-encoded path parameters. It uses its existing HTTP protocol. Direct calls such as `await api.page.list({ limit: 20 })` also work. ## Mutate and invalidate related queries Pass the action input to `mutate` or `mutateAsync`. Use an action-wide key to invalidate every cached input for a related action: ```tsx title="src/hooks/use-create-page.ts" import { useMutation, useQueryClient } from "@tanstack/react-query"; import { api } from "../lib/api"; export function useCreatePage() { const queryClient = useQueryClient(); return useMutation({ ...api.page.create.mutationOptions(), onSuccess: () => queryClient.invalidateQueries({ queryKey: api.page.list.queryKey() }), }); } ``` `mutationOptions` returns `mutationKey` and `mutationFn`. Add native callbacks such as `onSuccess` and `onError` beside those properties. Brick does not infer which queries a mutation affects. To target one input, include it in the key: ```ts await queryClient.invalidateQueries({ queryKey: api.page.get.queryKey({ input: { id: pageId } }), exact: true, }); ``` Keys contain the server URL, prefix, action namespace path, contract method and route identity, and input. TanStack Query hashes object inputs deterministically. Different actions and inputs have separate cache entries. Headers are excluded, so clear or partition the cache when the authenticated user changes. ## Use QueryClient methods and Router loaders The same options work outside React hooks: ```ts const options = api.page.get.queryOptions({ input: { id: pageId } }); const page = await queryClient.ensureQueryData(options); await queryClient.prefetchQuery(options); const cached = queryClient.getQueryData(options.queryKey); ``` `page` and `cached` infer the action output. Input-specific `queryKey({ input })` calls also carry the cached output type. A bare `queryKey()` is an action-wide filter for invalidation. For a TanStack Router route, use the `queryClient` supplied through your router context: ```ts title="src/routes/pages/$id.tsx" import { createFileRoute } from "@tanstack/react-router"; import { api } from "../../lib/api"; export const Route = createFileRoute("/pages/$id")({ loader: ({ context, params }) => context.queryClient.ensureQueryData( api.page.get.queryOptions({ input: { id: params.id } }), ), }); ``` ## Cancel requests and set request options TanStack Query's cancellation signal reaches fetch. You can also supply per-request headers, an abort signal, and a timeout: ```ts const controller = new AbortController(); const options = api.page.list.queryOptions({ input: { limit: 20 }, request: { signal: controller.signal, timeoutMs: 5_000, headers: { "X-Request-ID": "page-list" }, }, }); const pending = queryClient.prefetchQuery(options); await queryClient.cancelQueries({ queryKey: options.queryKey }); await pending; ``` Query cancellation, the configured request signal, and the timeout remain active together. A per-request signal replaces the client's default signal. Mutations accept the same request options through `.mutationOptions({ request })`; TanStack Query does not automatically cancel mutations. Request failures follow the typed HTTP client's [error handling](/docs/typed-client#error-handling). Native signal cancellation rejects with `AbortError`; timeout failures are reported as `BrickTransportError`. ## Connect a TanStack DB collection Pass the generated options to `queryCollectionOptions` and supply the collection's row identity and response selection: ```ts import { createCollection } from "@tanstack/react-db"; import { queryCollectionOptions } from "@tanstack/query-db-collection"; import { api } from "./api"; import { queryClient } from "./query-client"; export const pages = createCollection( queryCollectionOptions({ ...api.page.list.queryOptions({ input: { limit: 100 } }), queryClient, getKey: (page) => page.id, select: (response) => response.items, }), ); ``` This collection contains the requested page of results. Implement pagination yourself when you need all rows. Brick does not create collection identity, optimistic writes, or mutation synchronization. Explicitly invalidate the collection's query or call `pages.utils.refetch()` after relevant mutations. The Pages example uses Query options for editor loads and saves. Successful saves invalidate typed list and detail keys and explicitly refetch the collection used by the dashboard and command bar. --- # AI Tools and Model Context Protocol (MCP) Expose backend actions to AI agents via Model Context Protocol (MCP) and JSON-RPC. Source: /docs/tools-and-mcp Brick-TS provides first-class support for AI agents through Model Context Protocol (MCP) tool declarations and JSON-RPC transports. ## Minimal Example Define an AI tool on a service, assemble an MCP registry, and serve it over JSON-RPC 2.0: ```ts title="src/services/documents.ts" import { brick, createMcpHandler, createMcpRegistry, defineService, t, } from "@brickkit/core"; export const docsService = defineService("docs"); // 1. Declare an AI tool with an object input schema and description export const searchDocs = docsService.tool({ name: "search_docs", description: "Search knowledge base documents by query string", input: t.Object({ query: t.String({ description: "Search terms or keywords" }), limit: t.Optional(t.Number({ default: 5 })), }), output: t.Array( t.Object({ id: t.String(), title: t.String(), snippet: t.String(), }), ), execute: async ({ input, ctx }) => { return [ { id: "doc_1", title: "Architecture", snippet: "Brick-TS is built on Bun...", }, ]; }, }); // 2. Build MCP registry from registered tools const mcpRegistry = createMcpRegistry({ services: [docsService], }); // 3. Create JSON-RPC 2.0 handler const mcpHandler = createMcpHandler({ registry: mcpRegistry, serverInfo: { name: "docs-agent-server", version: "1.0.0" }, }); // 4. Mount on the Brick (Elysia) app; the handler only accepts POST const app = brick({ services: [docsService] }); app.post("/mcp", ({ request }) => mcpHandler(request)); app.listen(4000); ``` ## Tools (`defineTool` / `service.tool`) Tools are specialized actions designed for invocation by Large Language Models: - **Object Schema Requirement**: The `input` schema, if present, must be a TypeBox object (`t.Object(...)`). Any other schema makes `defineTool()` / `service.tool()` throw `Tool '' input must be an object schema` when the tool is defined. - **MCP-only by default**: Tools are not mounted as HTTP routes. Set `http: true` on the tool (or register it with `service.action(tool)`) to also expose it as an HTTP action and in OpenAPI. - **Agent Descriptions**: Provide clear `description` strings on the tool and each schema property; LLM agents use these descriptions to determine tool calling logic. - **Annotations**: Tools accept MCP annotations: `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. You can also set a top-level `title`: ```ts title="src/services/admin.ts" export const deleteUserTool = adminService.tool({ name: "delete_user", description: "Permanently delete a user account and associated data", annotations: { destructiveHint: true, readOnlyHint: false, }, input: t.Object({ userId: t.String({ description: "The unique identifier of the user to remove", }), }), output: t.Object({ success: t.Boolean() }), execute: async ({ input, error }) => { return { success: true }; }, }); ``` ## MCP Registry (`createMcpRegistry`) `createMcpRegistry` gathers only explicitly defined tools: - Standard service actions and internal functions are **never exposed** to AI agents unless declared as tools via `service.tool` or `defineTool`. - You can pass `services` (their `service.tool()` tools are collected), standalone `tools` from `defineTool()`, or both. - Two different tools with the same name are rejected with `Duplicate MCP tool: ` when the registry is created. ```ts title="src/mcp-setup.ts" import { createMcpRegistry } from "@brickkit/core"; import { docsService } from "./services/documents"; import { adminService } from "./services/admin"; export const registry = createMcpRegistry({ services: [docsService, adminService], }); // Inspect available tools: const toolList = registry.listTools(); console.log(toolList.map((t) => t.name)); // ["search_docs", "delete_user"] ``` ## JSON-RPC 2.0 Handler (`createMcpHandler`) `createMcpHandler({ registry, serverInfo, protocolVersions? })` returns a stateless `(request, ctx?) => Promise` handler for MCP JSON-RPC 2.0 over HTTP POST. Non-POST requests get `405`, and notifications (requests without an `id`) get `202` with no body. It handles: - `initialize`: Returns `{ capabilities: { tools: {} }, protocolVersion, serverInfo }`. The client's requested version is echoed if it is in `protocolVersions` (default `["2025-06-18"]`); otherwise the first listed version is returned. - `ping`: Returns `{}`. - `tools/list`: Returns each tool's `name`, `title` (if set), `description`, `inputSchema`, and `annotations`. - `tools/call`: Validates and executes the tool, returning `{ content: [{ type: "text", text }] }`. Strings are returned as-is; other values are JSON-stringified. A thrown error becomes a result with `isError: true` and the error message as text. An unknown tool name returns JSON-RPC error `-32602`. The handler performs no authentication. Wrap it with your own auth, and pass an optional `ctx` (for example `{ user }`) as the second argument; it is forwarded to the tool's execution context along with the `request`. ### Connecting a Client Point an MCP client that supports HTTP transport at your endpoint, for example `http://localhost:4000/mcp`. The configuration format depends on the client. --- # OpenAPI 3.1 & Interactive Documentation Generate OpenAPI 3.1 specifications and serve interactive Scalar and Swagger docs automatically. Source: /docs/openapi Brick-TS automatically generates compliant OpenAPI 3.1 JSON specifications from TypeBox schemas and serves interactive documentation via Scalar and Swagger UI. ## Minimal Example Enable automatic documentation generation by passing services into `brick()`. Export the app as the **default export** so the CLI generators can import the definition without starting a server: ```ts title="src/server.ts" import { brick, defineService, t } from "@brickkit/core"; const usersService = defineService("users"); usersService.action({ name: "getUser", description: "Fetch user profile by ID", method: "GET", path: "/api/users/:id", input: t.Object({ id: t.String() }), output: t.Object({ id: t.String(), name: t.String() }), execute: ({ input }) => ({ id: input.id, name: "Ada Lovelace" }), }); export default brick({ services: [usersService], title: "Users API", version: "1.0.0", description: "User management and profile service mesh", prefix: "/api", }); ``` Start the server from a separate entry file, so importing the app has no side effects: ```ts title="src/index.ts" import app from "./server"; app.listen(4000); ``` Once started, the following endpoints are available (mounted at the app root, not under `prefix`): - **OpenAPI 3.1 Spec**: `http://localhost:4000/openapi.json` - **Scalar Modern UI**: `http://localhost:4000/docs` - **Swagger UI**: `http://localhost:4000/swagger` ## Server Documentation Options Configure OpenAPI and documentation routes in `CreateServerOptions`: | Option | Type | Default | Description | | :------------ | :---------------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `docs` | `boolean` | `true` | Enables or disables OpenAPI spec and HTML documentation endpoints. | | `title` | `string` | `"Brick-TS API Mesh"` | API title in OpenAPI `info.title`. | | `version` | `string` | `"1.0.0"` | API version in OpenAPI `info.version`. | | `description` | `string` | `undefined` | Overview description rendered in OpenAPI `info.description`. | | `openApiPath` | `string` | `"/openapi.json"` | Path where raw OpenAPI 3.1 JSON is served. | | `docsPath` | `string` | `"/docs"` | Path where Scalar interactive documentation is mounted. | | `swaggerPath` | `string` | `"/swagger"` | Path where Swagger UI is mounted. | | `reference` | `boolean \| ReferenceOptions` | `false` | `true` mounts a dedicated Scalar page at `/reference`. Options mount one only with a `path`; `documents` adds OpenAPI documents to the `brick dev` dashboard. | ### Mounting a Dedicated `/reference` Route For web apps that use `/docs` for product documentation (such as a documentation website), mount the API reference on `/reference`: ```ts title="src/server.ts" export default brick({ services: [usersService], docs: true, reference: { path: "/reference", title: "Platform API Reference", }, }); ``` ### Adding Documents to the Dashboard `brick dev` renders API documentation in its dashboard, in the APIs tab, so a page does not have to be served at all. `reference.documents` adds OpenAPI documents next to the app's own, such as the one Better Auth generates: ```ts title="src/server.ts" export default brick({ services: [usersService], reference: { documents: [ { title: "Auth", document: () => auth.api.generateOpenAPISchema() }, ], }, }); ``` Each entry has a `title` and a `document`: an OpenAPI object, or a function (sync or async) that returns one and runs each time the app starts. A document that throws is skipped with a warning. `app.referenceDocuments()` returns the app's document followed by these, and the app's own is left out when `docs` is `false`. With `reference: { path }` the same options also serve a standalone Scalar page. ## Programmatic Spec Generation (`generateOpenApiSpec`) Generate specifications programmatically (e.g. for CI pipelines or static documentation sites) without starting an HTTP server: ```ts title="scripts/generate-spec.ts" import { generateOpenApiSpec } from "@brickkit/core"; import { usersService } from "../src/services/users"; const spec = generateOpenApiSpec({ title: "Production Platform API", version: "2.0.0", prefix: "/api", services: [usersService], }); await Bun.write("dist/openapi.json", JSON.stringify(spec, null, 2)); console.log("OpenAPI 3.1 spec written to dist/openapi.json"); ``` You can also use the CLI generator, which imports the entry's **default export** (so it reads the app definition without calling `listen()`): ```bash bunx brick gen openapi src/server.ts --output openapi.json ``` ## Security Requirements in OpenAPI Brick automatically flags protected operations in the OpenAPI specification: - Actions with `authorize` guards advertise authentication requirements in their operation definition. - CRUD resources with `access !== "public"` automatically advertise security requirements while public CRUD resources omit them. --- # Zero-Codegen Type Inference Infer static TypeScript types directly from TypeBox schemas and Drizzle models without build steps. Source: /docs/type-inference Brick-TS delivers instant, bidirectional type safety by deriving static TypeScript types directly from runtime TypeBox schemas and Drizzle database models without intermediate compilation steps. ## Minimal Example Derive static TypeScript types directly from schemas and tables without running codegen commands: ```ts title="src/types/models.ts" import { type Static, sqliteTable, t, text } from "@brickkit/core"; // 1. Runtime validation schema -> TypeScript static type export const UserSchema = t.Object({ id: t.String(), name: t.String(), role: t.Union([t.Literal("admin"), t.Literal("member")]), }); export type User = Static; // Equivalent to: { id: string; name: string; role: "admin" | "member" } // 2. Drizzle table -> Select and Insert types export const users = sqliteTable("users", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: text("name").notNull(), bio: text("bio"), }); export type UserRow = typeof users.$inferSelect; export type NewUserRow = typeof users.$inferInsert; ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `users` | Field | Type | Description | | ------ | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `name` | `text` | Display name. | | `bio` | `text` | Profile biography. Optional | ## Why Zero-Codegen Matters Many backend frameworks rely on external DSLs, generated code directories (`gen/`, `prisma/client`), or file watchers. Brick-TS avoids code generation: - **Instant Feedback**: Modifying a database column or an action schema reflects immediately in TypeScript errors across your editor and test runners. - **Single Source of Truth**: The runtime validator and the static compiler type are defined in the exact same TypeScript statement. - **No Stale Build Artifacts**: You never encounter compiler errors caused by forgotten `generate` commands. ## Action Type Inference Actions infer types automatically from their schema declarations: ```ts title="src/services/projects.ts" import { defineService, t } from "@brickkit/core"; export const projectsService = defineService("projects"); export const createProject = projectsService.action({ name: "createProject", input: t.Object({ title: t.String(), tags: t.Array(t.String()), }), output: t.Object({ id: t.String(), title: t.String(), }), execute: ({ input }) => { // TypeScript automatically types `input` as: // { title: string; tags: string[] } return { id: "proj_1", title: input.title }; }, }); ``` ### Context User Narrowing (`NarrowAuthorizedContext`) When an action declares an `authorize` guard, Brick narrows `ctx.user` from `User | null` to non-nullable `User` inside `execute`: ```ts title="src/services/profile.ts" export const updateProfile = projectsService.action({ name: "updateProfile", authorize: ({ user }) => !!user, execute: ({ ctx }) => { // ctx.user is typed as NonNullable return { owner: ctx.user.id }; }, }); ``` ## Client Type Inference Frontend clients get typed callers from the generated contract (see Typed Client SDK). The contract file exports both the runtime metadata and its type, so the frontend bundle never includes backend server code: ```ts title="src/frontend/api.ts" import { createBrickClient } from "@brickkit/core/client"; import { contract, type AppContract } from "../../_brick/contract"; export const api = createBrickClient({ baseUrl: "http://localhost:4000", contract, }); // Autocomplete and type checking on every action: const project = await api.projects.createProject({ title: "New Website", tags: ["design", "typescript"], }); ``` --- # Database Management & Migrations Configure SQLite and PostgreSQL databases, development sync, and production migrations. Source: /docs/database Brick-TS provides database configuration handles for SQLite and PostgreSQL, with development table synchronization and caller-owned production migrations via Drizzle ORM. ## Minimal Example Configure an in-memory SQLite database for local development and test: ```ts title="src/database.ts" import { defineDatabase, sqliteTable, syncSchema, text } from "@brickkit/core"; export const users = sqliteTable("users", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), email: text("email").notNull().unique(), }); // Configure SQLite database handle export const database = defineDatabase({ tables: { users }, }); // Dev/test only: explicitly sync SQLite schema syncSchema({ users }, database.getDb()); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `users` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `email` | `text` | Unique email address. Unique | ## Configuring Databases (`defineDatabase`) `defineDatabase` creates a lazy, typed database handle. Connections are opened only when requested by services or runtime actions. ### 1. SQLite Configuration The default database engine in Brick-TS is `sqlite`, running on Bun's high-performance native SQLite client (`bun:sqlite`): ```ts title="src/db/sqlite.ts" import { defineDatabase } from "@brickkit/core"; import { users, posts } from "./schema"; export const sqliteDb = defineDatabase({ engine: "sqlite", // default filename: "./data/app.db", // Omit for in-memory :memory: wal: true, // Enable Write-Ahead Logging (default true) foreignKeys: true, // Enforce foreign key constraints (default true) tables: { users, posts }, }); ``` ### 2. PostgreSQL Configuration Connect to PostgreSQL using the PostgresJS driver: ```ts title="src/db/postgres.ts" import { defineDatabase, secret } from "@brickkit/core"; import { users, posts } from "./schema"; export const postgresDb = defineDatabase({ engine: "postgres", url: secret("DATABASE_URL"), // Resolves from environment variable ssl: process.env.NODE_ENV === "production", tables: { users, posts }, }); ``` ### Database Configuration Options Table | Option | Engine | Default | Description | | :------------ | :------- | :----------- | :------------------------------------------------------------------------------------------------------------- | | `engine` | All | `"sqlite"` | `"sqlite"` or `"postgres"`. | | `tables` | All | `undefined` | Record or array of Drizzle tables registered with the handle. | | `filename` | SQLite | `":memory:"` | Path to SQLite file on disk. Parent directories are created automatically. | | `wal` | SQLite | `true` | Enables Write-Ahead Logging for high-concurrency performance. | | `foreignKeys` | SQLite | `true` | Enforces SQLite foreign key constraints. | | `url` | Postgres | `undefined` | PostgreSQL connection string URL (`postgres://...`) or `SecretRef`. Falls back to `DATABASE_URL` when omitted. | | `ssl` | Postgres | `undefined` | SSL mode or certificate configuration. | ## SQLite Schema Synchronization (`syncSchema`) For local development and unit tests, Brick provides an explicit schema preparation utility: ```ts title="src/setup.ts" import { syncSchema } from "@brickkit/core"; import { usersService } from "./services/users"; // Automatically generates SQLite DDL and creates tables if they don't exist: syncSchema(usersService); ``` > **Explicit Development Helper**: `syncSchema` is an explicit development and > test utility. Brick-TS **never** alters database schema automatically at > server boot. Do not call `syncSchema` in production environments. ## Production Schema Migrations (PostgreSQL) In production, database migrations are **caller-owned**. Manage migrations with Drizzle Kit: ### 1. Configure Drizzle Kit Create `drizzle.config.ts` in your project root: ```ts title="drizzle.config.ts" import { defineConfig } from "drizzle-kit"; export default defineConfig({ schema: "./src/schema/**/*.ts", out: "./drizzle", dialect: "postgresql", dbCredentials: { url: process.env.DATABASE_URL!, }, }); ``` ### 2. Generate and Apply Migrations Use standard Drizzle Kit commands in your deployment pipeline: ```bash # 1. Generate migration SQL files from schema diffs bunx drizzle-kit generate # 2. Apply pending migrations to the target database bunx drizzle-kit migrate ``` This workflow gives you full version control, rollback inspection, and zero runtime lock-in. ## Secondary Indexes and Search Optimizations Brick-TS provides explicit opt-in helpers for secondary indexes and search (no indexes are created automatically — call these yourself): - **Secondary B-Tree Indexes**: `ensureResourceIndexes(db, table, { sortFields, ownerField })` creates indexes on hot lookup and sorting paths. Pass `searchFields` on PostgreSQL to also add `pg_trgm` GIN indexes for fast substring matching. - **SQLite FTS5 Full-Text Search**: `ensureSQLiteFts(db, table, searchCols)` builds an FTS5 sidecar virtual table with sync triggers for rapid text search queries. --- # brick CLI Overview Command-line developer and build tooling for Brick-TS applications. Source: /docs/brick-cli The `brick` CLI ships with `@brickkit/cli` to provide development live reload, production servers, ahead-of-time compilation, and code generation tooling. ## Installation Install `@brickkit/cli` alongside `@brickkit/core`: **bun** ```bash bun add @brickkit/cli @brickkit/core ``` **pnpm** ```bash pnpm add @brickkit/cli @brickkit/core ``` **npm** ```bash npm install @brickkit/cli @brickkit/core ``` Run commands with `bunx brick` or add them to your `package.json` scripts: ```json title="package.json" { "scripts": { "dev": "brick dev src/server.ts --port 4000", "start": "brick start src/server.ts", "build": "brick build src/server.ts --outdir dist/brick", "gen:openapi": "brick gen openapi src/server.ts --output openapi.json", "gen:client": "brick gen client src/server.ts --output src/_brick/contract.ts", "info": "brick info src/server.ts" } } ``` ## Command Summary | Command | Arguments & Flags | Description | | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------- | | `brick dev` | `[entry] [--port ] [--dashboard-port ] [--history-days ] [--no-dashboard] [--client ] [--frontend ]` | Start local development server with file watching, live reload, and a local dashboard. | | `brick start` | `[entry] [--port ]` | Start production server without file watchers. | | `brick build` | ` [--outdir ] [--port ] [--no-aot]` | Compile a default-exported `BrickApp` into a standalone Bun server bundle. | | `brick gen openapi` | ` [--output ]` | Generate OpenAPI 3.1 JSON specification from services. | | `brick gen client` | ` [--output ]` | Generate typed browser client contract file (`_brick/contract.ts`). | | `brick info` / `brick services` | `` | Export the complete runtime architecture JSON schema (`ArchitectureSchema`). | ## Port Resolution Rules For `dev` and `start`, the listening port is resolved in strict precedence order: 1. **CLI Flag**: `--port ` (e.g. `brick dev --port 5000`) 2. **Environment Variable**: `PORT` (e.g. `PORT=3000 brick dev`) 3. **Default**: `4000` Port values must be valid integers between `1` and `65535`. Out-of-range values or non-numeric strings cause the CLI to exit immediately with an error. To generate contracts before frontend startup, see [coordinated development](/docs/brick-cli/dev-and-start#generate-contracts-before-frontend-startup). For typed query and mutation options, see [TanStack Query](/docs/tanstack-query). --- # Development & Production Server Run development servers with live reload and start production instances. Source: /docs/brick-cli/dev-and-start The `brick dev` and `brick start` commands run your service mesh with automatic route discovery, port validation, and live reload. ## Development Server (`brick dev`) Start local development with recursive file watching and debounced server restarts: ```bash brick dev src/server.ts --port 4000 ``` ### Live Reload Behavior When running `brick dev`: 1. **Entry File Loading**: The CLI resolves the application entry file. If an entry file path is provided but does not exist, the CLI exits immediately with an error. 2. **Watch Directories**: The CLI monitors `src/`, `app/`, and the parent folder of the entry file for changes (excluding `node_modules`). 3. **Fresh Application Process**: After a short debounce, `brick dev` stops the application process and starts a new one. The entry and all its dependencies run again with a fresh service registry and module graph. Default-exported `BrickApp` options (including the route prefix) are preserved; the CLI port takes precedence. For the programmatic `startDevServer({ prefix })` option, the exported app’s prefix takes precedence and the runner prints a warning. ### Generate contracts before frontend startup For a separate frontend tool, pass a contract destination and a frontend command: ```bash brick dev src/api/app.ts --client frontend/src/contract.ts --frontend 'bun run --cwd frontend dev' ``` The entry must default-export a `BrickApp` without calling `.listen()`. Keep startup jobs outside that module because generation evaluates it. `--client ` generates the contract before starting the backend. `--frontend ` starts the frontend once, after the first successful contract write. Entry and contract paths are relative to the CLI working directory. The frontend command requires `--client`, and both options require an entry. The development worker tracks imported local files, including dependencies outside the entry directory. It coalesces edits and restarts with a fresh module graph. Each restart regenerates types and HTTP route metadata before starting the backend. Contract writes are atomic, unchanged content preserves the file timestamp, and generated files cannot trigger regeneration loops. If generation fails, the error appears in the terminal and the previous contract remains intact. The backend worker exits; the supervisor waits for edits and retries. The frontend command waits for the first successful generation and remains running through later backend restarts. Stopping `brick dev` stops its backend and frontend children. Omit `--frontend` if another process starts the frontend. For an embedded Vite app such as Pages, use the [Vite contract plugin](/docs/typed-client#generate-the-contract-automatically), which generates during development and production builds. ### Startup Route Banner Upon startup, `brick dev` logs registered services and their public HTTP endpoints: ```text 🧱 Brick-TS Development Server Registered Services: 2 • pages (8 actions) -> POST /api/pages/page.list -> POST /api/pages/page.get -> POST /api/pages/page.create • users (2 actions) -> GET /api/users/:id -> POST /api/users/login ⚡ API Gateway running on: http://localhost:4000 🩺 Health check: http://localhost:4000/_health 🗺️ Architecture graph: http://localhost:4000/_brick/services 👀 Watching for changes in: /Users/project/src ``` ### Local Dashboard `brick dev` with an entry also serves a dashboard at `http://127.0.0.1:9400`. It lists requests as they run, shows a waterfall of each request's action and remote-call spans, and collects failures and reloads, including startup crashes. The live views survive restarts within the same session. Requests appear only from apps that opt in with `telemetry: true`. `brick dev` warns when the dashboard is on but the entry doesn't set it: ```ts const app = brick({ services: [pagesService], telemetry: true }); ``` ```bash brick dev src/server.ts # dashboard on 9400 brick dev src/server.ts --dashboard-port 9500 # another port brick dev src/server.ts --history-days 30 # keep a month of requests brick dev src/server.ts --history-days 0 # live requests only, nothing on disk brick dev src/server.ts --no-dashboard # API only ``` The Overview tab draws the app as a graph of clients, services, and databases. Each service shows its actions and tools, each database its name, engine, and tables, and the calls, durations, and health fill in from recent requests. Below it are each HTTP route with its action and recent request and failure counts, and the services with their databases and resources. SQL statements appear as child spans under the action that ran them, with the statement text on hover. Spans are colored by type: request, action, service call, and SQL. ### API Reference The APIs tab renders the app's OpenAPI documents with [Fumadocs OpenAPI](https://www.fumadocs.dev/docs/integrations/openapi), inside the dashboard and in its colors, with no separate page to serve. A sidebar lists each document's operations by tag, with a filter. The open operation shows its parameters, responses, and example requests, which default to JavaScript `fetch`. Try in Playground sends the call to the app, so a call made there shows up in the Requests tab, and under the response the dashboard shows the app's trace of that call (when the app has `telemetry` on): the error, the stack trace if the app threw one, and the spans. The dashboard forwards those calls itself, because the app is a different origin, and it forwards only to the app it is showing. The first document is the app's own; it is left out when `docs` is `false`. Add more through `reference.documents`, each a title and an OpenAPI document or a function that returns one. Better Auth's `openAPI()` plugin is a good fit: ```ts const app = brick({ services: [pagesService], reference: { documents: [ { title: "Auth", document: () => auth.api.generateOpenAPISchema() }, ], }, }); ``` A document that throws is skipped with a warning. Documents load again on each restart. ### Developer Docs Set `documentation` on the app and the dashboard gains a Docs tab that shows a folder of pages, so the notes about how your backend works live beside the app and its requests. The folder follows Fumadocs' layout: `.md` and `.mdx` files with frontmatter (`title`, `description`), and `meta.json` files for the order of pages and folders. `fumadocs-core` builds the page tree and search. ```ts // src/app.ts, with the docs in docs/ at the project root const app = brick({ services: [pagesService], documentation: "../docs", // or { dir: "../docs", components: "../docs/components.tsx" } }); ``` Relative paths start at the folder of the app entry, as they read in the file that sets them: from `src/app.ts`, `"../docs"` is `docs/` at the project root. Absolute paths work too. ```bash bun add -d fumadocs-core @mdx-js/mdx ``` These two packages are optional: nothing loads them unless the app sets `documentation`. Without them the tab says what to install. ```text docs/ meta.json { "title": "Orders docs", "pages": ["index", "---Guides---", "guides"] } index.mdx architecture.mdx guides/ add-a-tool.mdx ``` A `pages` list in `meta.json` shows only what it names, in that order, so a page you add later needs a line there. Add `"..."` to the list to include every page you did not name. A folder without a `meta.json` lists its pages in alphabetical order. Saving a file updates the tab without a reload. Links between pages (`[setup](./setup.mdx#install)`, or the `href` of a card component) and local images work as they would on a docs site, code blocks are highlighted for light and dark, and the page has a table of contents and search. Pages can use Fumadocs' components without importing them: `Callout`, `Cards` and `Card`, `Tabs` and `Tab`, `Steps` and `Step`, `Accordions` and `Accordion`, `Files`, `File` and `Folder`, `TypeTable`, `InlineTOC`, `Banner`, and `DynamicCodeBlock` (shown without highlighting). Zoomable images, copyable code blocks with titles, and `npm` code blocks that show every package manager come with them. `AutoTypeTable`, `GithubInfo`, and `GraphView` need a build step or the network, so they show a note instead. Add your own with `components`: a module that exports React components, either as a `components` object or as the default export. The dashboard bundles it, so it can import anything the project has, and a component of the same name replaces a built-in one. React is shared with the dashboard, so hooks work. ```tsx title="docs/components.tsx" export function Endpoint({ method, path }: { method: string; path: string }) { return (

{method} {path}

); } export const components = { Endpoint }; ``` A page that uses a component nobody provided, or that does not compile, shows what is wrong in place of its content. A docs folder that is missing, or a `meta.json` that is not valid JSON, is reported in the terminal rather than stopping the app. ### Logs Call `ctx.logger.info()`, `.warn()`, `.error()`, or `.debug()` in an action, tool, or endpoint, and the line shows up in the dashboard instead of the terminal: ```ts execute: async ({ input, ctx }) => { ctx.logger.info("creating a user", { email: input.email }); // ... }, ``` Plain `console.log`, `console.info`, `console.warn`, `console.error`, and `console.debug` calls made while a request runs are captured the same way, so code and libraries that log with `console` need no change. This happens only while a dashboard is on: in `brick dev`, and under `brick start` when the app sets the [`dashboard` option](#monitoring-dashboard). Otherwise nothing is captured or changed, and `ctx.logger` and `console` print as usual. The Logs tab lists the lines from this run's requests, newest first, and is built for finding one line quickly: - **Search** takes words that must all appear, `"an exact phrase"`, and `-word` to leave lines out. Matches are marked in each line. - **Fields** narrow a search to one part of a line: `level:error` (or `level:warn,error`), `route:/api/pages`, `action:pages.echo`, `method:post`, and `trace:2ef0d1` (the start of a trace id). Any of them can be negated with `-`, and a value with spaces can be quoted, as in `route:"/api/a b"`. - **Level buttons** show how many lines match at each level, and **5m / 15m / 1h** limit how far back to look. Both are remembered. - **Pause** holds the list still while you read it, and says how many new lines arrived. - Point at a line to copy it, show only the lines of its request, or open the request. A long message folds to three lines until you open it. A line also appears in its request's details, in order, with the time since the request started. This needs `telemetry` on, because a line belongs to the request being traced. A line logged outside a request (at startup, say) or in an app that is not traced still goes to the terminal. Logging this way costs less than writing to the console: a line is a span event, one context lookup and a small record, and nothing is sent to the dashboard until the span ends. To keep a chatty action from slowing a request or swamping the dashboard, a line is cut at 4,000 characters and a span keeps its first 100 lines. The request's details say how many more were left out. ### Request History The Requests tab is one log of every request the app has served. Choose Live for this run, or a range from 1 hour to 90 days to look back. Custom takes an exact start and end, to the second, in your time zone, with Today, Yesterday, and the last 15 minutes as shortcuts, and it is kept until you clear it. Four cards across the top show the request count, error rate, median duration, and slowest 5% of what the list shows, so they follow the range and filters. A timeline below them shows volume, and dragging across it zooms the list to that time, as a Custom range does. Filters on the left show a count beside each value: outcome, service, method, route, and speed, with several values allowed per filter. The search box matches routes, paths, actions, and failure messages. Each row shows the time, method and status, route, how long it took, and the action that handled it or why it failed. Open a row to see the request beside the list: when it started, what it ran, how it ended, the error and stack trace if it failed, and the waterfall of its spans. Export saves the filtered requests as CSV. Finished requests are kept on disk between runs for 90 days by default, in `.brick/telemetry.sqlite` in the project folder. `.brick/` ignores itself, so it is never committed. History needs Bun, or Node 22.5 or later; without either, the dashboard shows live data only. Requests cut off by a restart are not kept, and at most 100,000 requests are stored, oldest dropped first. Stored requests include their spans, which carry SQL statement text, error messages, and stack traces, so treat `.brick/` like your logs. Use `--history-days 0` to keep nothing, or delete `.brick/` to start over. With the Vite plugin, pass `dashboard: { historyDays }` to `brickClient`. Requests that send a W3C `traceparent` header join the caller's trace, and the dashboard shows them under that trace id. The dashboard binds to `127.0.0.1` and rejects requests whose `Host` header is not local. It does not record request bodies or headers. Test Request calls pass through the dashboard to the app and nowhere else, and only from the dashboard's own page. Error messages, stack traces, and SQL statement text are visible to anyone who can open the dashboard. ## Production Server (`brick start`) Start the production server without file system watchers or development restarts: ```bash brick start src/server.ts --port 8080 ``` Or pass the port via environment variable: ```bash PORT=8080 brick start src/server.ts ``` If the entry file default-exports its app (`export default brick({ ... })`), `brick start` serves that app with the options it was built with, as `brick dev` does. ### Monitoring Dashboard `brick start` can serve a read-only dashboard of the running app: the same Requests, Logs, Errors, Overview, and Reloads tabs as in development, for people to watch production with. It is off unless the app asks for it with the `dashboard` option, and it needs the app to be the entry's default export: ```ts export default brick({ services: [pagesService], dashboard: { port: 9400, // its own port, apart from the app's host: "0.0.0.0", // 127.0.0.1 unless you say otherwise auth: { token: process.env.DASHBOARD_TOKEN }, }, }); ``` The dashboard listens on a port of its own, so you can keep it private or mount it at a different address from the app, and it keeps answering when the app's routes do not. Setting it turns `telemetry` on, since the dashboard shows what is traced. **Access control is required to listen beyond the machine.** On any host but loopback (`127.0.0.1`, `::1`, `localhost`), `brick start` refuses to run the dashboard without `auth`. It takes either or both of: - `token`: one secret, or several to rotate one while the other still works, each at least 16 characters (`openssl rand -base64 32` makes a good one). A browser asks for it in its sign-in prompt (any user name, the secret as the password), and a monitor or script sends `Authorization: Bearer `. - `authorize(request)`: your own check, to sign people in with the auth the app already has. It receives a standard `Request` (method, URL, and headers, but no body) and returns `true` to let it in, `false` to refuse with 401, or a `Response` to send instead, such as a redirect to your login page. It is called for each request. If it throws or takes longer than five seconds, the request is refused with 503, so a broken check never lets anyone in. A request is let in when it carries a valid `token`, or when `authorize` returns `true`. With Better Auth: ```ts dashboard: { host: "0.0.0.0", auth: { // People sign in through the app; monitors use the token. token: process.env.DASHBOARD_TOKEN, authorize: async (request) => { const session = await auth.api.getSession({ headers: request.headers }); return session?.user.role === "admin"; }, }, }, ``` The same shape fits any other provider, such as Clerk: verify the request's session in `authorize` and return whether that user may look. `GET /_health` on the dashboard's port needs no sign-in, so an uptime monitor can use it: it answers 200 `{"status":"ok","app":"up"}` while the app answers its own `/_health`, and 503 when it does not. It says nothing else. **What it shows, and what it does not.** Requests, spans, errors, logs, and the shape of the service, from the time the process started. Restart, the APIs and Docs tabs, the API proxy, and OpenAPI documents are not served at all, so a signed-in viewer can look but not change or call anything. Responses carry `Cache-Control: no-store`, a strict Content-Security-Policy, and `X-Frame-Options: DENY`. Serve it over HTTPS (terminate it at your proxy): a token sent over plain HTTP can be read on the way. There is no limit on failed sign-ins, so use long random tokens. **Logs go to the dashboard, not the console.** While the dashboard is on, `ctx.logger` and `console.log`, `info`, `warn`, `error`, and `debug` lines written during a request are kept for the dashboard and are not printed, so they stay out of the container's logs. Lines outside a request (startup, shutdown) and crashes still print. See [Logs](#logs). **Retention.** The dashboard keeps recent requests in memory, so they are gone when the process restarts. Set `historyDays` to keep finished requests on disk, in `.brick/` of the working folder, which then needs a volume that outlives the container. The dashboard runs inside the app's process. It stays up through errors in the app's handlers and through requests it cannot route, but if the process itself dies, so does the dashboard; use the container's restart policy for that. ## Port Resolution and Validation Port resolution follows a strict three-tier precedence: ```mermaid flowchart LR Flag["--port flag"] -->|Overrides| Env["PORT environment variable"] Env -->|Overrides| Default["Default: 4000"] ``` Examples: ```bash # 1. Defaults to 4000: brick dev src/server.ts # 2. Uses PORT=3000 from environment: PORT=3000 brick dev src/server.ts # 3. CLI --port 5000 overrides PORT=3000: PORT=3000 brick dev src/server.ts --port 5000 ``` Port numbers must be integers between `1` and `65535`. Invalid ports cause immediate process termination: ```bash brick dev src/server.ts --port 99999 # Error: Invalid port: 99999. Port must be an integer between 1 and 65535 ``` --- # Build & Compilation Compile default-exported Brick applications into standalone Bun server bundles. Source: /docs/brick-cli/build The `brick build` command compiles a default-exported `BrickApp` into a standalone Bun server bundle with ahead-of-time route handlers. ## Minimal Example Create an application entry that default-exports a `brick()` server instance without calling `.listen()`: ```ts title="src/server.ts" import { brick, defineService, t } from "@brickkit/core"; export const healthService = defineService("health"); healthService.action({ name: "ping", method: "GET", output: t.Object({ ok: t.Boolean() }), execute: () => ({ ok: true }), }); export const app = brick({ services: [healthService], title: "Health API", }); // Default export is required for brick build: export default app; ``` Compile the application bundle: ```bash brick build src/server.ts --outdir dist/brick ``` Output (paths printed are absolute, resolved from `--outdir`): ```text Compiled 1 services / 1 routes IR: /Users/project/dist/brick/brick-ir.json Run: bun /Users/project/dist/brick/server.js ``` Run the compiled server with Bun: ```bash PORT=8080 bun dist/brick/server.js ``` ## Options Table | Flag | Argument | Default | Description | | :--------- | :------------- | :------------- | :------------------------------------------------------------------------------ | | `` | File path | _(Required)_ | Application entry file. Must default-export a `BrickApp` instance. | | `--outdir` | Directory path | `"dist/brick"` | Target output directory for compiled bundles and IR artifacts. | | `--port` | Number | `4000` | Default listening port baked into the server launcher (overridable via `PORT`). | | `--no-aot` | Flag | `false` | Disables ahead-of-time compilation of Elysia route handlers. | > **Application Configuration vs Build Flags**: The flags `--prefix` and `--no-request-logging` are rejected. Passing either makes `brick build` throw; configure `prefix` and `requestLogging` directly in your `brick({ prefix, requestLogging })` definition instead of CLI flags. ## Output Bundle Structure The compilation produces four artifacts in the output directory: ```text dist/brick/ ├── server.js # Standalone Bun server entry with graceful shutdown ├── routes.js # Precompiled route dispatcher and IR bindings ├── server.js.map # Source map for debugging └── brick-ir.json # Serialized intermediate representation of all routes ``` ### Production Readiness The generated `server.js` includes: - Production `PORT` environment variable resolution with numeric validation (1-65535). - Graceful shutdown listeners for `SIGINT` and `SIGTERM` signals. - Ahead-of-time compiled Elysia route plans (when `--no-aot` is not passed), reducing startup compilation work on cold boots. --- # Code & Spec Generators Generate OpenAPI 3.1 specifications and typed client contracts from application code. Source: /docs/brick-cli/gen The `brick gen` command extracts OpenAPI 3.1 specifications and typed browser client contracts directly from application code. ## Generating OpenAPI 3.1 (`brick gen openapi`) Extract the complete OpenAPI 3.1 specification from your application entry: ```bash # Print specification JSON to stdout: brick gen openapi src/server.ts # Write specification JSON to a file: brick gen openapi src/server.ts --output openapi.json ``` Output: ```text ✅ OpenAPI 3.1 specification written to openapi.json ``` ### How It Works `brick gen openapi` loads your application entry file (which must default-export a `BrickApp` instance), inspects all registered services, mounts, and CRUD access policies, and calls `generateOpenApiSpec()` from `@brickkit/core`. ## Generating Typed Client Contracts (`brick gen client`) Generate a standalone TypeScript client contract file for browser frontends without running Vite or launching an HTTP server: ```bash # Generate default contract in _brick/contract.ts: brick gen client src/server.ts # Write contract to a custom destination: brick gen client src/server.ts --output src/client/contract.ts ``` Output (the CLI prints the absolute resolved path): ```text Client contract written to /Users/project/src/client/contract.ts ``` ### Generated Contract Structure The generated contract file contains TypeScript types and route metadata used by `createBrickClient` and [the TanStack Query client](/docs/tanstack-query): ```ts title="src/client/contract.ts" // Generated by the Brick Vite plugin. Do not edit. import type { BrickClientAction } from "@brickkit/core/client"; export type AppContract = { pages: { page: { list: BrickClientAction; get: BrickClientAction<{ id: string }, PageOutput>; create: BrickClientAction; }; }; }; export const contract: AppContract = JSON.parse("..."); ``` Frontend applications import this contract to provide autocompletion and compile-time validation: ```ts title="src/frontend/api.ts" import { createBrickClient } from "@brickkit/core/client"; import { contract } from "../client/contract"; export const api = createBrickClient({ baseUrl: "http://localhost:4000", contract, }); ``` ## Options Summary | Subcommand | Flag | Description | | :--------- | :---------------- | :------------------------------------------------------------------ | | `openapi` | `` | _(Required)_ Application entry file default-exporting a `BrickApp`. | | `openapi` | `--output ` | Writes JSON to specified file path instead of stdout. | | `client` | `` | _(Required)_ Application entry file default-exporting a `BrickApp`. | | `client` | `--output ` | Destination file path (defaults to `_brick/contract.ts`). | ## Run generation during development and builds For Vite 8, use the [automatic contract plugin](/docs/typed-client#generate-the-contract-automatically). It runs before frontend startup and production compilation and regenerates after API edits. For other frontend tools, use [`brick dev --client --frontend`](/docs/brick-cli/dev-and-start#generate-contracts-before-frontend-startup). Put `brick gen client` first in production build and typecheck scripts so a clean checkout has the contract before frontend compilation. All integrations share the same generator and atomic writer. Unchanged content preserves the destination file and its timestamp. Generation errors preserve the previous contract. The entry must default-export a `BrickApp` without calling `.listen()`; keep startup side effects in a separate bootstrap. --- # Architecture Introspection Export and inspect the runtime architecture JSON schema of your application. Source: /docs/brick-cli/info The `brick info` command exports the complete runtime service graph, mounted endpoints, access policies, and database configurations as JSON. ## Minimal Example Run `brick info` against an application entry file: ```bash brick info src/server.ts ``` Alias: ```bash brick services src/server.ts ``` The CLI loads the application and prints the `ArchitectureSchema` JSON to stdout: ```json { "version": "1.0.0", "services": [ { "name": "pages", "hasDatabase": true, "databaseConfig": { "engine": "sqlite", "tables": ["pages"] }, "resources": [{ "name": "page" }], "tools": [], "actions": [ { "name": "page.list", "method": "POST", "path": "/api/pages/page.list", "serviceName": "pages", "plugin": "@brickkit/crud", "resource": "page", "hasAuthorize": false, "hasScope": false, "accessPolicy": "public" }, { "name": "page.create", "method": "POST", "path": "/api/pages/page.create", "serviceName": "pages", "plugin": "@brickkit/crud", "resource": "page", "hasAuthorize": true, "hasScope": false, "accessPolicy": "authorized" } ] } ] } ``` ## Runtime Architecture Endpoint In addition to the CLI command, every running Brick server exposes the exact same architecture snapshot over HTTP at: ```bash curl http://localhost:4000/_brick/services ``` This endpoint is read-only and provides an up-to-date representation of all mounted services and security policies. The database handle itself is excluded from the export (`options.database` is omitted), but the remaining service `options` are included; do not treat the output as free of configuration values. ## Use Cases for Introspection - **Security Auditing**: Verify in CI that no internal or administrative actions are accidentally exposed with `accessPolicy: "public"`. - **Microservice Graph Visualization**: Ingest the architecture JSON to render dynamic service dependency graphs and infrastructure maps. - **Contract Verification**: Ensure that paths and methods match expected API gateway routing rules. --- # Plugins Overview Extend services and resources with modular plugins, typed APIs, and scoped routes. Source: /docs/plugins Brick-TS provides modular plugin primitives—`defineServicePlugin` and `defineResourcePlugin`—to package reusable capabilities, custom endpoints, and typed resource extensions. ## Minimal Example Define a reusable service plugin that registers a health check action and install it with `service.use()`: ```ts title="src/plugins/ping.ts" import { defineServicePlugin, defineService, t } from "@brickkit/core"; // 1. Declare the plugin descriptor export function pingPlugin(options: { endpointPath?: string } = {}) { return defineServicePlugin({ name: "ping", setup({ routes }) { const ping = routes.action({ name: "ping", method: "GET", path: options.endpointPath ?? "/ping", output: t.Object({ status: t.String(), timestamp: t.Number() }), execute: () => ({ status: "ok", timestamp: Date.now() }), }); return { ping }; }, }); } // 2. Install the plugin onto a service const service = defineService("system"); const api = service.use(pingPlugin()); // The returned API is fully typed in TypeScript: const result = await api.ping({}); console.log(result.status); // "ok" ``` ## Two Plugin Kinds Brick-TS divides plugins into two distinct scopes: ### 1. Service Plugins (`defineServicePlugin`) - Installed with `service.use(plugin)`. - Scope: Service-level capabilities (e.g. rate limiters, health endpoints, event emitters). - Returns a typed API object that becomes accessible to callers. ### 2. Resource Plugins (`defineResourcePlugin`) - Installed with `resource.use(plugin)`. - Scope: Table-level capabilities (e.g. `@brickkit/crud`, comments, audit logs, file attachments). - Receives `{ resource, service, routes }` and attaches properties directly onto the host resource. ```ts title="src/example-resource-plugin.ts" // Attaching CRUD plugin to a resource: const page = service .resource({ name: "page", table: pages, id: pages.id }) .use(crud({ access: "public", fields: { id: { read: true } } })); ``` ## Plugin Lifecycle & Guarantees All plugins follow strict design rules: - **Synchronous Registration**: The `setup()` function must be synchronous. Plugins register actions, routes, and hooks before the server compiles. - **Connection Isolation**: Plugins do not open database pools or mutate schemas during setup. Database connections flow through `ctx.db` at runtime. - **Namespaced Actions**: Actions registered by plugins stay scoped to their host resource or service, preventing naming collisions. ### Schema The ping service plugin requires no database table. The resource-plugin example requires a host table; its CRUD field policy exposes only `id`. #### Table Name: `pages` | Field | Type | Description | | ----- | ------ | ------------------- | | `id` | `text` | Page identifier. PK | --- # Built-in Capabilities Explore built-in framework plugins for CRUD, request logging, and interactive documentation. Source: /docs/plugins/built-in Brick-TS ships with built-in capabilities and plugins for declarative database operations, request timing, health checks, and API documentation. ## The CRUD Plugin (`@brickkit/crud`) The primary resource plugin in Brick-TS is `crud()`, published in `@brickkit/crud`. It installs declarative, field-guarded operations onto any table resource: ```ts title="src/services/catalog.ts" import { defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; export const products = sqliteTable("products", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: text("name").notNull(), price: text("price").notNull(), }); const database = defineDatabase({ tables: { products } }); const service = defineService("catalog", { database }); // Install the CRUD plugin onto the product resource: export const product = service .resource({ name: "product", table: products, id: products.id }) .use( crud({ access: "public", fields: { id: { read: true, filter: ["eq", "in"] }, name: { read: true, create: true, update: true, filter: ["contains"], sort: true, }, price: { read: true, create: true, update: true }, }, }), ); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `products` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `name` | `text` | Display name. | | `price` | `text` | Product price stored as text. | For full field policy options, query filter syntax, and operations control, see **[Resources and CRUD](/docs/resources)**. ## Request Logging Plugin Request timing and logging are enabled by default in `brick()`. Every incoming route is measured and logged with status code and duration: ```ts title="src/server.ts" import { brick } from "@brickkit/core"; // Enabled by default: const app = brick({ requestLogging: true, }); // Or customize options: const customApp = brick({ requestLogging: { enabled: true, logger: (msg) => console.log(msg), }, }); ``` To disable logging entirely, pass `requestLogging: false`. ## Interactive API Documentation Interactive documentation is built into `brick()` and requires no separate middleware: - **Scalar API Docs**: Modern documentation available at `/docs` (e.g. `http://localhost:4000/docs`). - **Swagger UI**: Traditional Swagger documentation available at `/swagger` (e.g. `http://localhost:4000/swagger`). - **Raw OpenAPI 3.1 Spec**: Machine-readable specification at `/openapi.json`. These documentation routes mount at the root and are not affected by `prefix`. For configuration options and mounting custom reference routes, see **[OpenAPI 3.1 & Interactive Documentation](/docs/openapi)**. ## Diagnostics & Health Check (`/_health`) Every Brick server automatically mounts a diagnostics route at `/_health`: ```bash curl http://localhost:4000/_health ``` Response: ```json { "status": "ok", "uptime": 12.45, "timestamp": 1728821400000 } ``` This endpoint is ideal for Kubernetes liveness/readiness probes, Docker health checks, and load balancer pinging. --- # Audit Trail Plugin Recipe Record immutable audit trails for resource mutations using CRUD lifecycle hooks. Source: /docs/plugins/audit This recipe demonstrates how to create a reusable audit trail plugin that logs resource mutations to an application-owned database table using CRUD `after*` hooks. ## Minimal Example Create an audit logging resource plugin and attach it to sensitive entity resources: ```ts title="src/plugins/audit.ts" import { defineResourcePlugin, integer, sqliteTable, text, } from "@brickkit/core"; // 1. Declare the audit log storage table export const auditLogs = sqliteTable("audit_logs", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), resourceName: text("resource_name").notNull(), recordId: text("record_id").notNull(), operation: text("operation").notNull(), // "create" | "update" | "delete" userId: text("user_id"), timestamp: integer("timestamp").notNull(), }); // 2. Define the resource plugin export function auditTrail() { return defineResourcePlugin({ name: "auditTrail", setup({ resource, service }) { const recordMutation = async ( operation: string, recordId: string, ctx: any, ) => { if (!ctx.db) return; await ctx.db.insert(auditLogs).values({ resourceName: resource.name, recordId: String(recordId), operation, userId: ctx.user?.id ?? null, timestamp: Date.now(), }); }; return { recordMutation, }; }, }); } ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `audit_logs` | Field | Type | Description | | --------------- | --------- | --------------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `resource_name` | `text` | Name of the mutated resource. | | `record_id` | `text` | Identifier of the mutated row. | | `operation` | `text` | Mutation action: create, update, or delete. | | `user_id` | `text` | Authenticated user identifier, or null for anonymous writes. Optional | | `timestamp` | `integer` | Unix epoch timestamp in milliseconds, set by the audit logger. | ## Wiring into CRUD Lifecycle Hooks Attach the audit logger to your resource's `crud()` lifecycle hooks: ```ts title="src/services/accounts.ts" import { crud } from "@brickkit/crud"; import { accountsTable, accountsService } from "./schema"; import { auditTrail } from "../plugins/audit"; // 1. Install audit plugin onto resource const accountResource = accountsService .resource({ name: "account", table: accountsTable, id: accountsTable.id }) .use(auditTrail()); // 2. Wire mutation records in crud() hooks accountResource.use( crud({ access: { authorize: ({ ctx }) => !!ctx.user, }, fields: { id: { read: true }, name: { read: true, create: true, update: true }, }, hooks: { afterCreate: async ({ row, ctx }) => { await accountResource.recordMutation("create", String(row.id), ctx); }, afterUpdate: async ({ id, ctx }) => { await accountResource.recordMutation("update", String(id), ctx); }, afterDelete: async ({ id, ctx }) => { await accountResource.recordMutation("delete", String(id), ctx); }, }, }), ); ``` ## Architectural Considerations - **Asynchronous Execution**: `after*` hooks run after the write commits to the primary table. If audit row insertions must commit atomically with the mutation, run both queries inside an application transaction via `ctx.db.transaction()`. - **Masking & Security**: Never log unredacted passwords, payment details, or personal data to audit tables. - **Dedicated Read Policy**: Expose audit logs through a separate administrative resource with restricted access controls. --- # Comments Plugin Pattern Attach discussion threads to any parent resource using a canonical comments schema. Source: /docs/plugins/comments This guide demonstrates the canonical Brick-TS comments plugin pattern, attaching threaded discussions to any parent resource with target foreign-key validation. ## Canonical Schema The comments pattern uses a single, unified database schema across all services: ```ts title="src/extensions/comments/schema.ts" import { sqliteTable, text } from "@brickkit/core"; export const commentsTable = sqliteTable("comments", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), targetId: text("target_id").notNull(), body: text("body").notNull(), }); ``` ### Schema These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `comments` | Field | Type | Description | | ----------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `target_id` | `text` | Parent record identifier, validated by the application. | | `body` | `text` | Text content. | #### Table Name: `pages` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | ## Plugin Implementation Implement the comments factory using `defineResourcePlugin` and `@brickkit/crud`: ```ts title="src/extensions/comments/index.ts" import { ActionExecutionError, defineResourcePlugin, eq } from "@brickkit/core"; import { crud } from "@brickkit/crud"; import { commentsTable } from "./schema"; export { commentsTable } from "./schema"; /** Resource plugin that attaches comments to any host resource. */ export function comments(options: { table: typeof commentsTable }) { return defineResourcePlugin({ name: "example/comments", setup({ resource, service }) { const comment = service .resource({ name: `${resource.name}Comment`, table: options.table, id: options.table.id, }) .use( crud({ // Anyone may read and post comments; editing and deleting // existing comments requires an authenticated user. access: { authorize: ({ operation, ctx }) => { if (operation === "update" || operation === "delete") { return !!ctx.user; } return true; }, }, fields: { id: { read: true, filter: ["eq"] }, targetId: { read: true, create: true, filter: ["eq"] }, body: { read: true, create: true, update: true, filter: ["contains"], }, }, hooks: { beforeCreate: async ({ data, ctx }) => { data.body = String(data.body).trim(); if (!data.body) { throw new ActionExecutionError( "BAD_REQUEST", "Comment body cannot be blank", 400, ); } // Verify parent resource exists in the database const parents = await ctx.db .select({ id: resource.id }) .from(resource.table) .where(eq(resource.id, data.targetId)) .limit(1); if (!parents.length) { throw new ActionExecutionError( "NOT_FOUND", "Comment target not found", 404, ); } }, beforeUpdate: ({ data }) => { if (data.body !== undefined) { data.body = String(data.body).trim(); if (!data.body) { throw new ActionExecutionError( "BAD_REQUEST", "Comment body cannot be blank", 400, ); } } }, }, }), ); return { comments: comment }; }, }); } ``` ## Attaching to Resources Attach the comments plugin to any host resource with `.use()`: ```ts title="src/services/pages.ts" import { defineDatabase, defineService, sqliteTable, text, } from "@brickkit/core"; import { crud } from "@brickkit/crud"; import { comments, commentsTable } from "../extensions/comments"; export const pagesTable = sqliteTable("pages", { id: text("id") .primaryKey() .$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), }); const database = defineDatabase({ tables: { pagesTable, commentsTable } }); export const pagesService = defineService("pages", { database }); // Attach comments plugin to the page resource: export const page = pagesService .resource({ name: "page", table: pagesTable, id: pagesTable.id }) .use( crud({ access: "public", fields: { id: { read: true }, title: { read: true, create: true } }, }), ) .use(comments({ table: commentsTable })); ``` This automatically mounts the standard CRUD REST endpoints for the comment resource (under the service prefix, e.g. `/api`): - `POST /api/pageComment` (create) - `POST /api/pageComment/query` (list) - `POST /api/pageComment/count` and `POST /api/pageComment/exists` - `GET /api/pageComment/:id` (public), `PATCH /api/pageComment/:id` and `DELETE /api/pageComment/:id` (authenticated) The recipe has no per-comment ownership, so any authenticated user can edit or delete any comment. If comments should be editable only by their author, add an owner column to the table and return an ownership `scope` from the access policy, alongside `authorize`. --- # Authoring Plugins Create custom service and resource plugins with scoped routes and typed APIs. Source: /docs/plugins/build-plugin Plugins package modular capabilities into reusable TypeScript descriptors that install onto services or resources. ## Authoring a Service Plugin (`defineServicePlugin`) A service plugin registers actions and raw HTTP endpoints onto a host service using `setup({ service, routes })`: ```ts title="src/plugins/greeting.ts" import { defineServicePlugin, t, type PluginRoutes } from "@brickkit/core"; export interface GreetingOptions { salutation?: string; routes?: PluginRoutes<"app" | "service">; } export function greeting(options: GreetingOptions = {}) { const salutation = options.salutation ?? "Hello"; return defineServicePlugin({ name: "example/greeting", routes: options.routes, setup({ service, routes }) { // 1. Register an action onto the service mesh const hello = routes.action({ name: "hello", method: "GET", path: `/${service.name}/hello`, input: t.Object({ name: t.String({ minLength: 1 }), }), output: t.String(), execute: ({ input }) => `${salutation}, ${input.name}!`, }); // 2. Register a raw HTML endpoint (outside the JSON action mesh) routes.endpoint({ name: "welcome", method: "GET", path: `/${service.name}/welcome.html`, handler: async ({ set }) => { set.headers["content-type"] = "text/html; charset=utf-8"; return `

${salutation}!

`; }, }); // Return a typed API namespace for callers return { hello, salutation, }; }, }); } ``` ### Installing the Service Plugin Install onto a service using `service.use()`: ```ts title="src/services/greeter.ts" import { defineService } from "@brickkit/core"; import { greeting } from "../plugins/greeting"; export const greeterService = defineService("greeter"); // Install the plugin export const api = greeterService.use(greeting({ salutation: "Welcome" })); // In-process TypeScript calls are fully typed: const message = await api.hello({ input: { name: "Ada" } }); console.log(message); // "Welcome, Ada!" ``` ## Authoring a Resource Plugin (`defineResourcePlugin`) A resource plugin attaches behavior to a specific database table and entity: ```ts title="src/plugins/slugify.ts" import { defineResourcePlugin, eq } from "@brickkit/core"; export function slugLookup(options: { slugColumn: any }) { return defineResourcePlugin({ name: "example/slugLookup", setup({ resource, service, routes }) { // Return helper methods attached directly onto the resource object return { findBySlug: async (slug: string, ctx: any) => { if (!ctx.db) throw new Error("Database required"); const rows = await ctx.db .select() .from(resource.table) .where(eq(options.slugColumn, slug)) .limit(1); return rows[0] ?? null; }, }; }, }); } ``` ### Installing the Resource Plugin Install onto a resource using `resource.use()`: ```ts title="src/services/articles.ts" const article = service .resource({ name: "article", table: articlesTable, id: articlesTable.id }) .use(slugLookup({ slugColumn: articlesTable.slug })); // The returned resource is augmented with findBySlug: const found = await article.findBySlug("my-post-slug", ctx); ``` ## Plugin Design Principles 1. **Synchronous Setup**: `setup()` must execute synchronously. Asynchronous connection initialization or network lookups are prohibited during registration. 2. **Connection Flow**: Use `ctx.db` provided during request handling. Never create persistent connection pools inside plugin constructors. 3. **Collision Avoidance**: Resource plugin contributions must not overwrite existing resource properties or prototype methods. 4. **Isolated Descriptors**: Reinstalling a plugin with a different descriptor of the same name fails. Reuse descriptors when sharing instances. ### Schema The resource-plugin example expects an application-owned `articles` table with an identity column and a slug column. Declare the table before registering the resource. A unique slug makes `findBySlug` unambiguous. #### Table Name: `articles` | Field | Type | Description | | ------ | ------ | ------------------------------- | | `id` | `text` | Article identifier. PK | | `slug` | `text` | Slug used by findBySlug. Unique | --- # Plugin Best Practices Architectural guidelines for building maintainable, safe, and isolated plugins. Source: /docs/plugins/best-practices Adhering to plugin best practices ensures your extensions maintain high performance, avoid naming collisions, and preserve security boundaries. ## 1. Keep Setup Synchronous The `setup()` function must be completely synchronous: ```ts title="src/plugins/bad-practice.ts" // ❌ Anti-pattern: Async setup functions throw an error at runtime setup: async ({ service }) => { await fetchRemoteConfig(); } // ✅ Correct: Synchronous registration of declarations setup: ({ service, routes }) => { routes.action({ ... }); } ``` Never initiate database migrations, connect to external network endpoints, or execute queries during `setup()`. Plugin installation occurs when defining services before the server boots. ## 2. Depend on Runtime Context (`ctx.db`) Plugins should not instantiate their own database connections or cache clients: ```ts title="src/plugins/good-practice.ts" // ✅ Correct: Use ctx.db injected from the host service execute: async ({ input, ctx }) => { const db = ctx.db; return db.select().from(table); }; ``` Using `ctx.db` guarantees that plugins respect transaction boundaries, tenant routing, and context isolation. ## 3. Explicit Access and Validation When registering actions inside a plugin via `routes.action()`, always specify: - TypeBox `input` and `output` schemas. - Explicit `method` and `path`. - An authorization check or clear documentation indicating the action is public. ## 4. Avoid Property Collisions on Host Resources Resource plugins return objects that are assigned directly to the host resource. Never return keys that conflict with core resource properties: - Reserved resource keys: `name`, `serviceName`, `table`, `id`, `idField`, `config`, `use`. - Avoid prototype keys: `__proto__`, `prototype`, `constructor`. Brick checks for collisions during plugin installation and throws an immediate error if a collision is detected. ## 5. Structured Error Handling When rejecting operations in plugins or hooks, throw `ActionExecutionError` with standard HTTP error codes: ```ts title="src/plugins/error-handling.ts" import { ActionExecutionError } from "@brickkit/core"; if (!isValid) { throw new ActionExecutionError( "BAD_REQUEST", "Invalid plugin parameter", 400, ); } ``` This ensures errors format into the standard Brick-TS JSON envelope expected by typed clients. --- # API Cheatsheet Quick reference of all public functions, classes, and types in Brick-TS. Source: /docs/reference/cheatsheet A quick reference of commonly used public exports from `@brickkit/core`, `@brickkit/crud`, and `@brickkit/cli`. For the full surface, see `packages/core/src/index.ts`. ## Core Primitives (`@brickkit/core`) | Export | Kind | Description | | :--------------------------------- | :------- | :------------------------------------------------------------------------------------------ | | `defineService(name, options)` | Function | Creates an isolated service domain with optional database, context resolver, and lifecycle. | | `defineAction(config)` | Function | Constructs a typed action with input/output validation, error helpers, and authorization. | | `defineResource(service, config)` | Function | Binds a Drizzle table to a service with explicit primary key identity. | | `createServiceProxy(serviceName)` | Function | Creates a type-safe proxy for calling another service in-process with context isolation. | | `brick(options)` | Function | Instantiates an Elysia HTTP server gateway precompiled with services and routes. | | `t` / `Type` | Object | TypeBox schema builder for declaring runtime input and output data shapes. | | `Static` | Type | Incurs static TypeScript compile-time types from TypeBox runtime schemas. | | `validateWithSchema(schema, data)` | Function | Directly validates arbitrary data against a TypeBox schema. | ## Database Integration (`@brickkit/core`) | Export | Kind | Description | | :--------------------------------------- | :------- | :--------------------------------------------------------------------------------- | | `defineDatabase(options)` | Function | Configures a lazy database handle for SQLite or PostgreSQL. | | `syncSchema(serviceOrTables, db?)` | Function | Explicit development/test helper to create SQLite tables from Drizzle definitions. | | `ensureResourceIndexes(db, table, opts)` | Function | Generates secondary B-Tree indexes and PostgreSQL trigram search indexes. | | `ensureSQLiteFts(db, table, searchCols)` | Function | Creates an FTS5 full-text search sidecar table with sync triggers on SQLite. | | `buildFtsMatchQuery(query)` | Function | Formats user search queries into sanitized SQLite FTS5 MATCH expressions. | | `isDatabaseHandle(value)` | Function | Type guard checking if an object is a Brick database handle. | ## Errors & Builders (`@brickkit/core`) | Export | Kind | Description | | :------------------------------ | :------- | :--------------------------------------------------------------------------------------- | | `ActionExecutionError` | Class | Standard domain error with HTTP status, machine error code, and optional details. | | `ValidationError` | Class | Thrown when TypeBox input (400) or output (500) validation fails. | | `buildErrorHelpers(errorsDef?)` | Function | Generates standard HTTP error helpers (`error.NOT_FOUND`, etc.) and custom action codes. | ## Typed Client SDK (`@brickkit/core/client`) | Export | Kind | Description | | :--------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `createBrickClient(options)` | Function | Instantiates an end-to-end typed HTTP client proxy from backend server types. | | `BrickTransportError` | Class | Client error thrown when network connectivity fails, bodies are unparsable, or responses fall outside the declared contract. Aborts propagate the native `AbortError` unwrapped. | | `BrickServerError` | Class | Client error thrown when the server responds with an unhandled 5xx status code. | | `isBrickTransportError(err)` | Function | Type guard for checking if an error originated from client transport failure. | | `isBrickServerError(err)` | Function | Type guard for checking if an error is an unexpected 5xx server failure. | | `InferActionInput` | Type | Extracts the static TypeScript input payload type for an action. | | `InferActionOutput` | Type | Extracts the static TypeScript return type for an action. | | `InferActionErrorCodes` | Type | Union of all error code strings declared by an action. | ## AI & Model Context Protocol (`@brickkit/core`) | Export | Kind | Description | | :--------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------- | | `defineTool(config)` | Function | Constructs an LLM-invocable tool. When `input` is present it must be an object schema. | | `isTool(value)` | Function | Type guard checking if an action is designated as an AI tool. | | `createMcpRegistry(options)` | Function | Collects explicitly declared tools into a Model Context Protocol registry. | | `createMcpHandler(options)` | Function | Generates an MCP-compliant JSON-RPC 2.0 HTTP POST handler from `{ registry, serverInfo }`. Non-POST requests return `405`. | | `UnknownToolError` | Class | Error thrown when an MCP client requests an unregistered tool name. | ## Plugins (`@brickkit/core`) | Export | Kind | Description | | :------------------------------------- | :------- | :------------------------------------------------------------------------------------- | | `defineServicePlugin(descriptor)` | Function | Packages reusable capabilities installed at service scope via `service.use()`. | | `defineResourcePlugin(descriptor)` | Function | Packages table-attached capabilities installed at resource scope via `resource.use()`. | | `getPluginActionRoute(action, prefix)` | Function | Retrieves the effective HTTP route configuration bound to a plugin action. | | `getPluginEndpoints(service, prefix)` | Function | Retrieves raw HTTP endpoints registered by service and resource plugins. | ## CRUD Plugin (`@brickkit/crud`) | Export | Kind | Description | | :--------------- | :-------- | :------------------------------------------------------------------------------------------------------------- | | `crud(options)` | Function | Deny-by-default resource plugin providing field-level operations, filtering, and hooks. | | `FieldPolicy` | Interface | Configuration options for column visibility (`read`, `create`, `update`, `filter`, `sort`, `schema`, `scrub`). | | `CrudOptions` | Interface | Full options object accepted by `crud()` (`fields`, `access`, `hooks`, `operations`, `routes`, limits). | | `FilterOperator` | Type | Union of allowed query operators (`eq`, `ne`, `in`, `contains`, `between`, `isNull`, etc.). | ## Secrets Management (`@brickkit/core`) | Export | Kind | Description | | :---------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | `secret(name, options?)` | Function | Declares a secret reference resolved lazily from environment variables or custom sources. | | `SecretRef` | Class | Container wrapping secret names with `.default()`, `.transform()`, `.validate()`, `.url()`, `.value()`, `.require()`, and `.isSet()` methods. | | `resolveSecrets(options?)` | Function | Eagerly validates and resolves all declared secret references. | | `overrideSecret(name, value)` | Function | Overrides a secret value in test environments. | ## CLI Commands (`@brickkit/cli`) | Command | Usage | Description | | :------------------ | :-------------------------------------------- | :--------------------------------------------------------------------- | | `brick dev` | `brick dev [entry] [--port ]` | Starts development server with live reload and route banners. | | `brick start` | `brick start [entry] [--port ]` | Starts production server without file system watching. | | `brick build` | `brick build [--outdir ]` | Compiles a default-exported `BrickApp` into an AOT Bun server bundle. | | `brick gen openapi` | `brick gen openapi [--output ]` | Extracts OpenAPI 3.1 JSON specification from services. | | `brick gen client` | `brick gen client [--output ]` | Generates a typed browser client contract file (`_brick/contract.ts`). | | `brick info` | `brick info ` | Exports the runtime service graph and access policies as JSON. | --- # Error Catalog & Troubleshooting Catalog of error codes, HTTP statuses, response envelopes, and troubleshooting advice. Source: /docs/reference/errors This catalog documents the runtime errors, HTTP status codes, error envelopes, and common troubleshooting solutions in Brick-TS. ## Error Envelopes Errors thrown from an action (including CRUD actions) are serialized by the HTTP runtime into one of three JSON shapes. `ActionExecutionError` and `ValidationError` are serialized with their `toJSON()` methods; the HTTP status is the error's `status` (falling back to `400`). ### ActionExecutionError Envelope ```json { "name": "ActionExecutionError", "code": "NOT_FOUND", "message": "User with ID 'usr_10' not found", "status": 404, "details": { "entity": "user", "id": "usr_10" } } ``` `details` is omitted from the JSON body when it is `undefined`. ### ValidationError Envelope ```json { "name": "ValidationError", "message": "Validation failed for action 'createArticle' input", "status": 400, "errors": [ { "path": "/title", "message": "Expected string length greater or equal to 3", "value": "Hi" } ] } ``` Input validation failures use status `400`; output validation failures use status `500` with the message `Validation failed for action '' output`. ### Unexpected Errors (500) Any other thrown value from an action becomes a `500` response with this body: ```json { "name": "InternalServerError", "message": "", "status": 500 } ``` > The original error's `message` is returned to the client verbatim (falling > back to `"An unexpected error occurred"` only when it is empty). Do not put > secrets or internal details in messages of errors that may escape `execute`. ### Raw Endpoint Errors Raw endpoints (`brick({ endpoints })`, `app.endpoint()`, plugin `routes.endpoint()`) serialize `ActionExecutionError` and `ValidationError` the same way. Any other error with a numeric `status` returns `{ "message", "status" }` with that status; everything else returns `{ "message", "status": 500 }` (no `name` field). ## Standard Error Codes Catalog The standard error helpers injected into `execute({ error })` and CRUD hooks throw an `ActionExecutionError` with the following codes and HTTP status codes. Each helper accepts an optional message override (defaulting to the standard message, e.g. `"Not Found"`) and optional `details`. Custom codes declared in an action's `errors` map get helpers too; their status defaults to `400` when `status` is omitted. | Code | HTTP Status | Standard Meaning | Helper Method | | :---------------------- | :------------------- | :--------------------------------------------------- | :-------------------------------------------- | | `BAD_REQUEST` | `400 Bad Request` | Client sent malformed payload or unprocessable query | `error.BAD_REQUEST(msg?, details?)` | | `UNAUTHORIZED` | `401 Unauthorized` | Missing authentication or invalid credentials | `error.UNAUTHORIZED(msg?, details?)` | | `FORBIDDEN` | `403 Forbidden` | Authenticated caller lacks permissions or role | `error.FORBIDDEN(msg?, details?)` | | `NOT_FOUND` | `404 Not Found` | Target record or entity does not exist | `error.NOT_FOUND(msg?, details?)` | | `CONFLICT` | `409 Conflict` | Unique constraint violation or state collision | `error.CONFLICT(msg?, details?)` | | `INTERNAL_SERVER_ERROR` | `500 Internal Error` | Unexpected backend or database failure | `error.INTERNAL_SERVER_ERROR(msg?, details?)` | ## Current Authorization Status Codes & Issue #53 Authorization failures are reported as follows in the current release: | Layer | Trigger | Thrown Code | HTTP Status | Source Reference | | :----- | :----------------------------------- | :------------- | :-------------- | :---------------------------- | | Action | `authorize()` returns `false` | `UNAUTHORIZED` | `403 Forbidden` | `packages/core/src/action.ts` | | CRUD | `access.authorize()` returns `false` | `FORBIDDEN` | `403 Forbidden` | `packages/crud/src/index.ts` | | CRUD | `access` object has no `authorize` | `FORBIDDEN` | `403 Forbidden` | `packages/crud/src/index.ts` | | CRUD | `create` with a non-empty `scope` | `FORBIDDEN` | `403 Forbidden` | `packages/crud/src/index.ts` | The action message is `Access denied for action ''`; the CRUD message is `Access denied` (or `Row-scoped creation requires an explicit create policy` for scoped creates). > **Known inconsistency (Issue #53)**: In the current release, an action > `authorize` rejection throws code `UNAUTHORIZED` with HTTP status `403`, while > CRUD access denial uses `FORBIDDEN`/`403`. Issue #53 tracks aligning these > codes; until it lands, clients should treat both codes as access denial. Track > progress in [GitHub Issue > #53](https://github.com/Chandraprakash-Darji/brick/issues/53). ## Common Troubleshooting Scenarios ### 1. `Validation failed for action '' output` (Status 500) - **Cause**: An action returned data that violated its declared `output` schema. Brick responds with a `ValidationError` (status 500) instead of the invalid payload. - **Fix**: Check that your `execute` function returns all properties marked as required in `output: t.Object(...)`. ### 2. `ActionExecutionError: FORBIDDEN: Access denied` (Status 403) - **Cause**: A CRUD resource's `access` object has no `authorize` function, or `access.authorize()` returned `false`. CRUD is deny-by-default (`access` is a required option). - **Fix**: Configure `access: "public"` for open resources, or implement `access: { authorize: ({ ctx }) => ... }`. ### 3. `Error: CRUD requires a configured database` - **Cause**: A resource with `crud()` was invoked on a service that did not have a database handle attached. This is a plain `Error`, so the client receives a `500` `InternalServerError` body with this message. - **Fix**: Supply a database handle when declaring the service: `defineService("name", { database: defineDatabase(...) })`. ### 4. `Error: Invalid port: . Port must be an integer between 1 and 65535` - **Cause**: `--port` or `PORT` environment variable contained an out-of-range integer or non-numeric string. - **Fix**: Ensure `PORT` is set to a valid integer between 1 and 65535. ### 5. `[Brick-TS] Error: Entry file not found: ` - **Cause**: `brick dev` or `brick start` was invoked with an explicit entry file argument that does not exist on disk. The CLI prints this message and exits with code 1. (`brick build` reports `Brick compiler: entry not found: ` instead.) - **Fix**: Verify file path spelling. --- # Context API Reference Detailed specification of BaseContext, ActionContext, and context lifecycle guarantees. Source: /docs/reference/context-api This reference documents the TypeScript interfaces, fields, and lifecycle guarantees of the Brick-TS context system. ## `BaseContext` Interface `BaseContext` is the foundational object passed to all service context resolvers and action executions: ```ts export interface BaseContext { request: Request; db: TDb; logger: Logger; traceId: string; requestId?: string; caller?: string; tenantId?: string; services?: Record; metadata?: Record; } ``` ### Properties Breakdown | Property | Type | Description | | :---------- | :------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `request` | `Request` | Standard web `Request` for the active HTTP call. In-process calls without a request receive a placeholder `Request("http://localhost")`. | | `db` | `TDb` | The database client from the service's database handle (`service.getDb()`). Undefined when the service has no database. | | `logger` | `Logger` | Logger with `.info()`, `.warn()`, `.error()`, and `.debug()` methods. The default logger prefixes output with the level (e.g. `[INFO]`). | | `traceId` | `string` | Correlation id. Over HTTP it is taken from the `x-trace-id` request header or minted (e.g. `tr_1`), and echoed back as an `x-trace-id` response header. | | `requestId` | `string \| undefined` | Request id. Over HTTP it is taken from the `x-request-id` header or minted (e.g. `req_1`), and echoed back as an `x-request-id` response header. The HTTP runtime always sets it. | | `services` | `Record` | Reserved. The HTTP runtime and default context set it to an empty object. | | `metadata` | `Record` | Free-form metadata. The HTTP action runtime sets it to `{ headers }` (the request headers). | ## Logger Interface The `logger` property on `BaseContext` adheres to: ```ts export interface Logger { info: (...args: any[]) => void; warn: (...args: any[]) => void; error: (...args: any[]) => void; debug: (...args: any[]) => void; } ``` Arguments are formatted as `console.log` would format them. While a dashboard is on (`brick dev`, or `brick start` with the [`dashboard` option](/docs/brick-cli/dev-and-start#monitoring-dashboard)), a line logged while a traced request is running goes to the dashboard (the Logs tab, and the request's own details) instead of the terminal, and so does a plain `console.log`, `info`, `warn`, `error`, or `debug`; see [Logs in the dashboard](/docs/brick-cli/dev-and-start#logs). With no dashboard, nothing is captured: both print to the console as before, as do lines logged outside a request. ## `ActionContext` `ActionContext` is the intersection of `BaseContext` and your service's custom context resolver return type: ```ts export type ActionContext< TDb = any, TServiceContext = DefaultServiceContext, > = BaseContext & TServiceContext; ``` Inside an action's `execute({ ctx })` or CRUD hook, `ctx` includes all fields from `BaseContext` plus whatever your service `context` resolver returned. ## `NarrowAuthorizedContext` When an action declares an `authorize` guard, Brick wraps the context with `NarrowAuthorizedContext`: ```ts export type NarrowAuthorizedContext = TCtx extends { user: infer U } ? [U] extends [null | undefined] ? TCtx : Omit & { user: NonNullable } : TCtx; ``` This utility narrows `ctx.user` from `User | null` to `NonNullable` at the type level. Inside `execute({ ctx })` of an action with `authorize`, you do not need optional chaining or null assertions on `ctx.user`. The narrowing is a type assertion only: it is your `authorize` function that must actually reject calls without a user. ## Context Isolation Across Service Proxies When calling actions with `createServiceProxy("targetService")` (call signature: `proxy.actionName(input, ctx?)`), Brick isolates execution contexts: 1. **Envelope allowlist**: Only `user`, `traceId`, `requestId`, and `tenantId` from the proxy's `options.context` or per-call context are forwarded, plus `caller` as described below. Per-call values take precedence over proxy defaults. Custom fields such as `session` and `isOwner` are dropped. 2. **Independent Context Resolution**: The target service receives the envelope in its resolver input and uses its own database. Resolver return values take precedence over envelope values, including explicit `null` or `undefined`. Direct action calls enforce the same envelope allowlist for caller custom fields while accepting runtime fields such as `request`. 3. **`caller` Field**: The proxy sets `ctx.caller` to `options.from` — the name of the service that created the proxy, passed to `createServiceProxy(target, { from: "..." })` — never the target service's name. When `from` is provided, any `caller` in `options.context` or the per-call context is ignored, so it cannot be spoofed. Without `from`, an explicitly supplied `caller` still passes through; otherwise `caller` is left unset. Only in-process dispatch is implemented; creating a proxy with `{ isMonolith: false }` throws when an action is called. The resolver input also includes `user?: unknown`, an identity claim. Validate it before use. `tenantId` is a claim to validate against the user's access, not proof of tenant membership. Internal proxy calls have a placeholder request without browser cookies; resolve their identity from the envelope according to the receiving service's trust policy. --- # Server & Environment Configuration Complete configuration reference for brick() server options, environment variables, and secrets. Source: /docs/reference/configuration This reference documents all configuration options for the `brick()` HTTP server runtime, CLI environment variables, and secrets management. ## `CreateServerOptions` for `brick()` When constructing the server gateway via `brick(options)`, configure the following settings: | Option | Type | Default | Description | | :--------------- | :--------------------------------- | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `port` | `number` | `undefined` | Accepted by the type but not read by `brick()`. Pass the port to `app.listen(port)` instead. | | `prefix` | `string` | `"/api"` | Base path prefix for service action routes (`${prefix}//`) and plugin routes. | | `services` | `Service[]` | All registered services | Array of services to mount. Defaults to `getGlobalRegistry().list()`. | | `title` | `string` | `"Brick-TS API Mesh"` | Application title rendered in OpenAPI 3.1 and documentation headers. | | `version` | `string` | `"1.0.0"` | Application API version in OpenAPI `info.version`. | | `description` | `string` | `undefined` | Narrative API description for OpenAPI specifications. | | `docs` | `boolean` | `true` | Enables or disables the OpenAPI specification, Scalar, and Swagger UI routes. | | `openApiPath` | `string` | `"/openapi.json"` | Path where raw OpenAPI 3.1 JSON is served. Not affected by `prefix`. | | `docsPath` | `string` | `"/docs"` | Path where Scalar interactive API documentation is mounted. Not affected by `prefix`. | | `swaggerPath` | `string` | `"/swagger"` | Path where Swagger UI documentation is mounted. Not affected by `prefix`. | | `requestLogging` | `boolean \| RequestLoggingOptions` | `true` | Logs `METHOD path → status (ms)` per request. Options: `{ enabled?: boolean; logger?: (msg: string) => void }`. | | `reference` | `boolean \| ReferenceOptions` | `false` | `true` mounts a Scalar page at `/reference`. Options mount one only with a `path`; `documents` adds OpenAPI documents to the dashboard's APIs tab. | | `compiler` | `boolean` | `true` | When true, generates specialized request handlers at server construction; `false` uses the generic handler. | | `compilation` | `CompiledApplication` | `undefined` | Precompiled handlers (used by the bundle that `brick build` emits). | | `secrets` | `{ source?, validate? }` | `undefined` | Secret source and boot validation. `resolveSecrets()` runs at construction unless `validate: false`. | | `endpoints` | `EndpointDefinition[]` | `undefined` | Raw HTTP routes mounted outside the JSON action mesh. | | `dashboard` | `DashboardOptions` | `undefined` | Read-only monitoring dashboard `brick start` serves on its own port, behind `auth`. Options: `{ port?: number; host?: string; auth?: { token?: string \| string[]; authorize?: (request: Request) => boolean \| Response \| Promise }; historyDays?: number }`. Required `auth` beyond loopback; turns on `telemetry`. See [Monitoring Dashboard](/docs/brick-cli/dev-and-start#monitoring-dashboard). | | `documentation` | `string \| DocumentationOptions` | `undefined` | Folder of `.md`/`.mdx` pages shown in the `brick dev` dashboard's Docs tab. Options: `{ dir: string; components?: string }`. Relative paths start at the app entry's folder. Needs `fumadocs-core` and `@mdx-js/mdx`. | `brick()` always mounts `GET /_health` and `GET /_brick/services` at the root, regardless of `prefix`. ## Environment Variables The framework and CLI inspect the following environment variables: ### `PORT` - **Used by**: `brick dev`, `brick start`, and compiled bundles (`dist/brick/server.js`). `brick()` itself does not read `PORT`. - **Precedence**: For `dev`/`start`, an explicit `--port ` flag wins over `PORT`, which wins over the default `4000`. Compiled bundles use `PORT`, falling back to the `--port` value given to `brick build` (default `4000`). - **Constraints**: Must be an integer between `1` and `65535`. Out-of-range values or non-numeric strings make the process exit with an error. ## Secrets Management (`@brickkit/core`) Brick-TS includes a secret referencing utility. Each `secret()` call registers a reference; values resolve from `process.env` (or a custom `SecretSource`) when you read them. A secret without a `default` is required: ```ts title="src/secrets.ts" import { secret, resolveSecrets } from "@brickkit/core"; // 1. Declare references to environment variables: export const dbPassword = secret("DB_PASSWORD"); export const apiKey = secret("STRIPE_API_KEY").default(""); // a default makes it optional export const port = secret("PORT").default("4000").transform(Number); // 2. Access secrets at runtime: const password = dbPassword.require(); // Throws if missing or invalid const key = apiKey.value(); // Returns the value, or undefined when unset // 3. Validate all declared secrets (brick() also does this at construction): resolveSecrets(); // Throws one error listing every missing required secret ``` `SecretRef` methods: `.default(value)`, `.transform(fn)`, `.validate(fn)`, `.url()`, `.value(source?)`, `.require(source?)`, and `.isSet(source?)`. `listSecrets()` returns `{ name, hasDefault, isSet }` metadata without values. ### Overriding Secrets in Tests In integration tests, override secret values without mutating `process.env`: ```ts title="test/helpers.ts" import { overrideSecret, resetSecrets } from "@brickkit/core"; overrideSecret("DB_PASSWORD", "test-secret"); // Clears all overrides AND the secret registry: resetSecrets(); ``` --- # API Reference Every function, class, and type the Brick packages export, generated from the JSDoc in their source. Source: /docs/api-reference Every function, class, interface, and type that the Brick packages export, with its signature, parameters, return value, and examples, so you do not have to read the source to find out what one does. The pages are generated from the JSDoc in the source, so they are always as current as the code. | Import | What it is | Exports | | --- | --- | --- | | [`@brickkit/core`](/docs/api-reference/core) | Services, actions, tools, resources, databases, secrets, the HTTP runtime, and the typed contract of a Brick application. | 213 | | [`@brickkit/core/client`](/docs/api-reference/core-client) | The typed HTTP client a frontend uses to call a Brick application, inferred from its contract. | 14 | | [`@brickkit/core/tanstack-query`](/docs/api-reference/core-tanstack-query) | TanStack Query options and keys for the typed client's actions. | 3 | | [`@brickkit/core/pg`](/docs/api-reference/core-pg) | Postgres databases for Brick services, with Drizzle's table builders. | 33 | | [`@brickkit/cli`](/docs/api-reference/cli) | The brick command's building blocks: the dev and production servers, contract generation, and the OpenAPI pages. | 6 | | [`@brickkit/cli/compiler`](/docs/api-reference/cli-compiler) | Compile and bind a Brick application's routes ahead of time. | 1 | | [`@brickkit/cli/vite`](/docs/api-reference/cli-vite) | The Vite plugin that generates the typed client contract and serves the dashboard. | 2 | | [`@brickkit/cli/dev-reporter`](/docs/api-reference/cli-dev-reporter) | Report a worker process's requests to a running dev dashboard. | 2 | | [`@brickkit/crud`](/docs/api-reference/crud) | CRUD actions for a resource, with access rules, filters, and hooks. | 17 | Each page lists its functions first, with a line about each, then classes, interfaces, and types. Search finds any name. Names are re-exported from where they are defined, so a type may appear on more than one page. ## Improve a description The text on these pages is the JSDoc above each export in `packages/*/src`. To fix or add one, edit the comment there: the first paragraph is the summary, `@param name - text` describes a parameter, `@returns` the result, `@example` adds a code sample, `@throws` and `@deprecated` are shown as written, and `@internal` leaves an export out of this reference. --- # @brickkit/core Services, actions, tools, resources, databases, secrets, the HTTP runtime, and the typed contract of a Brick application. Source: /docs/api-reference/core `@brickkit/core` exports 176 names: 63 functions, 8 classes, 68 interfaces, 34 types, 3 variables. It also re-exports 4 from `@sinclair/typebox` and 33 from `drizzle-orm`. ```ts import { ACTION_ACCESS_KEY, ActionExecutionError, attachDatabase } from "@brickkit/core"; ``` ## Functions | Function | Description | | --- | --- | | [`attachDatabase`](#attachdatabase) | Attach a defined database. | | [`bindCompiledApplication`](#bindcompiledapplication) | Build artifact binder: consumes emitted functions, with no runtime handler code generation. | | [`brick`](#brick) | Create the HTTP application: an Elysia app that serves every service's actions (and the tools that opt in with `http: true`) as routes, with `/_health`, an Ope… | | [`buildErrorHelpers`](#builderrorhelpers) | The `error` helpers an action receives: one function per error code that throws an `ActionExecutionError` with that code and status. | | [`buildFtsMatchQuery`](#buildftsmatchquery) | Builds an FTS5 MATCH condition for a resource search term. | | [`compileBrickApplication`](#compilebrickapplication) | Startup compiler: analyzes once, then binds live callbacks. | | [`compileRoutePlan`](#compilerouteplan) | Work out, once at startup, everything an action's route needs to answer a request: where its input comes from (body, query, or path), the validators, the autho… | | [`createActionHandler`](#createactionhandler) | An Elysia handler for one action: `compileRoutePlan` and `executeCompiledRoute` together. | | [`createBrickClient`](#createbrickclient) | Create an Eden/tRPC-style typed client for the application contract `TApp`. | | [`createDatabase`](#createdatabase) | Creates a database instance based on the provided configuration. | | [`createEndpointHandler`](#createendpointhandler) | Wrap a raw handler with trace ids, logger, db, and mesh-consistent error mapping. | | [`createMcpHandler`](#createmcphandler) | Stateless MCP JSON-RPC POST handler. | | [`createMcpRegistry`](#createmcpregistry) | Collect only explicitly defined tools, without exposing ordinary actions. | | [`createPostgresDatabase`](#createpostgresdatabase) | Creates a PostgreSQL Drizzle instance using postgres-js. | | [`createServiceProxy`](#createserviceproxy) | A typed way to call another service's actions in-process. | | [`createSQLiteDatabase`](#createsqlitedatabase) | Creates a zero-config SQLite database using Bun's native `bun:sqlite` engine and Drizzle ORM. | | [`defineAction`](#defineaction) | Define an action: a typed, validated function that Brick serves over HTTP, runs in-process, and exposes to the typed client and OpenAPI. | | [`defineDatabase`](#definedatabase) | Declare a service's database: its engine, its Drizzle tables, and how to reach it. | | [`defineResource`](#defineresource) | Declares a plugin host without registering actions or changing the schema. | | [`defineResourcePlugin`](#defineresourceplugin) | Define a plugin that installs onto a resource (a table and its entity) with `resource.use(plugin)`. | | [`defineService`](#defineservice) | Declare a service: a named group of actions, tools, and resources with its own database and request context. | | [`defineServicePlugin`](#defineserviceplugin) | Define a plugin that installs onto a service with `service.use(plugin)`. | | [`defineTool`](#definetool) | Define a tool: an action that AI agents can call through MCP. | | [`emitHandlerFactory`](#emithandlerfactory) | Emits framework code only. | | [`endpointPaths`](#endpointpaths) | OpenAPI `paths` fragment for declared endpoints (raw HTML included). | | [`ensureResourceIndexes`](#ensureresourceindexes) | Ensures secondary indexes exist for a resource table. | | [`ensureSQLiteFts`](#ensuresqlitefts) | Creates the FTS5 sidecar table + sync triggers + backfills existing rows. | | [`executeCompiledRoute`](#executecompiledroute) | Answer one request for a compiled route: read the input, run the action, and return its output or the error response, with the `x-trace-id` and `x-request-id` … | | [`generateOpenApiSpec`](#generateopenapispec) | Build the OpenAPI 3.1 document for the services: one path per action route, with request and response schemas from the actions' TypeBox schemas, their declared… | | [`generateSQLiteIndexDdl`](#generatesqliteindexddl) | Generates idempotent secondary index DDL for a Drizzle table. | | [`generateSQLiteTableDdl`](#generatesqlitetableddl) | Generates an idempotent SQLite CREATE TABLE IF NOT EXISTS definition directly from a Drizzle table schema. | | [`getActionAccess`](#getactionaccess) | How an action is protected: `"public"` when nothing checks the caller, `"authorized"` when an `authorize` does, and `"scoped"` when the action also limits what… | | [`getCompiledCheck`](#getcompiledcheck) | The compiled validator for a TypeBox schema. | | [`getGlobalRegistry`](#getglobalregistry) | The registry every `defineService` adds to: it is how `brick()`, the OpenAPI generator, `createServiceProxy`, and the dashboard find services by name. | | [`getPluginActionRoute`](#getpluginactionroute) | Where a plugin mounted an action over HTTP, for the OpenAPI generator, the compiler, and the typed client. | | [`getPluginEndpoints`](#getpluginendpoints) | The raw HTTP endpoints plugins registered on a service, ready to mount on the application. | | [`hoistAnchoredSchemas`](#hoistanchoredschemas) | TypeBox expresses recursive schemas with bare `$id`/`$ref` anchors (`\{ $id: "T0", ..., \{ $ref: "T0" \} \}`). | | [`isBrickApp`](#isbrickapp) | Whether a value is an application made by `brick()`. | | [`isBrickServerError`](#isbrickservererror) | Whether an error from the typed client is a `BrickServerError`: the server answered with a 5xx that is not one of the contract's declared errors, and the error… | | [`isBrickTransportError`](#isbricktransporterror) | Whether an error from the typed client is a `BrickTransportError`: the request got no meaningful answer from the server (a network failure, a body that was not… | | [`isDatabaseHandle`](#isdatabasehandle) | Whether a value is a database handle made by `defineDatabase`. | | [`isTool`](#istool) | Whether a value was made with `defineTool`, as opposed to `defineAction`. | | [`listSecrets`](#listsecrets) | Metadata for introspection (values never exposed — future dashboard use). | | [`loadCompiledBrickApp`](#loadcompiledbrickapp) | Build artifact bootstrap: bind generated handlers while evaluating the original app. | | [`overrideSecret`](#overridesecret) | Test/session override for one secret (`undefined` clears the override). | | [`overrideSecrets`](#overridesecrets) | Bulk overrides (test/session scoped). | | [`referencePlugin`](#referenceplugin) | Standalone Elysia plugin serving GET <path> with the Scalar HTML page. | | [`registerReferenceRoute`](#registerreferenceroute) | Mounts the Scalar reference route onto an existing app. | | [`requestLoggingPlugin`](#requestloggingplugin) | Elysia plugin that logs each request as `METHOD path → status (elapsedms)`. | | [`requireBrickApp`](#requirebrickapp) | Check that a module's default export is an application ready to be served, for tools that load an entry file. | | [`resetGlobalRegistry`](#resetglobalregistry) | Start over with an empty global registry. | | [`resetSecrets`](#resetsecrets) | Clear registry and overrides. | | [`resolveEndpointDb`](#resolveendpointdb) | Resolve the db for an endpoint: explicit service first, else first service holding one. | | [`resolveSecrets`](#resolvesecrets) | Validate every registered secret at boot. | | [`scalarDocsHTML`](#scalardocshtml) | Renders the Scalar API reference page for the given OpenAPI URL. | | [`secret`](#secret) | Declare a secret. | | [`setActionAccess`](#setactionaccess) | Record how an action is protected, for tools that cannot read it from the configuration: plugins such as `crud` enforce access inside `execute`, so the action … | | [`swaggerDocsHTML`](#swaggerdocshtml) | Renders the Swagger UI page. | | [`syncSchema`](#syncschema) | Explicit SQLite development/test schema preparation helper. | | [`toErrorResponse`](#toerrorresponse) | Turn what an action threw into the JSON body of its HTTP response, and record the failure on the active trace. | | [`toOpenApiPath`](#toopenapipath) | `/p/:slug` → `/p/\{slug\}` for OpenAPI. | | [`traceRoute`](#traceroute) | Wrap a raw route handler so each of its requests gets a server span. | | [`validateWithSchema`](#validatewithschema) | Check a value against a schema, the way actions check their input and output. | ### attachDatabase ```ts function attachDatabase( service: Service, database: DatabaseHandle, ): TDb ``` Attach a defined database. Schema changes remain the caller's responsibility. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service)`` | | | `database` | [`DatabaseHandle`](#databasehandle)`` | | **Returns** `TDb` *Source: [packages/core/src/db/index.ts:93](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L93)* ### bindCompiledApplication ```ts function bindCompiledApplication( ir: BrickIR, services: Service[], factories: readonly HandlerFactory[], ): CompiledApplication ``` Build artifact binder: consumes emitted functions, with no runtime handler code generation. **Parameters** | Name | Type | Description | | --- | --- | --- | | `ir` | [`BrickIR`](#brickir) | | | `services` | [`Service`](#service)`[]` | | | `factories` | `readonly` [`HandlerFactory`](#handlerfactory)`[]` | | **Returns** [`CompiledApplication`](#compiledapplication) *Source: [packages/core/src/compiler.ts:103](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler.ts#L103)* ### brick ```ts function brick(options: CreateServerOptions = {}): BrickApp ``` Create the HTTP application: an Elysia app that serves every service's actions (and the tools that opt in with `http: true`) as routes, with `/_health`, an OpenAPI document at `/openapi.json`, Scalar at `/docs`, Swagger UI at `/swagger`, and the shape of the services at `/_brick/services`. Services come from the global registry unless `services` is given. The app does not listen by itself: export it from your entry file, where `brick dev`, `brick start`, and the Vite plugin load it, or call `app.listen(port)` in your own server bootstrap. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options?` | [`CreateServerOptions`](#createserveroptions) default `{}` | `prefix` for action routes (default `/api`), `services`, `title`, `version`, and `description` for the OpenAPI document, `docs` and the paths of its pages, `requestLogging`, `reference`, `endpoints`, `secrets`, `telemetry`, `documentation`, and `dashboard`. See `CreateServerOptions`. | **Returns** [`BrickApp`](#brickapp) — The app, with `definition` (its services, prefix, and routes), `endpoint()` to add raw routes, and `listEndpoints()` to list them. **Throws** - When a required secret is missing, or `documentation` or `dashboard` is invalid. **Example** ```ts import { brick } from "@brickkit/core"; import { pagesService } from "./services/pages"; export default brick({ services: [pagesService], title: "Pages API", telemetry: true, }); ``` *Source: [packages/core/src/server.ts:115](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/server.ts#L115)* ### buildErrorHelpers ```ts function buildErrorHelpers< TErrors extends Record, >( errorsDef?: TErrors, ): ErrorBuilder ``` The `error` helpers an action receives: one function per error code that throws an `ActionExecutionError` with that code and status. The standard HTTP codes (`BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `INTERNAL_SERVER_ERROR`) are always there; the action's own `errors` add to them or replace one. **Parameters** | Name | Type | Description | | --- | --- | --- | | `errorsDef?` | `TErrors` | The action's `errors`: for each code, its `message` and `status` (default 400). | **Returns** [`ErrorBuilder`](#errorbuilder)`` — Helpers that take an optional message, `details`, and `ErrorOptions`, and always throw. *Source: [packages/core/src/action.ts:70](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/action.ts#L70)* ### buildFtsMatchQuery ```ts function buildFtsMatchQuery(search: string): string | null ``` Builds an FTS5 MATCH condition for a resource search term. Returns null when the term has no usable tokens or FTS errors. Caller falls back to LIKE in that case. **Parameters** | Name | Type | Description | | --- | --- | --- | | `search` | `string` | | **Returns** `string \| null` *Source: [packages/core/src/db/index.ts:342](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L342)* ### compileBrickApplication ```ts function compileBrickApplication( options: { services: Service[]; prefix?: string; mode?: "specialized" | "generic"; }, ): CompiledApplication ``` Startup compiler: analyzes once, then binds live callbacks. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | `{ services:` [`Service`](#service)`[]; prefix?: string; mode?: "specialized" \| "generic"; }` | | **Returns** [`CompiledApplication`](#compiledapplication) *Source: [packages/core/src/compiler.ts:79](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler.ts#L79)* ### compileRoutePlan ```ts function compileRoutePlan( service: Service, action: Action, routePath: string, method?: string, ): CompiledRoutePlan ``` Work out, once at startup, everything an action's route needs to answer a request: where its input comes from (body, query, or path), the validators, the authorization and execute functions, and its logger. Low level: `brick()` does this for every route. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service) | The service the action belongs to. | | `action` | [`Action`](#action) | The action. | | `routePath` | `string` | The route's path. | | `method?` | `string` | The HTTP method (POST by default). | **Returns** [`CompiledRoutePlan`](#compiledrouteplan) — The plan `executeCompiledRoute` runs. *Source: [packages/core/src/action-handler.ts:38](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/action-handler.ts#L38)* ### createActionHandler ```ts function createActionHandler( service: Service, action: Action, routePath: string, method?: string, ): (elysiaCtx: any) => Promise ``` An Elysia handler for one action: `compileRoutePlan` and `executeCompiledRoute` together. Use it to mount an action on your own Elysia app; `brick()` does it for you. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service) | The service the action belongs to. | | `action` | [`Action`](#action) | The action. | | `routePath` | `string` | The route's path. | | `method?` | `string` | The HTTP method (POST by default). | **Returns** `(elysiaCtx: any) => Promise` — A function from the Elysia context to the response. *Source: [packages/core/src/action-handler.ts:324](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/action-handler.ts#L324)* ### createBrickClient ```ts function createBrickClient>( options: BrickClientOptions & { contract: TApp }, ): BrickClient function createBrickClient>( options: BrickClientOptions, ): BrickClient ``` Create an Eden/tRPC-style typed client for the application contract `TApp`. `TApp` is a type-only parameter: `import type { AppServer }` keeps backend implementations out of the frontend bundle. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`BrickClientOptions`](#brickclientoptions) `& { contract: TApp }` | | **Returns** [`BrickClient`](#brickclient)`` *Source: [packages/core/src/client.ts:572](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L572)* ### createDatabase ```ts function createDatabase< TSchema extends Record = Record, >( config: DatabaseConfig = {}, ): BunSQLiteDatabase | PostgresDatabase ``` Creates a database instance based on the provided configuration. Zero-config: calling `createDatabase()` returns an ultra-fast Bun-native SQLite database in `:memory:` mode wrapped with Drizzle ORM. **Parameters** | Name | Type | Description | | --- | --- | --- | | `config?` | [`DatabaseConfig`](#databaseconfig)`` default `{}` | | **Returns** `BunSQLiteDatabase \|` [`PostgresDatabase`](#postgresdatabase)`` *Source: [packages/core/src/db/index.ts:70](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L70)* ### createEndpointHandler ```ts function createEndpointHandler( def: EndpointDefinition, services: Service[], ): (elysiaCtx: any) => Promise ``` Wrap a raw handler with trace ids, logger, db, and mesh-consistent error mapping. **Parameters** | Name | Type | Description | | --- | --- | --- | | `def` | [`EndpointDefinition`](#endpointdefinition) | | | `services` | [`Service`](#service)`[]` | | **Returns** `(elysiaCtx: any) => Promise` *Source: [packages/core/src/endpoints.ts:71](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L71)* ### createMcpHandler ```ts function createMcpHandler( options: McpHandlerOptions, ): (request: Request, ctx?: Partial) => Promise ``` Stateless MCP JSON-RPC POST handler. Wrap with the app's authentication. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`McpHandlerOptions`](#mcphandleroptions) | | **Returns** `(request: Request, ctx?: Partial<`[`ActionContext`](#actioncontext)`>) => Promise` *Source: [packages/core/src/mcp.ts:89](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L89)* ### createMcpRegistry ```ts function createMcpRegistry(options: McpRegistryOptions): McpRegistry ``` Collect only explicitly defined tools, without exposing ordinary actions. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`McpRegistryOptions`](#mcpregistryoptions) | | **Returns** [`McpRegistry`](#mcpregistry) *Source: [packages/core/src/mcp.ts:33](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L33)* ### createPostgresDatabase ```ts function createPostgresDatabase< TSchema extends Record = Record, >( config: PostgresDatabaseConfig = {}, ): PostgresDatabase ``` Creates a PostgreSQL Drizzle instance using postgres-js. Static imports (both are hard dependencies in package.json) so bundlers (Nitro/Rollup) trace and ship the driver. `bun:sqlite` stays lazy in `sqlite.ts` since it only exists under Bun. **Parameters** | Name | Type | Description | | --- | --- | --- | | `config?` | [`PostgresDatabaseConfig`](#postgresdatabaseconfig)`` default `{}` | | **Returns** [`PostgresDatabase`](#postgresdatabase)`` *Source: [packages/core/src/db/postgres.ts:46](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/postgres.ts#L46)* ### createServiceProxy ```ts function createServiceProxy>( serviceName: string, options: ProxyOptions = {}, ): T ``` A typed way to call another service's actions in-process. Each property of the proxy is an action of that service: call it with the action's input, and optionally a call context. The call runs through the target's own validation and authorization, with the target's database and context (never the caller's), and shows as an `rpc` span in the trace. Only in-process dispatch exists: with `isMonolith: false` a call throws. Use the typed client to call a service over HTTP. **Parameters** | Name | Type | Description | | --- | --- | --- | | `serviceName` | `string` | The registered name of the service to call. | | `options?` | [`ProxyOptions`](#proxyoptions) default `{}` | `from` (the calling service's name, which becomes `ctx.caller` in the target and cannot be spoofed), `context` (defaults for every call), and `isMonolith`. | **Returns** `T` — A proxy typed as `T`, usually the target's actions. **Throws** - On a call, when the service or the action is not registered. **Example** ```ts import { createServiceProxy } from "@brickkit/core"; const pages = createServiceProxy<{ get(input: { id: string }): Promise }>( "pages", { from: "billing" }, ); const page = await pages.get({ id: "p_1" }); ``` *Source: [packages/core/src/rpc.ts:31](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/rpc.ts#L31)* ### createSQLiteDatabase ```ts function createSQLiteDatabase< TSchema extends Record = Record, >( config: SQLiteDatabaseConfig = {}, ): BunSQLiteDatabase ``` Creates a zero-config SQLite database using Bun's native `bun:sqlite` engine and Drizzle ORM. Defaults to an ultra-fast in-memory database (`:memory:`). Bun-only: the driver is loaded lazily so importing `@brickkit/core` on other runtimes (e.g. Node) stays safe until this function is actually called. **Parameters** | Name | Type | Description | | --- | --- | --- | | `config?` | [`SQLiteDatabaseConfig`](#sqlitedatabaseconfig)`` default `{}` | | **Returns** `BunSQLiteDatabase` *Source: [packages/core/src/db/sqlite.ts:53](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/sqlite.ts#L53)* ### defineAction ```ts function defineAction< TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined, TErrors extends Record = Record, TContext extends ActionContext = ActionContext, >( config: ActionConfigWithAuthorize, ): Action function defineAction< TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined, TErrors extends Record = Record, TContext extends ActionContext = ActionContext, >( config: ActionConfigWithoutAuthorize, ): Action ``` Define an action: a typed, validated function that Brick serves over HTTP, runs in-process, and exposes to the typed client and OpenAPI. A call runs in order: the input is validated against `input` (an invalid one is a `VALIDATION_FAILED` error, HTTP 400); `authorize` runs, if there is one; `execute` runs; and its result is validated against `output` (a mismatch is a server error, HTTP 500). Throw `error.CODE(...)` from `execute` for a declared failure or a standard one such as `error.NOT_FOUND()`. Any other thrown value becomes `INTERNAL_SERVER_ERROR` (500), with the cause kept out of the response and in the log. `authorize` returns `false` to refuse: the call fails with `UNAUTHORIZED` (401) when there is no `ctx.user`, and `FORBIDDEN` (403) when there is one. Where it is given, `execute` can rely on what it checked. **Parameters** | Name | Type | Description | | --- | --- | --- | | `config` | [`ActionConfigWithAuthorize`](#actionconfigwithauthorize)`` | `name`, `execute`, and optionally `description`, `input` and `output` (TypeBox schemas), `errors`, `authorize`, `method` and `path` to change the route (POST by default), `tags`, and `emits`. | **Returns** [`Action`](#action)`` — The action. Register it with `service.action(action)`; call it directly with `action.run(input, ctx)`. **Example** ```ts import { defineAction, t } from "@brickkit/core"; export const getPage = defineAction({ name: "get", method: "GET", input: t.Object({ id: t.String() }), output: t.Object({ id: t.String(), title: t.String() }), errors: { PAGE_GONE: { message: "That page was removed", status: 410 } }, authorize: ({ user }) => user != null, execute: async ({ input, ctx, error }) => { const page = await findPage(ctx.db, input.id); if (!page) throw error.NOT_FOUND("No such page"); return page; }, }); ``` *Source: [packages/core/src/action.ts:144](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/action.ts#L144)* ### defineDatabase ```ts function defineDatabase< TSchema extends Record = Record, >( options?: SQLiteDefinition, ): DatabaseHandle, TSchema> function defineDatabase< TSchema extends Record = Record, >( options: PostgresDefinition, ): DatabaseHandle & { $client: Sql }, TSchema> ``` Declare a service's database: its engine, its Drizzle tables, and how to reach it. Nothing connects until the handle is used (`getDb()`), so defining one has no side effects. SQLite is the default and needs no options. Pass `engine: "postgres"` with a `url` for Postgres. Options that belong to the other engine (such as `url` for SQLite, or `path` for Postgres) are refused. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options?` | [`SQLiteDefinition`](#sqlitedefinition)`` | `engine` (`"sqlite"` or `"postgres"`), `tables`, an optional `name`, and the engine's connection settings. | **Returns** [`DatabaseHandle`](#databasehandle)`, TSchema>` — A handle to give to `defineService({ database })`. `getDb()` returns the Drizzle database. **Throws** - When the engine is unsupported, or an option does not suit the engine. **Example** ```ts import { defineDatabase } from "@brickkit/core"; import { sqliteTable, text } from "drizzle-orm/sqlite-core"; const pages = sqliteTable("pages", { id: text("id").primaryKey(), title: text("title").notNull(), }); export const database = defineDatabase({ tables: { pages } }); ``` *Source: [packages/core/src/db/define.ts:109](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L109)* ### defineResource ```ts function defineResource< TTable = any, TCtx extends ActionContext = ActionContext, const O extends ResourceTypeOptions = ResourceTypeOptions, >( service: Service, config: ResourceConfig & O, ): Resource> ``` Declares a plugin host without registering actions or changing the schema. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service)`` | | | `config` | [`ResourceConfig`](#resourceconfig)` & O` | | **Returns** [`Resource`](#resource)`>` *Source: [packages/core/src/resource.ts:13](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/resource.ts#L13)* ### defineResourcePlugin ```ts function defineResourcePlugin< S extends Service = Service, R extends Resource = Resource, any>, A extends object = object, >( definition: Omit, typeof pluginKind> & { setup: (context: ResourcePluginContext,) => A & (A extends PromiseLike ? never …, ): ResourcePlugin ``` Define a plugin that installs onto a resource (a table and its entity) with `resource.use(plugin)`. Its `setup` receives the service, the resource, and a `routes` registrar, whose routes are scoped under the resource's name, and returns the typed API that `resource.use` hands back. `crud` is built this way. **Parameters** | Name | Type | Description | | --- | --- | --- | | `definition` | `Omit<`[`ResourcePlugin`](#resourceplugin)`, typeof pluginKind> & { setup: (context:` [`ResourcePluginContext`](#resourceplugincontext)`,) => A & (A extends PromiseLike ? never : unknown); }` | A unique `name`, optional `routes` controls, and `setup`, which must be synchronous. | **Returns** [`ResourcePlugin`](#resourceplugin)`` — The plugin descriptor, frozen. **Throws** - When the name is empty or has surrounding whitespace. *Source: [packages/core/src/plugin.ts:191](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L191)* ### defineService ```ts function defineService< TDb = BunSQLiteDatabase>, TServiceContext extends Record = Record, >( name: string, options?: ServiceOptions, ): Service ``` Declare a service: a named group of actions, tools, and resources with its own database and request context. It is registered globally, so `brick()`, the OpenAPI generator, and `createServiceProxy` find it by name. Add actions with `service.action(...)`, tools with `service.tool(...)`, resources with `service.resource(...)`, and plugins with `service.use(...)`. **Parameters** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | Unique name. It is the service's segment of every action route (`/api//`) and appears in OpenAPI and the dashboard. Defining a second service with the same name replaces the first and logs a warning. | | `options?` | [`ServiceOptions`](#serviceoptions)`` | `database` (a handle from `defineDatabase`), `context` (adds fields such as the signed-in user to every action's `ctx`), and `auth`, `rateLimit`, `cache`, `resilience`, `telemetry`, and `lifecycle`. | **Returns** [`Service`](#service)`` — The service. **Example** ```ts import { defineDatabase, defineService, t } from "@brickkit/core"; const database = defineDatabase({ tables: { pages } }); export const pagesService = defineService("pages", { database, // Added to `ctx` in every action of this service. context: async ({ request }) => ({ user: await readUser(request) }), }); pagesService.action({ name: "list", input: t.Object({ limit: t.Optional(t.Number()) }), output: t.Array(t.Object({ id: t.String(), title: t.String() })), execute: ({ input, ctx }) => ctx.db.select().from(pages).limit(input.limit ?? 20), }); ``` *Source: [packages/core/src/service.ts:450](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/service.ts#L450)* ### defineServicePlugin ```ts function defineServicePlugin< S extends Service = Service, A = unknown, >( definition: Omit, typeof pluginKind> & { setup: (context: ServicePluginContext,) => A & (A extends PromiseLike ? never : unknow…, ): ServicePlugin ``` Define a plugin that installs onto a service with `service.use(plugin)`. Its `setup` receives the service and a `routes` registrar for adding actions and raw HTTP endpoints, and returns the typed API that `service.use` hands back. **Parameters** | Name | Type | Description | | --- | --- | --- | | `definition` | `Omit<`[`ServicePlugin`](#serviceplugin)`, typeof pluginKind> & { setup: (context:` [`ServicePluginContext`](#serviceplugincontext)`,) => A & (A extends PromiseLike ? never : unknown); }` | A unique `name`, optional `routes` controls that let the user move or switch off the plugin's routes, and `setup`, which must be synchronous. | **Returns** [`ServicePlugin`](#serviceplugin)`` — The plugin descriptor, frozen. **Throws** - When the name is empty or has surrounding whitespace. **Example** ```ts import { defineServicePlugin, t } from "@brickkit/core"; export const greeting = (salutation = "Hello") => defineServicePlugin({ name: "example/greeting", setup({ routes }) { const hello = routes.action({ name: "hello", input: t.Object({ name: t.String() }), output: t.String(), execute: ({ input }) => `${salutation}, ${input.name}!`, }); return { hello }; }, }); const api = greeterService.use(greeting("Welcome")); await api.hello({ input: { name: "Ada" } }); ``` *Source: [packages/core/src/plugin.ts:163](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L163)* ### defineTool ```ts function defineTool< TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined, TErrors extends Record = Record, TContext extends ActionContext = ActionContext, >( config: ToolConfigWithAuthorize, ): Tool function defineTool< TInputSchema extends TSchema | undefined = undefined, TOutputSchema extends TSchema | undefined = undefined, TErrors extends Record = Record, TContext extends ActionContext = ActionContext, >( config: ToolConfigWithoutAuthorize, ): Tool ``` Define a tool: an action that AI agents can call through MCP. It takes the same configuration as `defineAction` (validation, `authorize`, errors), plus `title`, `annotations` (the MCP hints about what the tool does), and `http` to also serve it over HTTP and in OpenAPI (off by default). **Parameters** | Name | Type | Description | | --- | --- | --- | | `config` | [`ToolConfigWithAuthorize`](#toolconfigwithauthorize)`` | An action configuration and tool options. `input` must be an object schema, since MCP passes arguments by name. | **Returns** [`Tool`](#tool)`` — The tool, which is also an action. Register it with `service.tool(tool)`. **Throws** - When `input` is not an object schema. **Example** ```ts import { defineTool, t } from "@brickkit/core"; export const echoTool = defineTool({ name: "echo", title: "Echo a message", description: "Returns the message it is given.", input: t.Object({ message: t.String() }), output: t.String(), execute: ({ input }) => `Echo: ${input.message}`, }); ``` *Source: [packages/core/src/tool.ts:33](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/tool.ts#L33)* ### emitHandlerFactory ```ts function emitHandlerFactory(route: RouteIR): string ``` Emits framework code only. User callbacks remain closures bound separately. **Parameters** | Name | Type | Description | | --- | --- | --- | | `route` | [`RouteIR`](#routeir) | | **Returns** `string` *Source: [packages/core/src/compiler/emit.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/emit.ts#L4)* ### endpointPaths ```ts function endpointPaths(defs: EndpointDefinition[]): Record ``` OpenAPI `paths` fragment for declared endpoints (raw HTML included). **Parameters** | Name | Type | Description | | --- | --- | --- | | `defs` | [`EndpointDefinition`](#endpointdefinition)`[]` | | **Returns** `Record` *Source: [packages/core/src/endpoints.ts:152](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L152)* ### ensureResourceIndexes ```ts function ensureResourceIndexes( db: any, table: any, opts: { ownerField?: string; sortFields?: string[]; searchFields?: string[]; } = {}, ): void ``` Ensures secondary indexes exist for a resource table. SQLite: B-tree indexes for owner scope + sort + composite hot path. Postgres: same B-trees plus pg_trgm GIN indexes for substring search. Best-effort: failures warn and continue (e.g. read-only replicas). **Parameters** | Name | Type | Description | | --- | --- | --- | | `db` | `any` | | | `table` | `any` | | | `opts?` | `{ ownerField?: string; sortFields?: string[]; searchFields?: string[]; }` default `{}` | | *Source: [packages/core/src/db/index.ts:204](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L204)* ### ensureSQLiteFts ```ts function ensureSQLiteFts(db: any, table: any, searchCols: string[]): void ``` Creates the FTS5 sidecar table + sync triggers + backfills existing rows. Idempotent; safe to call on every resource registration. No-op on Postgres (relies on pg_trgm instead) and when searchCols is empty. **Parameters** | Name | Type | Description | | --- | --- | --- | | `db` | `any` | | | `table` | `any` | | | `searchCols` | `string[]` | | *Source: [packages/core/src/db/index.ts:278](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L278)* ### executeCompiledRoute ```ts function executeCompiledRoute( plan: CompiledRoutePlan, elysiaCtx: any, ): Promise ``` Answer one request for a compiled route: read the input, run the action, and return its output or the error response, with the `x-trace-id` and `x-request-id` headers set. Low level: `brick()` mounts this for every route. **Parameters** | Name | Type | Description | | --- | --- | --- | | `plan` | [`CompiledRoutePlan`](#compiledrouteplan) | From `compileRoutePlan`. | | `elysiaCtx` | `any` | The Elysia request context: `params`, `body`, `query`, `headers`, `request`, and `set`. | **Returns** `Promise` — The action's output, or an error envelope with the status set on `set`. *Source: [packages/core/src/action-handler.ts:149](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/action-handler.ts#L149)* ### generateOpenApiSpec ```ts function generateOpenApiSpec( options: OpenApiGeneratorOptions = {}, ): Record ``` Build the OpenAPI 3.1 document for the services: one path per action route, with request and response schemas from the actions' TypeBox schemas, their declared errors, tags by service, and a bearer security scheme where any action needs authorization. `brick()` calls this for `/openapi.json`; call it yourself to write the document to a file or feed another tool. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options?` | [`OpenApiGeneratorOptions`](#openapigeneratoroptions) default `{}` | `title`, `version`, and `description` for the `info` block, `prefix` (default `/api`), and the `services` or `registry` to describe (the global registry by default). | **Returns** `Record` — The document as a plain object. *Source: [packages/core/src/openapi.ts:114](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/openapi.ts#L114)* ### generateSQLiteIndexDdl ```ts function generateSQLiteIndexDdl( table: any, opts: { ownerField?: string; sortFields?: string[] } = {}, ): string[] ``` Generates idempotent secondary index DDL for a Drizzle table. Covers owner scoping, sort columns, and exact-match filters. UNIQUE columns already carry an implicit index — skipped here. FTS/trigram indexes are handled separately by the search layer. **Parameters** | Name | Type | Description | | --- | --- | --- | | `table` | `any` | | | `opts?` | `{ ownerField?: string; sortFields?: string[] }` default `{}` | | **Returns** `string[]` *Source: [packages/core/src/db/index.ts:138](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L138)* ### generateSQLiteTableDdl ```ts function generateSQLiteTableDdl(table: any): string ``` Generates an idempotent SQLite CREATE TABLE IF NOT EXISTS definition directly from a Drizzle table schema. **Parameters** | Name | Type | Description | | --- | --- | --- | | `table` | `any` | | **Returns** `string` *Source: [packages/core/src/db/index.ts:105](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L105)* ### getActionAccess ```ts function getActionAccess(action: Action | object): ActionAccessInfo ``` How an action is protected: `"public"` when nothing checks the caller, `"authorized"` when an `authorize` does, and `"scoped"` when the action also limits what rows the caller can reach. It reads what a plugin recorded with `setActionAccess`, else the action's own `authorize`, and keeps the policy consistent with the flags. **Parameters** | Name | Type | Description | | --- | --- | --- | | `action` | [`Action`](#action) `\| object` | An action, or any object a plugin described. | **Returns** [`ActionAccessInfo`](#actionaccessinfo) — `hasAuthorize`, `hasScope`, and `policy`. *Source: [packages/core/src/access.ts:48](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/access.ts#L48)* ### getCompiledCheck ```ts function getCompiledCheck(schema: T): TypeCheck ``` The compiled validator for a TypeBox schema. Compiling is slow and checking is fast, so each schema is compiled once and kept for as long as the schema object lives. **Parameters** | Name | Type | Description | | --- | --- | --- | | `schema` | `T` | A TypeBox schema. | **Returns** `TypeCheck` — Its `TypeCheck`, with `Check(value)` and `Errors(value)`. *Source: [packages/core/src/typebox.ts:18](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/typebox.ts#L18)* ### getGlobalRegistry ```ts function getGlobalRegistry(): ServiceRegistry ``` The registry every `defineService` adds to: it is how `brick()`, the OpenAPI generator, `createServiceProxy`, and the dashboard find services by name. **Returns** [`ServiceRegistry`](#serviceregistry) — The current global registry. *Source: [packages/core/src/registry.ts:98](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/registry.ts#L98)* ### getPluginActionRoute ```ts function getPluginActionRoute( action: Action, prefix = "/api", ): false | { path: string; method: PluginRouteMethod; clientPath: string[] | undefined; plugin: string; resource: string | undefined; } | undefined ``` Where a plugin mounted an action over HTTP, for the OpenAPI generator, the compiler, and the typed client. **Parameters** | Name | Type | Description | | --- | --- | --- | | `action` | [`Action`](#action)`` | An action. | | `prefix?` | `string` default `"/api"` | The application's route prefix (default `/api`). | **Returns** `false \| { path: string; method:` [`PluginRouteMethod`](#pluginroutemethod)`; clientPath: string[] \| undefined; plugin: string; resource: string \| undefined; } \| undefined` — `undefined` when no plugin bound the action (it uses its own route), `false` when the plugin's route was switched off, otherwise the route's `path` and `method`, the `clientPath` the typed client uses, and the owning `plugin` and `resource`. *Source: [packages/core/src/plugin.ts:323](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L323)* ### getPluginEndpoints ```ts function getPluginEndpoints( service: Service, prefix = "/api", ): { path: string; method: PluginRouteMethod; service: Service; summary: string | undefined; description: string | undefined; tags: string[] | undefined… ``` The raw HTTP endpoints plugins registered on a service, ready to mount on the application. Each handler resolves the service's context before it runs. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service)`` | The service plugins were installed on. | | `prefix?` | `string` default `"/api"` | The application's route prefix (default `/api`). | **Returns** `{ path: string; method:` [`PluginRouteMethod`](#pluginroutemethod)`; service:` [`Service`](#service)`; summary: string \| undefined; description: string \| undefined; tags: string[] \| undefined; handler: (context: Omit<`[`PluginEndpointContext`](#pluginendpointcontext)`, "ctx">) => …` — The enabled endpoints: `path`, `method`, `handler`, and the `summary`, `description`, and `tags` for OpenAPI. *Source: [packages/core/src/plugin.ts:350](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L350)* ### hoistAnchoredSchemas ```ts function hoistAnchoredSchemas(spec: Record): string[] ``` TypeBox expresses recursive schemas with bare `$id`/`$ref` anchors (`{ $id: "T0", ..., { $ref: "T0" } }`). OpenAPI interprets a bare `$ref` as an external file reference, so downstream tooling tries to load a file named `T0` and fails. Hoist every anchored subschema into `#/components/schemas/` and rewrite the refs to local pointers. First definition wins when one `$id` is declared twice. **Parameters** | Name | Type | Description | | --- | --- | --- | | `spec` | `Record` | | **Returns** `string[]` *Source: [packages/core/src/openapi.ts:47](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/openapi.ts#L47)* ### isBrickApp ```ts function isBrickApp(app: unknown): app is BrickApp ``` Whether a value is an application made by `brick()`. It checks for the methods and `definition` a `BrickApp` has, so it also recognizes one built by another copy of the package. **Parameters** | Name | Type | Description | | --- | --- | --- | | `app` | `unknown` | Anything, such as the default export of an entry file. | **Returns** `app is` [`BrickApp`](#brickapp) — `true` for a `BrickApp`, narrowing its type. *Source: [packages/core/src/app-entry.ts:11](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/app-entry.ts#L11)* ### isBrickServerError ```ts function isBrickServerError(err: unknown): err is BrickServerError ``` Whether an error from the typed client is a `BrickServerError`: the server answered with a 5xx that is not one of the contract's declared errors, and the error carries its `status`, `url`, and `body`. **Parameters** | Name | Type | Description | | --- | --- | --- | | `err` | `unknown` | What a client call threw. | **Returns** `err is` [`BrickServerError`](#brickservererror) — `true` for a server error, narrowing its type. *Source: [packages/core/src/client.ts:187](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L187)* ### isBrickTransportError ```ts function isBrickTransportError(err: unknown): err is BrickTransportError ``` Whether an error from the typed client is a `BrickTransportError`: the request got no meaningful answer from the server (a network failure, a body that was not JSON, or a 4xx outside the declared contract). **Parameters** | Name | Type | Description | | --- | --- | --- | | `err` | `unknown` | What a client call threw. | **Returns** `err is` [`BrickTransportError`](#bricktransporterror) — `true` for a transport error, narrowing its type. *Source: [packages/core/src/client.ts:173](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L173)* ### isDatabaseHandle ```ts function isDatabaseHandle(value: unknown): value is DatabaseHandle ``` Whether a value is a database handle made by `defineDatabase`. **Parameters** | Name | Type | Description | | --- | --- | --- | | `value` | `unknown` | Anything. | **Returns** `value is` [`DatabaseHandle`](#databasehandle)`` — `true` for a handle, narrowing its type. *Source: [packages/core/src/db/define.ts:73](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L73)* ### isTool ```ts function isTool(value: unknown): value is Tool ``` Whether a value was made with `defineTool`, as opposed to `defineAction`. **Parameters** | Name | Type | Description | | --- | --- | --- | | `value` | `unknown` | Anything. | **Returns** `value is` [`Tool`](#tool)`` — `true` for a tool, narrowing its type. *Source: [packages/core/src/tool.ts:82](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/tool.ts#L82)* ### listSecrets ```ts function listSecrets( opts: { source?: SecretSource } = {}, ): Array<{ name: string; hasDefault: boolean; isSet: boolean }> ``` Metadata for introspection (values never exposed — future dashboard use). **Parameters** | Name | Type | Description | | --- | --- | --- | | `opts?` | `{ source?:` [`SecretSource`](#secretsource) `}` default `{}` | | **Returns** `Array<{ name: string; hasDefault: boolean; isSet: boolean }>` *Source: [packages/core/src/secrets.ts:196](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L196)* ### loadCompiledBrickApp ```ts function loadCompiledBrickApp( load: () => Promise<{ default: unknown }>, ir: BrickIR, bind: ApplicationBinder, ): Promise ``` Build artifact bootstrap: bind generated handlers while evaluating the original app. **Parameters** | Name | Type | Description | | --- | --- | --- | | `load` | `() => Promise<{ default: unknown }>` | | | `ir` | [`BrickIR`](#brickir) | | | `bind` | `ApplicationBinder` | | **Returns** `Promise<`[`BrickApp`](#brickapp)`>` *Source: [packages/core/src/compiler.ts:28](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler.ts#L28)* ### overrideSecret ```ts function overrideSecret(name: string, value: string | undefined): void ``` Test/session override for one secret (`undefined` clears the override). **Parameters** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `value` | `string \| undefined` | | *Source: [packages/core/src/secrets.ts:208](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L208)* ### overrideSecrets ```ts function overrideSecrets(values: Record): void ``` Bulk overrides (test/session scoped). **Parameters** | Name | Type | Description | | --- | --- | --- | | `values` | `Record` | | *Source: [packages/core/src/secrets.ts:213](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L213)* ### referencePlugin ```ts function referencePlugin(opts: ReferenceOptions = {}): Elysia ``` Standalone Elysia plugin serving GET <path> with the Scalar HTML page. **Parameters** | Name | Type | Description | | --- | --- | --- | | `opts?` | [`ReferenceOptions`](#referenceoptions) default `{}` | | **Returns** `Elysia` *Source: [packages/core/src/docs.ts:72](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L72)* ### registerReferenceRoute ```ts function registerReferenceRoute( app: Elysia, opts: ReferenceOptions = {}, ): Elysia ``` Mounts the Scalar reference route onto an existing app. **Parameters** | Name | Type | Description | | --- | --- | --- | | `app` | `Elysia` | | | `opts?` | [`ReferenceOptions`](#referenceoptions) default `{}` | | **Returns** `Elysia` *Source: [packages/core/src/docs.ts:85](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L85)* ### requestLoggingPlugin ```ts function requestLoggingPlugin(opts: RequestLoggingOptions = {}): Elysia ``` Elysia plugin that logs each request as `METHOD path → status (elapsedms)`. Timing is tracked per-Request via a WeakMap. The onError hook only logs and returns undefined so existing error mapping is preserved. Logging only — no CORS headers are set here. Hooks are registered with the "global" scope so they propagate to the parent app when mounted via `app.use(...)` — Elysia scopes plain instance hooks to the plugin's own routes. Timing is captured in beforeHandle because request hooks are not propagated from a used instance. Unmatched routes (no beforeHandle) fall back to Date.now(). **Parameters** | Name | Type | Description | | --- | --- | --- | | `opts?` | [`RequestLoggingOptions`](#requestloggingoptions) default `{}` | | **Returns** `Elysia` *Source: [packages/core/src/request-logger.ts:41](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/request-logger.ts#L41)* ### requireBrickApp ```ts function requireBrickApp(app: unknown): BrickApp ``` Check that a module's default export is an application ready to be served, for tools that load an entry file. **Parameters** | Name | Type | Description | | --- | --- | --- | | `app` | `unknown` | The default export of an entry file. | **Returns** [`BrickApp`](#brickapp) — The app. **Throws** - When it is not a `BrickApp`, or it already called `.listen()`: the entry must only build the app, and leave listening to the server bootstrap. *Source: [packages/core/src/app-entry.ts:39](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/app-entry.ts#L39)* ### resetGlobalRegistry ```ts function resetGlobalRegistry(): void ``` Start over with an empty global registry. For tests, which define the same services again in each case; an application never needs it. *Source: [packages/core/src/registry.ts:106](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/registry.ts#L106)* ### resetSecrets ```ts function resetSecrets(): void ``` Clear registry and overrides. Test-only. *Source: [packages/core/src/secrets.ts:222](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L222)* ### resolveEndpointDb ```ts function resolveEndpointDb( service: Service | string | undefined, services: Service[], ): any ``` Resolve the db for an endpoint: explicit service first, else first service holding one. **Parameters** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service)` \| string \| undefined` | | | `services` | [`Service`](#service)`[]` | | **Returns** `any` *Source: [packages/core/src/endpoints.ts:52](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L52)* ### resolveSecrets ```ts function resolveSecrets( opts: { source?: SecretSource } = {}, ): Record ``` Validate every registered secret at boot. Throws a single error listing all missing required secrets. Returns the resolved values. **Parameters** | Name | Type | Description | | --- | --- | --- | | `opts?` | `{ source?:` [`SecretSource`](#secretsource) `}` default `{}` | | **Returns** `Record` *Source: [packages/core/src/secrets.ts:176](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L176)* ### scalarDocsHTML ```ts function scalarDocsHTML(specUrl: string, title: string): string ``` Renders the Scalar API reference page for the given OpenAPI URL. **Parameters** | Name | Type | Description | | --- | --- | --- | | `specUrl` | `string` | | | `title` | `string` | | **Returns** `string` *Source: [packages/core/src/docs.ts:48](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L48)* ### secret ```ts function secret(name: string, options: SecretOptions = {}): SecretRef ``` Declare a secret. Idempotent: re-declaring the same name returns the original ref (first declaration wins) so module re-evaluation is safe. **Parameters** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `options?` | [`SecretOptions`](#secretoptions) default `{}` | | **Returns** [`SecretRef`](#secretref)`` *Source: [packages/core/src/secrets.ts:161](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L161)* ### setActionAccess ```ts function setActionAccess(action: object, info: ActionAccessInfo): void ``` Record how an action is protected, for tools that cannot read it from the configuration: plugins such as `crud` enforce access inside `execute`, so the action has no `authorize` to find. Introspection, OpenAPI, and the dashboard read what is recorded here. **Parameters** | Name | Type | Description | | --- | --- | --- | | `action` | `object` | The action to describe. | | `info` | [`ActionAccessInfo`](#actionaccessinfo) | Whether it checks authorization and whether it scopes queries, and the resulting policy. | *Source: [packages/core/src/access.ts:30](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/access.ts#L30)* ### swaggerDocsHTML ```ts function swaggerDocsHTML(specUrl: string, title: string): string ``` Renders the Swagger UI page. **Parameters** | Name | Type | Description | | --- | --- | --- | | `specUrl` | `string` | | | `title` | `string` | | **Returns** `string` *Source: [packages/core/src/docs.ts:93](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L93)* ### syncSchema ```ts function syncSchema( tablesOrService: Map | any[] | Record | Service, dbInstance?: any, ): Promise | void ``` Explicit SQLite development/test schema preparation helper. Never called by services, resources, auth, or server startup. Not a migration runner; PostgreSQL schema management is caller-owned. **Parameters** | Name | Type | Description | | --- | --- | --- | | `tablesOrService` | `Map \| any[] \| Record \|` [`Service`](#service) | | | `dbInstance?` | `any` | | **Returns** `Promise \| void` *Source: [packages/core/src/db/index.ts:365](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L365)* ### toErrorResponse ```ts function toErrorResponse(err: unknown, context: ErrorContext): ErrorEnvelope ``` Turn what an action threw into the JSON body of its HTTP response, and record the failure on the active trace. Declared failures and validation errors keep their code, status, and details; anything else is logged and answered as a generic `INTERNAL_SERVER_ERROR` (500) so internals are never exposed. **Parameters** | Name | Type | Description | | --- | --- | --- | | `err` | `unknown` | Whatever was thrown. | | `context` | `ErrorContext` | A `logger` with `error()`, and the `requestId` to put in the body. | **Returns** [`ErrorEnvelope`](#errorenvelope) — The envelope: `name`, `code`, `message`, `status`, `requestId`, and `details` or `errors` when there are any. *Source: [packages/core/src/error-response.ts:46](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/error-response.ts#L46)* ### toOpenApiPath ```ts function toOpenApiPath(elysiaPath: string): string ``` `/p/:slug` → `/p/{slug}` for OpenAPI. **Parameters** | Name | Type | Description | | --- | --- | --- | | `elysiaPath` | `string` | | **Returns** `string` *Source: [packages/core/src/endpoints.ts:142](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L142)* ### traceRoute ```ts function traceRoute( route: string, handler: (context: RawRouteContext) => T, ): (context: RawRouteContext) => T ``` Wrap a raw route handler so each of its requests gets a server span. Use it for routes registered directly on the app, outside actions and endpoints, such as auth or webhook routes. Spans are recorded whenever a tracer provider is registered; `brick({ telemetry })` does not apply to these routes. The wrapper forwards only the fields listed in `RawRouteContext`. Elysia decides what to parse from the handler's destructuring, and forwarding the whole context would parse the body and leave `request.json()` with nothing. **Parameters** | Name | Type | Description | | --- | --- | --- | | `route` | `string` | | | `handler` | `(context: RawRouteContext) => T` | | **Returns** `(context: RawRouteContext) => T` *Source: [packages/core/src/telemetry.ts:75](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/telemetry.ts#L75)* ### validateWithSchema ```ts function validateWithSchema( schema: T | undefined, value: unknown, ): | { success: true; data: Static } | { success: false; errors: ValidationErrorItem[] } ``` Check a value against a schema, the way actions check their input and output. **Parameters** | Name | Type | Description | | --- | --- | --- | | `schema` | `T \| undefined` | A TypeBox schema, or `undefined` to accept anything. | | `value` | `unknown` | The value to check. | **Returns** `\| { success: true; data: Static } \| { success: false; errors:` [`ValidationErrorItem`](#validationerroritem)`[] }` — `{ success: true, data }`, or `{ success: false, errors }` with a `path`, `message`, `type`, and the offending `value` for each problem. *Source: [packages/core/src/typebox.ts:35](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/typebox.ts#L35)* ## Classes ### ActionExecutionError ```ts class ActionExecutionError extends Error ``` **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new ( code: string, message: string, status = 400, details?: unknown, options?: ErrorOptions, )` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly code` | `string` | | | `readonly status` | `number` | | | `readonly details?` | `unknown` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `toJSON` | `toJSON(): { name: string; code: string; message: string; status: number; details: unknown; }` | | *Source: [packages/core/src/types.ts:101](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L101)* ### BrickServerError ```ts class BrickServerError extends Error ``` The server answered 5xx without a declared `code` / `ValidationError` shape, i.e. an unexpected failure rather than a modelled domain error. **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (message: string, opts: { status: number; url: string; body: unknown })` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"server"` | | | `readonly status` | `number` | | | `readonly url` | `string` | | | `readonly body` | `unknown` | | *Source: [packages/core/src/client.ts:147](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L147)* ### BrickTransportError ```ts class BrickTransportError extends Error ``` The request never received a meaningful server answer: DNS / connection failure, a malformed body, or a 4xx response outside the declared contract (no `code` and no `ValidationError` shape). **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new ( message: string, opts: { status?: number; url: string; body?: unknown; cause?: unknown }, )` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"transport"` | | | `readonly status?` | `number` | | | `readonly url` | `string` | | | `readonly body?` | `unknown` | | | `readonly cause?` | `unknown` | | *Source: [packages/core/src/client.ts:123](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L123)* ### SecretRef ```ts class SecretRef ``` **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (name: string, options:` [`SecretOptions`](#secretoptions) `= {})` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly name` | `string` | | | `readonly options` | [`SecretOptions`](#secretoptions) | | | `hasDefault` | `boolean` | True when a default was declared (i.e. the secret is optional). | **Methods** | Name | Type | Description | | --- | --- | --- | | `default` | `default(value: string): this` | Fallback used when the source has no value. Makes the secret optional. | | `transform` | `transform(fn: (raw: string) => U): SecretRef` | Convert the raw string (e.g. `Number`, `JSON.parse`, `v === "true"`). Runs before validators; a throwing transform fails resolution. | | `validate` | `validate(fn: SecretValidator): this` | Run a custom check on the (transformed) value at every resolution. | | `url` | `url(this: SecretRef): SecretRef` | Shorthand validator: value must parse as a URL. String refs only. | | `value` | `value(source:` [`SecretSource`](#secretsource) `= envSource): T \| undefined` | Resolved (transformed, validated) value or `undefined`. Never throws on missing. | | `require` | `require(source:` [`SecretSource`](#secretsource) `= envSource): T` | Resolved value, or throws naming the missing/invalid secret. | | `isSet` | `isSet(source:` [`SecretSource`](#secretsource) `= envSource): boolean` | True when a value would resolve (override, source, or default). | *Source: [packages/core/src/secrets.ts:42](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L42)* ### ServiceImpl ```ts class ServiceImpl>, TServiceContext extends Record = Record> implements Service ``` **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (name: string, options:` [`ServiceOptions`](#serviceoptions)` = {})` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly name` | `string` | | | `readonly options` | [`ServiceOptions`](#serviceoptions)`` | | | `readonly actions` | `Map>` | | | `readonly tools` | `Map>` | | | `readonly tables` | `Map` | | | `readonly resources` | `Map & TServiceContext, {}>>` | | | `readonly preparations` | `Map` | | | `db` | `TDb \| undefined` | | | `isBuilt` | `boolean` | | | `resource` | `(config:` [`ResourceConfig`](#resourceconfig)` & TServiceContext> & TOptions & Record, neve…` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `use` | `use(plugin:` [`ServicePlugin`](#serviceplugin)`): A` | | | `build` | `build(): this` | Closes declarations after plugins have registered their actions and preparations. | | `contributePreparation` | `contributePreparation(contribution:` [`PreparationContribution`](#preparationcontribution)`): void` | | | `listPreparations` | `listPreparations():` [`PreparationContribution`](#preparationcontribution)`[]` | | | `prepareContributions` | `prepareContributions(): void` | | | `registerTable` | `registerTable(table: any): this` | | | `getResource` | `getResource( name: string, ):` [`Resource`](#resource)` & TServiceContext> \| undefined` | | | `listResources` | `listResources():` [`Resource`](#resource)` & TServiceContext>[]` | | | `action` | `action(actionOrConfig: any): any` | | | `tool` | `tool(toolOrConfig: any): any` | | | `getTool` | `getTool(name: string):` [`Tool`](#tool)` \| undefined` | | | `listTools` | `listTools():` [`Tool`](#tool)`[]` | | | `getAction` | `getAction(name: string):` [`Action`](#action)` \| undefined` | | | `listActions` | `listActions():` [`Action`](#action)`[]` | | | `getDb` | `getDb(): TDb \| undefined` | | | `setDb` | `setDb(database:` [`DatabaseHandle`](#databasehandle)`): void` | | | `resolveContext` | `resolveContext( callCtx?: Partial<`[`BaseContext`](#basecontext)` & TServiceContext>, ): Promise<`[`BaseContext`](#basecontext)` & TServiceContext>` | Resolves the complete service execution context once per incoming request. Merges framework BaseContext with the service's custom context hook. Always rebuilds context for this service to prevent caller context from overriding. | | `introspect` | `introspect():` [`ServiceSchema`](#serviceschema) | | *Source: [packages/core/src/service.ts:46](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/service.ts#L46)* ### ServiceRegistry ```ts class ServiceRegistry ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `revision` | `number` | Definition revision for invalidating serialized architecture snapshots. | **Methods** | Name | Type | Description | | --- | --- | --- | | `touch` | `touch(): void` | | | `setApplicationRoutes` | `setApplicationRoutes(application: ArchitectureApplication): void` | Remember the actual registered routes for subsequent registry exports. | | `register` | `register(service:` [`Service`](#service)`): void` | | | `get` | `get(name: string):` [`Service`](#service) `\| undefined` | | | `has` | `has(name: string): boolean` | | | `list` | `list():` [`Service`](#service)`[]` | | | `clear` | `clear(): void` | | | `exportArchitecture` | `exportArchitecture(application?: ArchitectureApplication):` [`ArchitectureSchema`](#architectureschema) | | *Source: [packages/core/src/registry.ts:9](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/registry.ts#L9)* ### UnknownToolError ```ts class UnknownToolError extends Error ``` **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (name: string)` | | *Source: [packages/core/src/mcp.ts:25](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L25)* ### ValidationError ```ts class ValidationError extends Error ``` **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (message: string, errors:` [`ValidationErrorItem`](#validationerroritem)`[] = [], status = 400)` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly code` | `"VALIDATION_FAILED"` | | | `readonly status` | `number` | | | `readonly errors` | [`ValidationErrorItem`](#validationerroritem)`[]` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `toJSON` | `toJSON(): { name: string; code: string; message: string; status: number; errors:` [`ValidationErrorItem`](#validationerroritem)`[]; }` | | *Source: [packages/core/src/types.ts:74](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L74)* ## Interfaces ### Action ```ts interface Action = Record, TContext extends ActionContext = ActionContext> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `(call)` | `(params: { input?: TInputSchema extends TSchema ? Static : any; ctx?: Partial; }): Promise : any>` | | | `name` | `string` | | | `serviceName?` | `string` | | | `plugin?` | `string` | Owning plugin descriptor name for plugin actions (e.g. `@brickkit/crud`). | | `resource?` | `string` | Owning resource name for resource-plugin actions (e.g. `page`). | | `config` | [`ActionConfig`](#actionconfig)`` | | | `outputValidationSchema?` | `TSchema` | Optional runtime-only output schema. When present, `validateOutput` and HTTP output checking validate against it instead of `config.output`, while `config.output` remains the published contract (client types, OpenAPI). Lets projected/subset responses validate at runtime without leaking optionality into the generated contract. | | `errorHelpers?` | [`ErrorBuilder`](#errorbuilder)`` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `run` | `run( input?: TInputSchema extends TSchema ? Static : any, ctx?: Partial, ): Promise : any>` | | | `execute` | `execute( params: { input?: TInputSchema extends TSchema ? Static : any; ctx?: Partial; }, ): Promise : any>` | | | `executeResolved?` | `executeResolved( params: { input?: TInputSchema extends TSchema ? Static : any; ctx: TContext; }, ): Promise : any>` | | | `validateInput` | `validateInput( input: unknown, ): \| { success: true; data: any } \| { success: false; errors:` [`ValidationErrorItem`](#validationerroritem)`[] }` | | | `validateOutput` | `validateOutput( output: unknown, ): \| { success: true; data: any } \| { success: false; errors:` [`ValidationErrorItem`](#validationerroritem)`[] }` | | *Source: [packages/core/src/types.ts:261](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L261)* ### ActionAccessInfo ```ts interface ActionAccessInfo ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `hasAuthorize` | `boolean` | | | `hasScope` | `boolean` | | | `policy` | [`ActionAccessPolicy`](#actionaccesspolicy) | | *Source: [packages/core/src/access.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/access.ts#L4)* ### ActionConfigWithAuthorize ```ts interface ActionConfigWithAuthorize = Record, TContext extends ActionContext = ActionContext> extends BaseActionConfig ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `authorize` | `(params: { user?: TContext extends { user: infer U } ? U : any; input: TInputSchema extends TSchema ? Static : any; ctx: TContext; }) => boolean \| Promise` | | | `execute` | `(params: { input: TInputSchema extends TSchema ? Static : any; ctx:` [`NarrowAuthorizedContext`](#narrowauthorizedcontext)`; error:` [`ErrorBuilder`](#errorbuilder)`; }) => \| Promise : …` | | *Source: [packages/core/src/types.ts:202](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L202)* ### ActionConfigWithoutAuthorize ```ts interface ActionConfigWithoutAuthorize = Record, TContext extends ActionContext = ActionContext> extends BaseActionConfig ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `authorize?` | `undefined` | | | `execute` | `(params: { input: TInputSchema extends TSchema ? Static : any; ctx: TContext; error:` [`ErrorBuilder`](#errorbuilder)`; }) => \| Promise : any> \| (TOutputSchema ext…` | | *Source: [packages/core/src/types.ts:225](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L225)* ### ActionErrorDefinition ```ts interface ActionErrorDefinition ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `status?` | `number` | | | `message` | `string` | | *Source: [packages/core/src/types.ts:62](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L62)* ### ActionSchema ```ts interface ActionSchema ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `method?` | `"GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE"` | Present only when this action has a registered HTTP route. | | `path?` | `string` | | | `serviceName?` | `string` | | | `plugin?` | `string` | Owning plugin descriptor name for plugin actions (e.g. `@brickkit/crud`). | | `resource?` | `string` | Owning resource name for resource-plugin actions (e.g. `page`). | | `description?` | `string` | | | `inputSchema?` | `unknown` | | | `outputSchema?` | `unknown` | | | `hasAuthorize` | `boolean` | | | `hasScope?` | `boolean` | | | `accessPolicy?` | [`ActionAccessPolicy`](#actionaccesspolicy) | | | `errors?` | `Record` | | | `emits?` | `string[]` | | | `tags?` | `string[]` | | *Source: [packages/core/src/types.ts:480](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L480)* ### ApiDocument ```ts interface ApiDocument ``` A named OpenAPI document ready to render. **Properties** | Name | Type | Description | | --- | --- | --- | | `title` | `string` | | | `document` | [`OpenApiDocument`](#openapidocument) | | *Source: [packages/core/src/docs.ts:17](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L17)* ### ArchitectureSchema ```ts interface ArchitectureSchema ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `version` | `string` | | | `services` | [`ServiceSchema`](#serviceschema)`[]` | | *Source: [packages/core/src/types.ts:517](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L517)* ### BaseActionConfig ```ts interface BaseActionConfig = Record> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `description?` | `string` | | | `path?` | `string` | Effective HTTP path, including the prefix; replaces the default action path. | | `method?` | `"GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE"` | Single HTTP method exposed for this action (default POST). | | `input?` | `TInputSchema` | | | `output?` | `TOutputSchema` | | | `errors?` | `TErrors` | | | `emits?` | `string[]` | | | `tags?` | `string[]` | | *Source: [packages/core/src/types.ts:181](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L181)* ### BaseContext ```ts interface BaseContext ``` Base framework context supplied to all service resolvers and action executions. **Properties** | Name | Type | Description | | --- | --- | --- | | `request` | `Request` | | | `db` | `TDb` | | | `logger` | [`Logger`](#logger) | | | `traceId` | `string` | | | `requestId?` | `string` | | | `caller?` | `string` | | | `tenantId?` | `string` | | | `services?` | `Record` | | | `metadata?` | `Record` | | *Source: [packages/core/src/types.ts:23](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L23)* ### BrickCallOptions ```ts interface BrickCallOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `headers?` | `Record` | Extra headers merged over the client defaults for this call only. | | `signal?` | `AbortSignal` | Abort signal for this call only. Never wrapped: aborts reject with `AbortError`. | | `timeoutMs?` | `number` | Per-call timeout in milliseconds. Implemented with `AbortSignal.timeout`. | *Source: [packages/core/src/client.ts:109](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L109)* ### BrickClientAction ```ts interface BrickClientAction ``` Browser-safe action metadata generated by the Brick Vite plugin. **Properties** | Name | Type | Description | | --- | --- | --- | | `kind` | `"action"` | | | `name` | `string` | | | `serviceName` | `string` | | | `plugin?` | `string` | Owning plugin descriptor name for plugin actions (e.g. `@brickkit/crud`). | | `resource?` | `string` | Owning resource name for resource-plugin actions (e.g. `page`). | | `config` | `{ path: string; method: "GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE" }` | | | `readonly __types?` | `{ input: TInput; output: TOutput; errors: TErrors }` | Type information only; never emitted at runtime. | *Source: [packages/core/src/client.ts:92](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L92)* ### BrickClientOptions ```ts interface BrickClientOptions ``` Eden/tRPC-style typed HTTP client inferred from Brick-TS application contracts. The frontend imports only the contract *type* — never a backend implementation: ```ts import { createBrickClient } from "@brickkit/core/client"; import type { AppServer } from "./server"; // `import type`: erased at build const api = createBrickClient({ baseUrl: "http://localhost:4000" }); const pages = await api.page.list({ limit: 10, sort: "-createdAt" }); const created = await api.page.create({ title: "Hello", content: "World" }); ``` Build the contract from individually-typed actions (each `defineAction` call preserves its input/output/error literal types): ```ts // server.ts (backend) export const listPages = pagesService.action({ name: "page.list", ... }); export const createPage = pagesService.action({ name: "page.create", ... }); export const contract = { page: { list: listPages, create: createPage } }; export type AppServer = typeof contract; ``` **Result / error convention** The client uses a throw-based convention that mirrors local `action.execute` semantics, because TypeScript promises cannot encode thrown exception types: - Success → the promise resolves with the action output. - Declared domain failures (your `errors: { SLUG_EXISTS: ... }`) and framework guards (UNAUTHORIZED/FORBIDDEN/NOT_FOUND/...) → rejected with [`ActionExecutionError`](#actionexecutionerror) carrying the declared `code`, `status`, `message`, and `details`. Narrow with `instanceof` and read `err.code`. - Input/output validation failures → rejected with [`ValidationError`](#validationerror), exactly as local execution throws. - Network failures, unparsable bodies, and non-OK responses outside the declared contract → [`BrickTransportError`](#bricktransporterror) (4xx) or [`BrickServerError`](#brickservererror) (5xx). - Cancellation via `AbortSignal` → the native `AbortError` propagates unwrapped so `err.name === "AbortError"` keeps working. **URL mapping** A client path is a namespace walk ending in an action call. The leaf segment decides the route, matching the server compiler (`packages/cli/src/compiler`): | Client call | HTTP request | |---|---| Generated browser contracts carry each action's HTTP method and path, including plugin routes and consumer overrides. Without runtime metadata, callers use `POST /api//` with dotted action names. GET/DELETE input is serialized as a query string; other methods send JSON. **Properties** | Name | Type | Description | | --- | --- | --- | | `baseUrl` | `string` | Server origin, e.g. `"http://localhost:4000"`. No trailing slash required. | | `prefix?` | `string` | Route prefix the server was mounted with (default `"/api"`). Must match the `prefix` passed to `brick({ prefix })`. | | `headers?` | `\| Record \| (() => Record \| Promise>)` | Default headers sent with every call (e.g. an `Authorization` bearer). A thunk is re-evaluated per call, so rotated tokens stay fresh. | | `fetch?` | `(input: RequestInfo \| URL, init?: RequestInit) => Promise` | `fetch` implementation. Defaults to `globalThis.fetch`. | | `signal?` | `AbortSignal` | Default signal applied to every call (per-call `signal` wins). | | `timeoutMs?` | `number` | Default per-call timeout in milliseconds (per-call `timeoutMs` wins). | | `contract?` | `Record` | Browser-safe route metadata generated by the Brick Vite plugin. Required for custom methods/paths and plugin routes. Without a contract, calls use POST and service/action names inferred from namespace keys. | *Source: [packages/core/src/client.ts:62](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L62)* ### BrickIR ```ts interface BrickIR ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly version` | `1` | | | `readonly target` | `"bun-elysia"` | | | `readonly prefix` | `string` | | | `readonly services` | `readonly string[]` | | | `readonly routes` | `readonly` [`RouteIR`](#routeir)`[]` | | | `readonly preparations` | `readonly { service: string; name: string; metadata: unknown; }[]` | | | `readonly diagnostics` | `readonly string[]` | | *Source: [packages/core/src/compiler/ir.ts:24](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/ir.ts#L24)* ### CacheOptions ```ts interface CacheOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `driver?` | `"memory" \| "redis"` | | | `defaultTtl?` | `number` | | *Source: [packages/core/src/types.ts:433](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L433)* ### CompiledApplication ```ts interface CompiledApplication ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly ir` | [`BrickIR`](#brickir) | | | `readonly routes` | `readonly` [`CompiledRoute`](#compiledroute)`[]` | | *Source: [packages/core/src/compiler.ts:55](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler.ts#L55)* ### CompiledRoute ```ts interface CompiledRoute ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly ir` | [`RouteIR`](#routeir) | | | `readonly handler` | [`CompiledHandler`](#compiledhandler) | | *Source: [packages/core/src/compiler.ts:51](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler.ts#L51)* ### CompiledRoutePlan ```ts interface CompiledRoutePlan ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `service` | [`Service`](#service)`` | | | `action` | [`Action`](#action)`` | | | `actionName` | `string` | | | `serviceName` | `string` | | | `routePath` | `string` | | | `method?` | `string` | | | `isGetLike` | `boolean` | | | `hasInput` | `boolean` | | | `hasContextHook` | `boolean` | | | `contextHook?` | `(baseCtx: any) => Promise \| any` | | | `hasAuthorize` | `boolean` | | | `authorize?` | `(params: any) => boolean \| Promise` | | | `execute` | `(params: any) => Promise \| any` | | | `inputChecker?` | `{ Check: (val: any) => boolean; Errors: (val: any) => Iterable; }` | | | `outputChecker?` | `{ Check: (val: any) => boolean; Errors: (val: any) => Iterable; }` | | | `coercions?` | `Array<{ key: string; isNumber: boolean; isBoolean: boolean; isArray?: boolean; }>` | | | `logger` | [`Logger`](#logger) | | | `errorBuilder` | `any` | | | `cachedDb` | `any` | | *Source: [packages/core/src/types.ts:522](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L522)* ### CreateServerOptions ```ts interface CreateServerOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `port?` | `number` | | | `compiler?` | `boolean` | Generate specialized handlers once at startup (default true). | | `compilation?` | [`CompiledApplication`](#compiledapplication) | Ahead-of-time handlers already bound to the supplied services. | | `services?` | [`Service`](#service)`[]` | | | `prefix?` | `string` | | | `title?` | `string` | | | `version?` | `string` | | | `description?` | `string` | | | `docs?` | `boolean` | | | `openApiPath?` | `string` | | | `docsPath?` | `string` | | | `swaggerPath?` | `string` | | | `requestLogging?` | `boolean \|` [`RequestLoggingOptions`](#requestloggingoptions) | | | `reference?` | `boolean \|` [`ReferenceOptions`](#referenceoptions) | `true` serves a Scalar page at /reference. Options serve one only with a `path`; `documents` adds OpenAPI documents to the dashboard's APIs view. | | `secrets?` | `{ source?:` [`SecretSource`](#secretsource)`; validate?: boolean }` | | | `endpoints?` | [`EndpointDefinition`](#endpointdefinition)`[]` | Raw endpoints (outside the JSON action mesh), mounted at startup. | | `telemetry?` | `boolean` | Record request, action, and RPC spans with OpenTelemetry (default false). Spans go to the globally registered tracer provider; without one they are no-ops. `brick dev` shows them in its dashboard. | | `documentation?` | `string \|` [`DocumentationOptions`](#documentationoptions) | Developer documentation for the `brick dev` dashboard (its Docs tab): a folder of `.md`/`.mdx` pages in Fumadocs' layout, with frontmatter and `meta.json` for ordering. A string is the folder. Relative paths start at the folder of the app entry, as they read in the file that sets them (from `src/app.ts`, `"../docs"` is `docs/` at the project root). Needs `fumadocs-core` and `@mdx-js/mdx` installed. | | `dashboard?` | [`DashboardOptions`](#dashboardoptions) | Monitoring dashboard for `brick start`, on a port of its own and behind `auth`. Setting it turns on `telemetry`. See `DashboardOptions`. | *Source: [packages/core/src/server.ts:34](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/server.ts#L34)* ### DashboardAuth ```ts interface DashboardAuth ``` Who may open the dashboard that `brick start` serves for monitoring. **Properties** | Name | Type | Description | | --- | --- | --- | | `token?` | `string \| readonly string[]` | Secrets that let a request in. A browser asks for one in its sign-in prompt (any user name, the secret as the password), and monitors and scripts send `Authorization: Bearer `. Give several to rotate one while the other still works. Each is at least 16 characters long; `openssl rand -base64 32` makes a good one. | | `authorize?` | `(request: Request,) => boolean \| Response \| Promise` | Your own check, to sign people in with the auth the app already has: read a session cookie, verify a Better Auth or Clerk session, look at a header a proxy adds. It receives the request (method, URL, and headers; no body) and returns `true` to let it in, `false` to refuse it with 401, or a `Response` to send instead, such as a redirect to your login page. If it throws or takes longer than five seconds, the request is refused with 503. A request is let in when it carries a valid `token`, or when this returns `true`; give both to let monitors in by token and people in by sign-in. | *Source: [packages/core/src/dashboard.ts:2](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/dashboard.ts#L2)* ### DashboardOptions ```ts interface DashboardOptions ``` The monitoring dashboard `brick start` serves beside the app, on a port of its own: live requests, logs, errors, and the shape of the service, so it stays reachable when the app's own routes are not. `brick dev` has its own dashboard, set by command line options, and ignores this. Setting it turns on `telemetry`, since the dashboard shows what is traced. While it is on, `ctx.logger` and `console` lines written during a request go to the dashboard instead of the console. **Properties** | Name | Type | Description | | --- | --- | --- | | `port?` | `number` | Default 9400. | | `host?` | `string` | The address to listen on. Default `127.0.0.1`, reachable only from the same machine. In a container use `0.0.0.0`, which needs `auth`: the dashboard will not start on any other address without it. | | `auth?` | [`DashboardAuth`](#dashboardauth) | | | `historyDays?` | `number` | Days of finished requests to keep on disk, in `.brick/` of the working folder. Default 0: only what happened since the process started, which is lost when it restarts. Needs a folder that outlives the container. | *Source: [packages/core/src/dashboard.ts:38](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/dashboard.ts#L38)* ### DatabaseAdapter ```ts interface DatabaseAdapter ``` **Methods** | Name | Type | Description | | --- | --- | --- | | `createDatabase` | `createDatabase(config?: TConfig): TDb` | | *Source: [packages/core/src/db/postgres.ts:36](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/postgres.ts#L36)* ### DatabaseConfig ```ts interface DatabaseConfig = Record> extends Omit, "client">, Omit, "client"> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `client?` | \| [`SQLiteDatabaseConfig`](#sqlitedatabaseconfig)`["client"] \|` [`PostgresDatabaseConfig`](#postgresdatabaseconfig)`["client"]` | | | `engine?` | `"sqlite" \| "postgres" \| "clickhouse"` | Database engine. Default: 'sqlite' | | `name?` | `string` | Optional service name associated with this database | *Source: [packages/core/src/db/index.ts:46](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L46)* ### DatabaseHandle ```ts interface DatabaseHandle = Record> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"brick-database"` | | | `readonly name?` | `string` | | | `readonly engine` | `"sqlite" \| "postgres"` | | | `readonly tables` | `Readonly>` | | | `readonly schema` | `TSchema` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `getDb` | `getDb(): TDb` | | | `close?` | `close(): void \| Promise` | | *Source: [packages/core/src/db/define.ts:54](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L54)* ### DefaultServiceContext ```ts interface DefaultServiceContext ``` Default fallback service context for untyped or simple services. **Properties** | Name | Type | Description | | --- | --- | --- | | `user?` | `any` | | | `session?` | `any` | | | `[index]` | `[key: string]: any` | | *Source: [packages/core/src/types.ts:38](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L38)* ### DocumentationOptions ```ts interface DocumentationOptions ``` Developer documentation for the `brick dev` dashboard. **Properties** | Name | Type | Description | | --- | --- | --- | | `dir` | `string` | Folder of `.md`/`.mdx` pages. A relative path starts at the folder of the app entry, as it reads in the file that sets it. | | `components?` | `string` | Module whose exports are React components for MDX, added to the built-in ones; a relative path starts where `dir` does. A default export or a `components` export holds the name-to-component map. The dashboard bundles it, so it may import anything the project has. | *Source: [packages/core/src/docs.ts:23](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L23)* ### EndpointContext ```ts interface EndpointContext ``` Context for raw endpoint handlers: Elysia route fields + Brick extras. **Properties** | Name | Type | Description | | --- | --- | --- | | `params` | `Record` | | | `query` | `Record` | | | `body` | `unknown` | | | `headers` | `Record` | | | `request` | `Request` | | | `set` | `{ status?: number; headers: Record }` | Elysia response setter (status/headers). Mutate like in `app.get`. | | `db` | `any` | Drizzle db from `service` (or first service with one); `undefined` when none. | | `logger` | [`EndpointLogger`](#endpointlogger) | | | `traceId` | `string` | | | `requestId` | `string` | | *Source: [packages/core/src/endpoints.ts:22](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L22)* ### EndpointDefinition ```ts interface EndpointDefinition ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `method` | [`EndpointMethod`](#endpointmethod) | | | `path` | `string` | Elysia-style path, e.g. `/p/:slug`. | | `handler` | `(ctx:` [`EndpointContext`](#endpointcontext)`) => unknown \| Promise` | | | `service?` | [`Service`](#service)` \| string` | Db source: instance or registered service name. Defaults to first service with a db. | | `database?` | [`DatabaseHandle`](#databasehandle)`` | Explicit database handle, independent of a service. | | `summary?` | `string` | | | `description?` | `string` | | | `tags?` | `string[]` | | *Source: [packages/core/src/endpoints.ts:37](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L37)* ### EndpointLogger ```ts interface EndpointLogger ``` Logger subset handed to endpoint handlers (mirrors action context loggers). **Properties** | Name | Type | Description | | --- | --- | --- | | `info` | `(...args: any[]) => void` | | | `warn` | `(...args: any[]) => void` | | | `error` | `(...args: any[]) => void` | | | `debug` | `(...args: any[]) => void` | | *Source: [packages/core/src/endpoints.ts:14](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L14)* ### ErrorEnvelope ```ts interface ErrorEnvelope ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `code` | `string` | | | `message` | `string` | | | `status` | `number` | | | `requestId` | `string` | | | `details?` | `unknown` | | | `errors?` | [`ValidationError`](#validationerror)`["errors"]` | | *Source: [packages/core/src/error-response.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/error-response.ts#L4)* ### LifecycleOptions ```ts interface LifecycleOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `onInit?` | `(ctx: { db?: any; logger?:` [`Logger`](#logger) `}) => Promise` | | | `onDestroy?` | `() => Promise` | | *Source: [packages/core/src/types.ts:457](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L457)* ### Logger ```ts interface Logger ``` The logger on action, tool, and endpoint contexts. While a dashboard is on (`brick dev`, or `brick start` with the `dashboard` option) a line logged during a traced request appears in the dashboard with that request, instead of on the console (as does a plain `console.log`); anywhere else it prints to the console. **Properties** | Name | Type | Description | | --- | --- | --- | | `info` | `(...args: any[]) => void` | | | `warn` | `(...args: any[]) => void` | | | `error` | `(...args: any[]) => void` | | | `debug` | `(...args: any[]) => void` | | *Source: [packages/core/src/types.ts:13](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L13)* ### McpHandlerOptions ```ts interface McpHandlerOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `registry` | [`McpRegistry`](#mcpregistry) | | | `serverInfo` | `{ name: string; version: string; title?: string }` | | | `protocolVersions?` | `readonly string[]` | Versions supported by this transport, newest first. | *Source: [packages/core/src/mcp.ts:71](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L71)* ### McpRegistry ```ts interface McpRegistry ``` **Methods** | Name | Type | Description | | --- | --- | --- | | `listTools` | `listTools():` [`McpToolDefinition`](#mcptooldefinition)`[]` | | | `callTool` | `callTool( name: string, args?: Record, ctx?: Partial<`[`ActionContext`](#actioncontext)`>, ): Promise` | | *Source: [packages/core/src/mcp.ts:16](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L16)* ### McpRegistryOptions ```ts interface McpRegistryOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `services?` | `readonly Pick<`[`Service`](#service)`, "listTools">[]` | | | `tools?` | `readonly` [`Tool`](#tool)`[]` | | *Source: [packages/core/src/mcp.ts:11](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L11)* ### McpToolDefinition ```ts interface McpToolDefinition ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `title?` | `string` | | | `description` | `string` | | | `inputSchema` | `unknown` | | | `annotations` | [`ToolAnnotations`](#toolannotations) | | *Source: [packages/core/src/mcp.ts:3](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/mcp.ts#L3)* ### OpenApiGeneratorOptions ```ts interface OpenApiGeneratorOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `title?` | `string` | | | `version?` | `string` | | | `description?` | `string` | | | `prefix?` | `string` | | | `services?` | [`Service`](#service)`[]` | | | `registry?` | [`ServiceRegistry`](#serviceregistry) | | *Source: [packages/core/src/openapi.ts:7](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/openapi.ts#L7)* ### PluginEndpointContext ```ts interface PluginEndpointContext ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `params` | `Record` | | | `query` | `Record` | | | `body` | `unknown` | | | `headers` | `Record` | | | `request` | `Request` | | | `set` | `{ status?: number; headers: Record }` | | | `ctx` | `C` | | *Source: [packages/core/src/plugin.ts:24](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L24)* ### PluginRouteRegistrar ```ts interface PluginRouteRegistrar ``` **Methods** | Name | Type | Description | | --- | --- | --- | | `action` | `action< I extends TSchema \| undefined = undefined, O extends TSchema \| undefined = undefined, E extends Record = Record, >( config:` [`ActionConfigWithAuthorize`](#actionconfigwithauthorize)` & { path: string; method:` [`PluginRouteMethod`](#pluginroutemethod)`; scope?: S; }, ):` [`Action`](#action)`` | | | `action` | `action< I extends TSchema \| undefined = undefined, O extends TSchema \| undefined = undefined, E extends Record = Record, >( config:` [`ActionConfigWithoutAuthorize`](#actionconfigwithoutauthorize)` & { path: string; method:` [`PluginRouteMethod`](#pluginroutemethod)`; scope?: S; }, ):` [`Action`](#action)`` | | | `endpoint` | `endpoint( config: { name: string; path: string; method:` [`PluginRouteMethod`](#pluginroutemethod)`; scope?: S; handler: (context:` [`PluginEndpointContext`](#pluginendpointcontext)`) => unknown \| Promise; summary…, ): void` | | *Source: [packages/core/src/plugin.ts:36](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L36)* ### PostgresDatabaseConfig ```ts interface PostgresDatabaseConfig = Record> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `client?` | `Sql` | Existing postgres-js client. Connection options are ignored; the caller owns its lifecycle. | | `url?` | `string` | Database connection URL (e.g. postgresql://user:pass@host:5432/dbname) | | `connectionString?` | `string` | Connection string alias | | `host?` | `string` | | | `port?` | `number` | | | `user?` | `string` | | | `password?` | `string` | | | `database?` | `string` | | | `ssl?` | `boolean \| "require" \| "allow" \| "prefer" \| "verify-full"` | | | `max?` | `number` | | | `prepare?` | `boolean` | Disable prepared statements (required for transaction-mode poolers such as Supabase's Supavisor on :6543, which cannot hold named prepared statements across transactions). | | `schema?` | `TSchema` | Optional Drizzle schema for relational queries | | `schemaName?` | `string` | Custom schema name for Encore-style isolated schemas | *Source: [packages/core/src/db/postgres.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/postgres.ts#L4)* ### PreparationContribution ```ts interface PreparationContribution ``` Generic preparation declarations consumed by the application compiler. **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly name` | `string` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `describe` | `describe(): unknown` | | | `prepare` | `prepare(): void` | | *Source: [packages/core/src/types.ts:389](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L389)* ### ProxyOptions ```ts interface ProxyOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `isMonolith?` | `boolean` | | | `from?` | `string` | Name of the service creating this proxy. Populates ctx.caller on the target's envelope. | | `context?` | `Partial<`[`ActionContext`](#actioncontext)`>` | | *Source: [packages/core/src/types.ts:695](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L695)* ### RateLimitOptions ```ts interface RateLimitOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `maxRequests` | `number` | | | `window` | `number \| string` | | | `scope?` | `"ip" \| "user" \| "tenant"` | | *Source: [packages/core/src/types.ts:427](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L427)* ### ReferenceDocument ```ts interface ReferenceDocument ``` An extra OpenAPI document for the API reference, such as Better Auth's. **Properties** | Name | Type | Description | | --- | --- | --- | | `title` | `string` | Name in the reference's list of documents. | | `document` | \| [`OpenApiDocument`](#openapidocument) `\| (() =>` [`OpenApiDocument`](#openapidocument) `\| Promise<`[`OpenApiDocument`](#openapidocument)`>)` | The document, or a function returning it; called each time the app starts. | *Source: [packages/core/src/docs.ts:7](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L7)* ### ReferenceOptions ```ts interface ReferenceOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `path?` | `string` | Also serve a standalone Scalar page here. Without a path, no page is served. | | `title?` | `string` | | | `specUrl?` | `string` | | | `documents?` | [`ReferenceDocument`](#referencedocument)`[]` | More OpenAPI documents for the `brick dev` dashboard's APIs view. | *Source: [packages/core/src/docs.ts:38](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L38)* ### RequestLoggingOptions ```ts interface RequestLoggingOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `enabled?` | `boolean` | | | `logger?` | `(msg: string) => void` | | *Source: [packages/core/src/request-logger.ts:3](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/request-logger.ts#L3)* ### ResilienceOptions ```ts interface ResilienceOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `timeoutMs?` | `number` | | | `retry?` | `{ attempts: number; backoff: "linear" \| "exponential"; delayMs: number; }` | | | `circuitBreaker?` | `{ errorThresholdPercentage: number; resetTimeoutMs: number; }` | | *Source: [packages/core/src/types.ts:438](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L438)* ### Resource ```ts interface Resource ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly name` | `string` | | | `readonly serviceName` | `string` | | | `readonly table` | `TTable` | | | `readonly id` | `TOptions extends { id: infer I extends Column; } ? I : Column` | | | `readonly idField` | `string` | | | `readonly config` | [`ResourceConfig`](#resourceconfig)` & TOptions` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `use` | `use< P extends` [`ResourcePlugin`](#resourceplugin)`<`[`Service`](#service)`>, this, any>, >( plugin: P, ): this &` [`ResourcePluginAPI`](#resourcepluginapi)`` | | *Source: [packages/core/src/types.ts:395](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L395)* ### ResourceConfig ```ts interface ResourceConfig ``` A resource declares identity and a table; plugins supply behavior. **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `table` | `TTable` | | | `id?` | `Column` | | *Source: [packages/core/src/types.ts:363](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L363)* ### ResourcePlugin ```ts interface ResourcePlugin, R extends Resource, A extends object> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly [pluginKind]` | `"resource"` | | | `readonly name` | `string` | | | `readonly routes?` | [`PluginRoutes`](#pluginroutes) | | | `readonly setup` | `(context:` [`ResourcePluginContext`](#resourceplugincontext)`) => A` | | *Source: [packages/core/src/plugin.ts:122](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L122)* ### ResourcePluginContext ```ts interface ResourcePluginContext = Service, R extends Resource = Resource, any>> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `service` | `S` | | | `resource` | `R` | | | `routes` | [`PluginRouteRegistrar`](#pluginrouteregistrar)`>` | | *Source: [packages/core/src/plugin.ts:86](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L86)* ### ResourcePluginTypes ```ts interface ResourcePluginTypes ``` Plugins may specialize their returned API for the installation host. **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly table` | `unknown` | | | `readonly context` | `unknown` | | | `readonly resource` | `unknown` | | | `readonly api` | `object` | | *Source: [packages/core/src/plugin.ts:96](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L96)* ### RouteIR ```ts interface RouteIR ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly method` | [`RouteMethod`](#routemethod) | | | `readonly path` | `string` | | | `readonly service` | `string` | | | `readonly action` | `string` | | | `readonly kind` | `"action"` | | | `readonly input` | `boolean` | | | `readonly output` | `boolean` | | | `readonly context` | `boolean` | | | `readonly authorize` | `boolean` | | | `readonly inputPrecedence` | `readonly ("query" \| "params" \| "body")[]` | | | `readonly coercions` | `readonly { readonly key: string; readonly type: "number" \| "boolean" \| "array"; }[]` | | | `readonly inputSchema?` | `unknown` | JSON projection for inspection and stale-artifact detection, not runtime validation. | | `readonly outputSchema?` | `unknown` | | *Source: [packages/core/src/compiler/ir.ts:5](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/ir.ts#L5)* ### SecretOptions ```ts interface SecretOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `default?` | `string` | Fallback when the source has no value. Secrets without a default are required. | *Source: [packages/core/src/secrets.ts:31](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L31)* ### SecretSource ```ts interface SecretSource ``` Type-safe secrets & environment variables. Declare each secret once at module scope instead of scattering untyped `process.env.FOO` reads: const port = secret("PORT").default("3333").transform(Number).require(); const baseUrl = secret("BASE_URL").url().require(); const key = secret("STRIPE_KEY").validate((v) => v.startsWith("sk_") || "must start with sk_" ).require(); Values resolve from the environment (Bun populates `process.env` from `.env` locally; in prod the same env source applies — a future `SecretSource` such as AWS can slot in without touching call sites). Resolution order per secret: test/session override → source (`process.env` by default) → declared `default`. `resolveSecrets()` validates every registered secret at boot and throws listing all missing required ones. **Methods** | Name | Type | Description | | --- | --- | --- | | `get` | `get(name: string): string \| undefined` | | *Source: [packages/core/src/secrets.ts:22](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L22)* ### Service ```ts interface Service>, TServiceContext extends Record = Record> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly name` | `string` | | | `readonly options` | [`ServiceOptions`](#serviceoptions)`` | | | `readonly actions` | `Map>` | | | `readonly tools` | `Map>` | | | `readonly tables` | `Map` | | | `readonly resources` | `Map & TServiceContext>>` | | | `readonly db?` | `TDb` | | | `readonly isBuilt?` | `boolean` | | | `readonly preparations` | `Map` | | **Methods** | Name | Type | Description | | --- | --- | --- | | `use` | `use(plugin:` [`ServicePlugin`](#serviceplugin)`): A` | | | `build` | `build(): this` | | | `action` | `action< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( action:` [`Action`](#action)` & TServiceContext>, ): this` | | | `action` | `action< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( config:` [`ActionConfigWithAuthorize`](#actionconfigwithauthorize)` & TServiceContext>, ):` [`Action`](#action)` & TServiceContext>` | | | `action` | `action< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( config:` [`ActionConfigWithoutAuthorize`](#actionconfigwithoutauthorize)` & TServiceContext>, ):` [`Action`](#action)` & TServiceContext>` | | | `tool` | `tool< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( action:` [`Tool`](#tool)` & TServiceContext>, ): this` | | | `tool` | `tool< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( config:` [`ToolConfigWithAuthorize`](#toolconfigwithauthorize)` & TServiceContext>, ):` [`Tool`](#tool)` & TServiceContext>` | | | `tool` | `tool< TIn extends TSchema \| undefined = undefined, TOut extends TSchema \| undefined = undefined, TErr extends Record = Record, >( config:` [`ToolConfigWithoutAuthorize`](#toolconfigwithoutauthorize)` & TServiceContext>, ):` [`Tool`](#tool)` & TServiceContext>` | | | `resource` | `resource< TTable = any, const TOptions extends` [`ResourceTypeOptions`](#resourcetypeoptions) `=` [`ResourceTypeOptions`](#resourcetypeoptions)`, >( config:` [`ResourceConfig`](#resourceconfig)` & TServiceContext> & TOptions & Record, never>, ):` [`Resource`](#resource)` & TServiceContext,` [`ResourceIdentity`](#resourceidentity)`>` | | | `contributePreparation` | `contributePreparation(contribution:` [`PreparationContribution`](#preparationcontribution)`): void` | | | `listPreparations` | `listPreparations():` [`PreparationContribution`](#preparationcontribution)`[]` | | | `prepareContributions` | `prepareContributions(): void` | | | `registerTable` | `registerTable(table: any): this` | | | `getAction` | `getAction(name: string):` [`Action`](#action)` \| undefined` | | | `listActions` | `listActions():` [`Action`](#action)`[]` | | | `getTool` | `getTool(name: string):` [`Tool`](#tool)` \| undefined` | | | `listTools` | `listTools():` [`Tool`](#tool)`[]` | | | `getResource` | `getResource( name: string, ):` [`Resource`](#resource)` & TServiceContext> \| undefined` | | | `listResources` | `listResources():` [`Resource`](#resource)` & TServiceContext>[]` | | | `getDb` | `getDb(): TDb \| undefined` | | | `setDb` | `setDb(database:` [`DatabaseHandle`](#databasehandle)`): void` | | | `resolveContext` | `resolveContext( callCtx?: Partial<`[`BaseContext`](#basecontext)` & TServiceContext>, ): Promise<`[`BaseContext`](#basecontext)` & TServiceContext>` | | | `introspect` | `introspect():` [`ServiceSchema`](#serviceschema) | | *Source: [packages/core/src/types.ts:555](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L555)* ### ServiceAuthOptions ```ts interface ServiceAuthOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `requireAuth?` | `boolean` | | | `allowedRoles?` | `string[]` | | | `policy?` | `(ctx:` [`ActionContext`](#actioncontext)`) => boolean \| Promise` | | *Source: [packages/core/src/types.ts:421](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L421)* ### ServiceOptions ```ts interface ServiceOptions = Record, TDb = any> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `database?` | [`DatabaseHandle`](#databasehandle)`` | | | `context?` | `(baseCtx:` [`BaseContext`](#basecontext)` & { user?: unknown },) => Promise \| TServiceContext` | | | `auth?` | [`ServiceAuthOptions`](#serviceauthoptions) | | | `rateLimit?` | [`RateLimitOptions`](#ratelimitoptions) | | | `cache?` | [`CacheOptions`](#cacheoptions) | | | `resilience?` | [`ResilienceOptions`](#resilienceoptions) | | | `telemetry?` | [`TelemetryOptions`](#telemetryoptions) | | | `lifecycle?` | [`LifecycleOptions`](#lifecycleoptions) | | *Source: [packages/core/src/types.ts:462](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L462)* ### ServicePlugin ```ts interface ServicePlugin, A> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly [pluginKind]` | `"service"` | | | `readonly name` | `string` | | | `readonly routes?` | [`PluginRoutes`](#pluginroutes)`<"app" \| "service">` | | | `readonly setup` | `(context:` [`ServicePluginContext`](#serviceplugincontext)`) => A` | | *Source: [packages/core/src/plugin.ts:116](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L116)* ### ServicePluginContext ```ts interface ServicePluginContext = Service> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `service` | `S` | | | `routes` | [`PluginRouteRegistrar`](#pluginrouteregistrar)`, "app" \| "service">` | | *Source: [packages/core/src/plugin.ts:80](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L80)* ### ServiceSchema ```ts interface ServiceSchema ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `name` | `string` | | | `options` | `Record` | The service options as JSON: functions, such as `context` and hooks, are left out. | | `hasDatabase` | `boolean` | | | `databaseConfig?` | `{ name?: string; engine: "sqlite" \| "postgres"; tables: string[]; }` | | | `actions` | [`ActionSchema`](#actionschema)`[]` | | | `tools?` | `{ name: string }[]` | MCP tools. A tool with an HTTP route is also listed under `actions`. | | `resources?` | `{ name: string }[]` | | *Source: [packages/core/src/types.ts:501](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L501)* ### SQLiteDatabaseConfig ```ts interface SQLiteDatabaseConfig = Record> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `path?` | `string` | Database file path or ':memory:' (default: ':memory:') | | `filename?` | `string` | Alias for path | | `wal?` | `boolean` | Whether to enable WAL journal mode for file-based DBs (default: true) | | `foreignKeys?` | `boolean` | Whether to enforce foreign key constraints (default: true) | | `client?` | `Database` | Optional existing bun:sqlite Database instance | | `schema?` | `TSchema` | Optional Drizzle schema object for typed relational queries | *Source: [packages/core/src/db/sqlite.ts:22](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/sqlite.ts#L22)* ### StandardErrorMap ```ts interface StandardErrorMap ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `BAD_REQUEST` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `UNAUTHORIZED` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `FORBIDDEN` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `NOT_FOUND` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `CONFLICT` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `INTERNAL_SERVER_ERROR` | `(overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never` | | | `[index]` | `[key: string]: \| ((overrideMessage?: string, details?: unknown, options?: ErrorOptions,) => never) \| undefined` | | *Source: [packages/core/src/types.ts:131](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L131)* ### TelemetryOptions ```ts interface TelemetryOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `traceQueries?` | `boolean` | | | `auditLog?` | `boolean` | | | `tags?` | `Record` | | *Source: [packages/core/src/types.ts:451](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L451)* ### Tool ```ts interface Tool = Record, C extends ActionContext = ActionContext> extends Action ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"tool"` | | | `config` | \| [`ToolConfigWithAuthorize`](#toolconfigwithauthorize)` \|` [`ToolConfigWithoutAuthorize`](#toolconfigwithoutauthorize)`` | | *Source: [packages/core/src/types.ts:347](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L347)* ### ToolAnnotations ```ts interface ToolAnnotations ``` MCP tool metadata, kept with the executable definition. **Properties** | Name | Type | Description | | --- | --- | --- | | `title?` | `string` | | | `readOnlyHint?` | `boolean` | | | `destructiveHint?` | `boolean` | | | `idempotentHint?` | `boolean` | | | `openWorldHint?` | `boolean` | | *Source: [packages/core/src/types.ts:316](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L316)* ### ToolOptions ```ts interface ToolOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `title?` | `string` | | | `annotations?` | [`ToolAnnotations`](#toolannotations) | | | `http?` | `boolean` | Also register HTTP routes and OpenAPI. Defaults to false. | *Source: [packages/core/src/types.ts:323](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L323)* ### ValidationErrorItem ```ts interface ValidationErrorItem ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `path` | `string` | | | `message` | `string` | | | `value?` | `unknown` | | | `type?` | `number` | | *Source: [packages/core/src/types.ts:67](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L67)* ## Types ### ActionAccessPolicy ```ts type ActionAccessPolicy = "public" | "authorized" | "scoped"; ``` *Source: [packages/core/src/types.ts:478](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L478)* ### ActionConfig ```ts type ActionConfig< TInputSchema extends TSchema | undefined = any, TOutputSchema extends TSchema | undefined = any, TErrors extends Record = Record< string, ActionErrorDefinition >, TContext extends ActionContext = ActionContext, > = | ActionConfigWithAuthorize | ActionConfigWithoutAuthorize< TInputSchema, TOutputSchema, TErrors, TContext >; ``` *Source: [packages/core/src/types.ts:244](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L244)* ### ActionContext ```ts type ActionContext< TDb = any, TServiceContext = DefaultServiceContext, > = BaseContext & TServiceContext; ``` The combined execution context: BaseContext & TServiceContext. *Source: [packages/core/src/types.ts:47](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L47)* ### BrickActionCaller ```ts type BrickActionCaller = undefined extends InferActionInput ? ( input?: InferActionInput, options?: BrickCallOptions, ) => Promise> : ( input: InferActionInput, options?: BrickCallOptions, ) => Promise>; ``` Typed caller for one action. Required inputs stay required; actions whose input includes `undefined` (no schema / all-optional) can be called bare. *Source: [packages/core/src/client.ts:259](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L259)* ### BrickApp ```ts type BrickApp = Elysia & { /** Service definitions, prefix, and telemetry setting chosen at construction. */ readonly definition: { readonly routes: readonly RouteIR[]; readonly services: readonly Service[]; readonly prefix: string; readonly telemetry: boolean; readonly documentation?: DocumentationOptions; /** What `brick start` serves for monitoring; its `auth` is not enumerable. */ readonly dashboard?: DashboardOptions; }; /** Register a raw endpoint (outside the JSON action mesh). */ endpoint(def: EndpointDefinition): void; /** * The OpenAPI documents behind the dashboard's APIs view: this app's own * unless `docs` is false, then those from `reference.documents`. */ referenceDocuments(): Promise; /** All endpoints registered via `endpoint()` (declaration order). */ listEndpoints(): EndpointDefinition[]; }; ``` The app object returned by `brick`: Elysia plus `endpoint()`. *Source: [packages/core/src/endpoints.ts:175](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L175)* ### BrickClient ```ts type BrickClient = T extends | BrickClientAction | Action ? BrickActionCaller : T extends (...args: any[]) => any ? BrickActionCaller : T extends object ? { [K in keyof T]: BrickClient } : T; ``` Recursively maps action leaves and their namespaces to browser callers. *Source: [packages/core/src/client.ts:271](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L271)* ### BrickDatabase ```ts type BrickDatabase< TSchema extends Record = Record, > = BunSQLiteDatabase | PostgresDatabase; ``` *Source: [packages/core/src/db/index.ts:61](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/index.ts#L61)* ### BrickSQLiteDatabase ```ts type BrickSQLiteDatabase< TSchema extends Record = Record, > = BunSQLiteDatabase & { $client: Database; }; ``` *Source: [packages/core/src/db/sqlite.ts:39](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/sqlite.ts#L39)* ### CompiledHandler ```ts type CompiledHandler = (context: any) => unknown | Promise; ``` *Source: [packages/core/src/compiler/runtime.ts:10](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/runtime.ts#L10)* ### DatabaseDefinition ```ts type DatabaseDefinition< TSchema extends Record = Record, > = SQLiteDefinition | PostgresDefinition; ``` *Source: [packages/core/src/db/define.ts:50](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L50)* ### EndpointMethod ```ts type EndpointMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; ``` *Source: [packages/core/src/endpoints.ts:11](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/endpoints.ts#L11)* ### EnvInput ```ts type EnvInput = T | SecretRef; ``` *Source: [packages/core/src/db/define.ts:18](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L18)* ### ErrorBuilder ```ts type ErrorBuilder< TErrors extends Record, > = StandardErrorMap & { [K in keyof TErrors]: ( overrideMessage?: string, details?: unknown, options?: ErrorOptions, ) => never; }; ``` *Source: [packages/core/src/types.ts:171](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L171)* ### HandlerFactory ```ts type HandlerFactory = ( binding: CompiledRoutePlan, runtime: typeof compilerRuntime, ) => CompiledHandler; ``` *Source: [packages/core/src/compiler/runtime.ts:11](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/runtime.ts#L11)* ### InferActionErrorCodes ```ts type InferActionErrorCodes = A extends BrickClientAction ? Extract : A extends { config: { errors?: infer E }; } ? E extends Record ? Extract : never : never; ``` Union of error `code`s declared in an action's `errors` map. *Source: [packages/core/src/client.ts:220](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L220)* ### InferActionFailure ```ts type InferActionFailure = ActionExecutionError & { code: | InferActionErrorCodes | "BAD_REQUEST" | "UNAUTHORIZED" | "FORBIDDEN" | "NOT_FOUND" | "CONFLICT" | "INTERNAL_SERVER_ERROR" | "VALIDATION_FAILED"; }; ``` The declared failure shape of an action for `catch` narrowing: ```ts try { await api.page.create(input); } catch (err) { if (err instanceof ActionExecutionError) { const code: InferActionErrorCodes | "UNAUTHORIZED" | ... = err.code; } } ``` (Standard framework codes are always possible alongside declared ones.) *Source: [packages/core/src/client.ts:243](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L243)* ### InferActionInput ```ts type InferActionInput = A extends BrickClientAction ? I : A extends { config: { input?: infer S extends TSchema }; } ? Static : A extends { execute(params: { input?: infer I; ctx?: any }): any; } ? I : A extends { run(input?: infer I, ctx?: any): any } ? I : A extends (params: { input?: infer I; ctx?: any }) => any ? I : unknown; ``` Static input type from the schema, with a fallback for structural actions. *Source: [packages/core/src/client.ts:192](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L192)* ### InferActionOutput ```ts type InferActionOutput = A extends BrickClientAction ? O : A extends (...args: any[]) => Promise ? O : A extends { execute(params: any): Promise } ? O : unknown; ``` Resolved output type of an action. *Source: [packages/core/src/client.ts:210](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L210)* ### InferTableInsert ```ts type InferTableInsert = TTable extends { $inferInsert: infer S } ? S : any; ``` *Source: [packages/core/src/types.ts:372](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L372)* ### InferTableRow ```ts type InferTableRow = TTable extends { $inferSelect: infer S } ? S : any; ``` *Source: [packages/core/src/types.ts:369](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L369)* ### NarrowAuthorizedContext ```ts type NarrowAuthorizedContext = TCtx extends { user: infer U } ? [U] extends [null | undefined] ? TCtx : Omit & { user: NonNullable } : TCtx; ``` Type utility that automatically narrows ctx.user from User | null to User in execute() when authorize() is declared on the action. *Source: [packages/core/src/types.ts:56](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L56)* ### OpenApiDocument ```ts type OpenApiDocument = Record; ``` An OpenAPI document as plain JSON. *Source: [packages/core/src/docs.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/docs.ts#L4)* ### PluginRouteMethod ```ts type PluginRouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; ``` *Source: [packages/core/src/plugin.ts:15](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L15)* ### PluginRoutes ```ts type PluginRoutes = | false | Readonly>; ``` *Source: [packages/core/src/plugin.ts:17](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L17)* ### PluginRouteScope ```ts type PluginRouteScope = "app" | "service" | "resource"; ``` *Source: [packages/core/src/plugin.ts:16](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L16)* ### PostgresDatabase ```ts type PostgresDatabase< _TSchema extends Record = Record, > = any; ``` *Source: [packages/core/src/db/postgres.ts:32](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/postgres.ts#L32)* ### PostgresDefinition ```ts type PostgresDefinition< TSchema extends Record = Record, > = DefinitionOptions & EnvOptions, "schema" | "client">> & { engine: "postgres"; path?: never; filename?: never; wal?: never; foreignKeys?: never; client?: PostgresDatabaseConfig["client"]; }; ``` *Source: [packages/core/src/db/define.ts:38](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L38)* ### ResourceIdentity ```ts type ResourceIdentity = O & { id: O extends { id: infer I extends import("drizzle-orm").Column } ? I : PrimaryColumn; }; ``` *Source: [packages/core/src/types.ts:382](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L382)* ### ResourcePluginAPI ```ts type ResourcePluginAPI = P extends { readonly types: infer H extends ResourcePluginTypes; } ? (H & { readonly table: T; readonly context: C; readonly resource: R; })["api"] : P extends ResourcePlugin ? A : never; ``` *Source: [packages/core/src/plugin.ts:102](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/plugin.ts#L102)* ### ResourceTypeOptions ```ts type ResourceTypeOptions = Pick; ``` *Source: [packages/core/src/types.ts:375](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L375)* ### RouteMethod ```ts type RouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; ``` *Source: [packages/core/src/compiler/ir.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/ir.ts#L4)* ### SQLiteDefinition ```ts type SQLiteDefinition< TSchema extends Record = Record, > = DefinitionOptions & EnvOptions, "schema" | "client">> & { engine?: "sqlite"; client?: SQLiteDatabaseConfig["client"]; url?: never; connectionString?: never; }; ``` *Source: [packages/core/src/db/define.ts:28](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/define.ts#L28)* ### ToolConfigWithAuthorize ```ts type ToolConfigWithAuthorize< I extends TSchema | undefined = undefined, O extends TSchema | undefined = undefined, E extends Record = Record< string, ActionErrorDefinition >, C extends ActionContext = ActionContext, > = ActionConfigWithAuthorize & ToolOptions; ``` *Source: [packages/core/src/types.ts:329](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L329)* ### ToolConfigWithoutAuthorize ```ts type ToolConfigWithoutAuthorize< I extends TSchema | undefined = undefined, O extends TSchema | undefined = undefined, E extends Record = Record< string, ActionErrorDefinition >, C extends ActionContext = ActionContext, > = ActionConfigWithoutAuthorize & ToolOptions; ``` *Source: [packages/core/src/types.ts:338](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/types.ts#L338)* ## Variables ### ACTION_ACCESS_KEY ```ts const ACTION_ACCESS_KEY: typeof ACTION_ACCESS_KEY ``` Cross-copy access metadata protocol. CRUD enforces its access policy inside `execute`, so `config.authorize` is absent and introspection can't see it. The plugin stamps each action under this process-global symbol; introspection and OpenAPI read it back. Uses `Symbol.for` so the binding survives a second copy of core (bundler SSR, Vitest), same as the plugin action-binding key. *Source: [packages/core/src/access.ts:18](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/access.ts#L18)* ### compilerRuntime ```ts const compilerRuntime: Readonly<{ emptyServices: Readonly<{}>; defaultRequest: Request; nextSequence: () => string; mergeInput: typeof mergeInput; checkDepth: typeof checkDepth; validate: typeof validate; mapError: typeof mapError; isThenable… ``` *Source: [packages/core/src/compiler/runtime.ts:97](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/compiler/runtime.ts#L97)* ### envSource ```ts const envSource: SecretSource ``` Default source: `process.env` (Bun loads `.env` into it automatically). *Source: [packages/core/src/secrets.ts:27](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/secrets.ts#L27)* ## Re-exported from @sinclair/typebox These come from `@sinclair/typebox`, which documents them. They are exported here so one import covers them. | Name | Kind | Description | | --- | --- | --- | | `Static` | type | Creates a static type from a TypeBox type | | `t` | variable | JavaScript Type Builder with Static Resolution for TypeScript | | `TSchema` | interface | | | `Type` | variable | JavaScript Type Builder with Static Resolution for TypeScript | ## Re-exported from drizzle-orm These come from `drizzle-orm`, which documents them. They are exported here so one import covers them. | Name | Kind | Description | | --- | --- | --- | | `and` | function | Combine a list of conditions with the `and` operator. | | `asc` | function | Used in sorting, this specifies that the given column or expression should be sorted in ascending order. | | `blob` | function | It's recommended to use `text('...', \{ mode: 'json' \})` instead of `blob` in JSON mode, because it supports JSON functions: >All JSON functions currently throw… | | `BunSQLiteDatabase` | class | | | `desc` | function | Used in sorting, this specifies that the given column or expression should be sorted in descending order. | | `eq` | variable | Test that two values are equal. | | `foreignKey` | function | | | `getTableColumns` | function | | | `getTableName` | function | | | `gt` | variable | Test that the first expression passed is greater than the second expression. | | `gte` | variable | Test that the first expression passed is greater than or equal to the second expression. | | `ilike` | function | Case-insensitively compare a column to a pattern, which can include `%` and `_` characters to match multiple variations. | | `inArray` | function | Test whether the first parameter, a column or expression, has a value from a list passed as the second argument. | | `index` | function | | | `integer` | function | | | `is` | function | | | `isNotNull` | function | Test whether an expression is not NULL. | | `isNull` | function | Test whether an expression is NULL. | | `like` | function | Compare a column to a pattern, which can include `%` and `_` characters to match multiple variations. | | `lt` | variable | Test that the first expression passed is less than the second expression. | | `lte` | variable | Test that the first expression passed is less than or equal to the second expression. | | `ne` | variable | Test that two values are not equal. | | `not` | function | Negate the meaning of an expression using the `not` keyword. | | `notInArray` | function | Test whether the first parameter, a column or expression, has a value that is not present in a list passed as the second argument. | | `or` | function | Combine a list of conditions with the `or` operator. | | `primaryKey` | function | | | `real` | function | | | `relations` | function | | | `sql` | function | | | `sqliteTable` | variable | | | `Table` | interface | | | `text` | function | | | `uniqueIndex` | function | | --- # @brickkit/core/client The typed HTTP client a frontend uses to call a Brick application, inferred from its contract. Source: /docs/api-reference/core-client `@brickkit/core/client` exports 14 names: 3 functions, 2 classes, 3 interfaces, 6 types. ```ts import { BrickServerError, BrickTransportError, createBrickClient } from "@brickkit/core/client"; ``` ## Functions | Function | Description | | --- | --- | | [`createBrickClient`](#createbrickclient) | Create an Eden/tRPC-style typed client for the application contract `TApp`. | | [`isBrickServerError`](#isbrickservererror) | Whether an error from the typed client is a `BrickServerError`: the server answered with a 5xx that is not one of the contract's declared errors, and the error… | | [`isBrickTransportError`](#isbricktransporterror) | Whether an error from the typed client is a `BrickTransportError`: the request got no meaningful answer from the server (a network failure, a body that was not… | ### createBrickClient ```ts function createBrickClient>( options: BrickClientOptions & { contract: TApp }, ): BrickClient function createBrickClient>( options: BrickClientOptions, ): BrickClient ``` Create an Eden/tRPC-style typed client for the application contract `TApp`. `TApp` is a type-only parameter: `import type { AppServer }` keeps backend implementations out of the frontend bundle. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`BrickClientOptions`](#brickclientoptions) `& { contract: TApp }` | | **Returns** [`BrickClient`](#brickclient)`` *Source: [packages/core/src/client.ts:572](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L572)* ### isBrickServerError ```ts function isBrickServerError(err: unknown): err is BrickServerError ``` Whether an error from the typed client is a `BrickServerError`: the server answered with a 5xx that is not one of the contract's declared errors, and the error carries its `status`, `url`, and `body`. **Parameters** | Name | Type | Description | | --- | --- | --- | | `err` | `unknown` | What a client call threw. | **Returns** `err is` [`BrickServerError`](#brickservererror) — `true` for a server error, narrowing its type. *Source: [packages/core/src/client.ts:187](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L187)* ### isBrickTransportError ```ts function isBrickTransportError(err: unknown): err is BrickTransportError ``` Whether an error from the typed client is a `BrickTransportError`: the request got no meaningful answer from the server (a network failure, a body that was not JSON, or a 4xx outside the declared contract). **Parameters** | Name | Type | Description | | --- | --- | --- | | `err` | `unknown` | What a client call threw. | **Returns** `err is` [`BrickTransportError`](#bricktransporterror) — `true` for a transport error, narrowing its type. *Source: [packages/core/src/client.ts:173](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L173)* ## Classes ### BrickServerError ```ts class BrickServerError extends Error ``` The server answered 5xx without a declared `code` / `ValidationError` shape, i.e. an unexpected failure rather than a modelled domain error. **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new (message: string, opts: { status: number; url: string; body: unknown })` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"server"` | | | `readonly status` | `number` | | | `readonly url` | `string` | | | `readonly body` | `unknown` | | *Source: [packages/core/src/client.ts:147](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L147)* ### BrickTransportError ```ts class BrickTransportError extends Error ``` The request never received a meaningful server answer: DNS / connection failure, a malformed body, or a 4xx response outside the declared contract (no `code` and no `ValidationError` shape). **Constructor** | Name | Type | Description | | --- | --- | --- | | `constructor` | `new ( message: string, opts: { status?: number; url: string; body?: unknown; cause?: unknown }, )` | | **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly kind` | `"transport"` | | | `readonly status?` | `number` | | | `readonly url` | `string` | | | `readonly body?` | `unknown` | | | `readonly cause?` | `unknown` | | *Source: [packages/core/src/client.ts:123](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L123)* ## Interfaces ### BrickCallOptions ```ts interface BrickCallOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `headers?` | `Record` | Extra headers merged over the client defaults for this call only. | | `signal?` | `AbortSignal` | Abort signal for this call only. Never wrapped: aborts reject with `AbortError`. | | `timeoutMs?` | `number` | Per-call timeout in milliseconds. Implemented with `AbortSignal.timeout`. | *Source: [packages/core/src/client.ts:109](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L109)* ### BrickClientAction ```ts interface BrickClientAction ``` Browser-safe action metadata generated by the Brick Vite plugin. **Properties** | Name | Type | Description | | --- | --- | --- | | `kind` | `"action"` | | | `name` | `string` | | | `serviceName` | `string` | | | `plugin?` | `string` | Owning plugin descriptor name for plugin actions (e.g. `@brickkit/crud`). | | `resource?` | `string` | Owning resource name for resource-plugin actions (e.g. `page`). | | `config` | `{ path: string; method: "GET" \| "POST" \| "PUT" \| "PATCH" \| "DELETE" }` | | | `readonly __types?` | `{ input: TInput; output: TOutput; errors: TErrors }` | Type information only; never emitted at runtime. | *Source: [packages/core/src/client.ts:92](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L92)* ### BrickClientOptions ```ts interface BrickClientOptions ``` Eden/tRPC-style typed HTTP client inferred from Brick-TS application contracts. The frontend imports only the contract *type* — never a backend implementation: ```ts import { createBrickClient } from "@brickkit/core/client"; import type { AppServer } from "./server"; // `import type`: erased at build const api = createBrickClient({ baseUrl: "http://localhost:4000" }); const pages = await api.page.list({ limit: 10, sort: "-createdAt" }); const created = await api.page.create({ title: "Hello", content: "World" }); ``` Build the contract from individually-typed actions (each `defineAction` call preserves its input/output/error literal types): ```ts // server.ts (backend) export const listPages = pagesService.action({ name: "page.list", ... }); export const createPage = pagesService.action({ name: "page.create", ... }); export const contract = { page: { list: listPages, create: createPage } }; export type AppServer = typeof contract; ``` **Result / error convention** The client uses a throw-based convention that mirrors local `action.execute` semantics, because TypeScript promises cannot encode thrown exception types: - Success → the promise resolves with the action output. - Declared domain failures (your `errors: { SLUG_EXISTS: ... }`) and framework guards (UNAUTHORIZED/FORBIDDEN/NOT_FOUND/...) → rejected with [`ActionExecutionError`](/docs/api-reference/core#actionexecutionerror) carrying the declared `code`, `status`, `message`, and `details`. Narrow with `instanceof` and read `err.code`. - Input/output validation failures → rejected with [`ValidationError`](/docs/api-reference/core#validationerror), exactly as local execution throws. - Network failures, unparsable bodies, and non-OK responses outside the declared contract → [`BrickTransportError`](#bricktransporterror) (4xx) or [`BrickServerError`](#brickservererror) (5xx). - Cancellation via `AbortSignal` → the native `AbortError` propagates unwrapped so `err.name === "AbortError"` keeps working. **URL mapping** A client path is a namespace walk ending in an action call. The leaf segment decides the route, matching the server compiler (`packages/cli/src/compiler`): | Client call | HTTP request | |---|---| Generated browser contracts carry each action's HTTP method and path, including plugin routes and consumer overrides. Without runtime metadata, callers use `POST /api//` with dotted action names. GET/DELETE input is serialized as a query string; other methods send JSON. **Properties** | Name | Type | Description | | --- | --- | --- | | `baseUrl` | `string` | Server origin, e.g. `"http://localhost:4000"`. No trailing slash required. | | `prefix?` | `string` | Route prefix the server was mounted with (default `"/api"`). Must match the `prefix` passed to `brick({ prefix })`. | | `headers?` | `\| Record \| (() => Record \| Promise>)` | Default headers sent with every call (e.g. an `Authorization` bearer). A thunk is re-evaluated per call, so rotated tokens stay fresh. | | `fetch?` | `(input: RequestInfo \| URL, init?: RequestInit) => Promise` | `fetch` implementation. Defaults to `globalThis.fetch`. | | `signal?` | `AbortSignal` | Default signal applied to every call (per-call `signal` wins). | | `timeoutMs?` | `number` | Default per-call timeout in milliseconds (per-call `timeoutMs` wins). | | `contract?` | `Record` | Browser-safe route metadata generated by the Brick Vite plugin. Required for custom methods/paths and plugin routes. Without a contract, calls use POST and service/action names inferred from namespace keys. | *Source: [packages/core/src/client.ts:62](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L62)* ## Types ### BrickActionCaller ```ts type BrickActionCaller = undefined extends InferActionInput ? ( input?: InferActionInput, options?: BrickCallOptions, ) => Promise> : ( input: InferActionInput, options?: BrickCallOptions, ) => Promise>; ``` Typed caller for one action. Required inputs stay required; actions whose input includes `undefined` (no schema / all-optional) can be called bare. *Source: [packages/core/src/client.ts:259](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L259)* ### BrickClient ```ts type BrickClient = T extends | BrickClientAction | Action ? BrickActionCaller : T extends (...args: any[]) => any ? BrickActionCaller : T extends object ? { [K in keyof T]: BrickClient } : T; ``` Recursively maps action leaves and their namespaces to browser callers. *Source: [packages/core/src/client.ts:271](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L271)* ### InferActionErrorCodes ```ts type InferActionErrorCodes = A extends BrickClientAction ? Extract : A extends { config: { errors?: infer E }; } ? E extends Record ? Extract : never : never; ``` Union of error `code`s declared in an action's `errors` map. *Source: [packages/core/src/client.ts:220](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L220)* ### InferActionFailure ```ts type InferActionFailure = ActionExecutionError & { code: | InferActionErrorCodes | "BAD_REQUEST" | "UNAUTHORIZED" | "FORBIDDEN" | "NOT_FOUND" | "CONFLICT" | "INTERNAL_SERVER_ERROR" | "VALIDATION_FAILED"; }; ``` The declared failure shape of an action for `catch` narrowing: ```ts try { await api.page.create(input); } catch (err) { if (err instanceof ActionExecutionError) { const code: InferActionErrorCodes | "UNAUTHORIZED" | ... = err.code; } } ``` (Standard framework codes are always possible alongside declared ones.) *Source: [packages/core/src/client.ts:243](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L243)* ### InferActionInput ```ts type InferActionInput = A extends BrickClientAction ? I : A extends { config: { input?: infer S extends TSchema }; } ? Static : A extends { execute(params: { input?: infer I; ctx?: any }): any; } ? I : A extends { run(input?: infer I, ctx?: any): any } ? I : A extends (params: { input?: infer I; ctx?: any }) => any ? I : unknown; ``` Static input type from the schema, with a fallback for structural actions. *Source: [packages/core/src/client.ts:192](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L192)* ### InferActionOutput ```ts type InferActionOutput = A extends BrickClientAction ? O : A extends (...args: any[]) => Promise ? O : A extends { execute(params: any): Promise } ? O : unknown; ``` Resolved output type of an action. *Source: [packages/core/src/client.ts:210](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/client.ts#L210)* --- # @brickkit/core/tanstack-query TanStack Query options and keys for the typed client's actions. Source: /docs/api-reference/core-tanstack-query `@brickkit/core/tanstack-query` exports 3 names: 1 function, 2 types. ```ts import { createBrickQueryClient } from "@brickkit/core/tanstack-query"; ``` ## Functions | Function | Description | | --- | --- | | [`createBrickQueryClient`](#createbrickqueryclient) | Query helpers share the ordinary Brick HTTP client, including custom routes. | ### createBrickQueryClient ```ts function createBrickQueryClient>( options: BrickClientOptions & { contract: T }, ): BrickQueryClient ``` Query helpers share the ordinary Brick HTTP client, including custom routes. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`BrickClientOptions`](/docs/api-reference/core#brickclientoptions) `& { contract: T }` | | **Returns** [`BrickQueryClient`](#brickqueryclient)`` *Source: [packages/core/src/tanstack-query.ts:63](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/tanstack-query.ts#L63)* ## Types ### BrickQueryAction ```ts type BrickQueryAction = BrickActionCaller & { /** Omit options for an action-wide invalidation key. */ queryKey: { (): ActionKey; (options: { input: InferActionInput; }): DataTag>; }; queryOptions: (...args: InputArgs) => { queryKey: DataTag>; queryFn: ( context: Pick, ) => Promise>; }; mutationOptions: (options?: { request?: BrickCallOptions }) => { mutationKey: ActionKey; mutationFn: (input: InferActionInput) => Promise>; }; }; ``` *Source: [packages/core/src/tanstack-query.ts:35](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/tanstack-query.ts#L35)* ### BrickQueryClient ```ts type BrickQueryClient = T extends | BrickClientAction | Action | ((...args: never[]) => unknown) ? BrickQueryAction : { [K in keyof T]: BrickQueryClient }; ``` *Source: [packages/core/src/tanstack-query.ts:55](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/tanstack-query.ts#L55)* --- # @brickkit/core/pg Postgres databases for Brick services, with Drizzle's table builders. Source: /docs/api-reference/core-pg `@brickkit/core/pg` exports 1 name: 1 type. It also re-exports 32 from `drizzle-orm`. ```ts import type { PgDatabase } from "@brickkit/core/pg"; ``` ## Types ### PgDatabase ```ts type PgDatabase< TSchema extends Record = Record, > = PostgresJsDatabase; ``` Typed Postgres Drizzle instance, as held by `defineDatabase({engine})`. *Source: [packages/core/src/db/pg.ts:49](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/core/src/db/pg.ts#L49)* ## Re-exported from drizzle-orm These come from `drizzle-orm`, which documents them. They are exported here so one import covers them. | Name | Kind | Description | | --- | --- | --- | | `bigint` | function | | | `bigserial` | function | | | `boolean` | function | | | `char` | function | | | `check` | function | | | `date` | function | | | `decimal` | variable | | | `doublePrecision` | function | | | `foreignKey` | function | | | `index` | function | | | `integer` | function | | | `interval` | function | | | `json` | function | | | `jsonb` | function | | | `numeric` | function | | | `pgEnum` | function | | | `pgSchema` | function | | | `pgTable` | variable | | | `pgView` | function | | | `PostgresJsDatabase` | class | | | `primaryKey` | function | | | `real` | function | | | `relations` | function | | | `serial` | function | | | `smallint` | function | | | `sql` | function | | | `text` | function | | | `time` | function | | | `timestamp` | function | | | `uniqueIndex` | function | | | `uuid` | function | | | `varchar` | function | | --- # @brickkit/cli The brick command's building blocks: the dev and production servers, contract generation, and the OpenAPI pages. Source: /docs/api-reference/cli `@brickkit/cli` exports 6 names: 4 functions, 2 interfaces. ```ts import { buildApplication, emitCompiledApplication, generateClientContract } from "@brickkit/cli"; ``` ## Functions | Function | Description | | --- | --- | | [`buildApplication`](#buildapplication) | Bundle an exported BrickApp with generated handlers and its original configuration. | | [`emitCompiledApplication`](#emitcompiledapplication) | An ES module of real JS functions; it never stringifies application callbacks. | | [`generateClientContract`](#generateclientcontract) | Generate browser metadata and transport types from the app's selected services. | | [`startDevServer`](#startdevserver) | What `brick dev` runs: start the application, restart it when its files change, and serve the dashboard beside it. | ### buildApplication ```ts function buildApplication( options: BuildApplicationOptions, ): Promise<{ outdir: string; entry: string; ir: any; }> ``` Bundle an exported BrickApp with generated handlers and its original configuration. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`BuildApplicationOptions`](#buildapplicationoptions) | | **Returns** `Promise<{ outdir: string; entry: string; ir: any; }>` *Source: [packages/cli/src/build.ts:17](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/build.ts#L17)* ### emitCompiledApplication ```ts function emitCompiledApplication( ir: BrickIR, runtimeImport = "@brickkit/core", ): string ``` An ES module of real JS functions; it never stringifies application callbacks. **Parameters** | Name | Type | Description | | --- | --- | --- | | `ir` | [`BrickIR`](/docs/api-reference/core#brickir) | | | `runtimeImport?` | `string` default `"@brickkit/core"` | | **Returns** `string` *Source: [packages/cli/src/compiler/emit.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/compiler/emit.ts#L4)* ### generateClientContract ```ts function generateClientContract(app: BrickApp): string ``` Generate browser metadata and transport types from the app's selected services. **Parameters** | Name | Type | Description | | --- | --- | --- | | `app` | [`BrickApp`](/docs/api-reference/core#brickapp) | | **Returns** `string` *Source: [packages/cli/src/client-contract.ts:7](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/client-contract.ts#L7)* ### startDevServer ```ts function startDevServer(options: DevServerOptions = {}): Promise ``` What `brick dev` runs: start the application, restart it when its files change, and serve the dashboard beside it. With an `entry`, the app runs in a worker process that restarts on every change to the entry or the files it imports, and reports its requests to the dashboard; without one, a server with the registered services starts in this process. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options?` | [`DevServerOptions`](#devserveroptions) default `{}` | `entry`, `port` (else `PORT`, else 4000), `prefix`, `clientOutput` to regenerate the typed client's contract on each start, `frontend` to launch a frontend command once, and `dashboard` (`port` and `historyDays`, or `false`). | **Returns** `Promise` — The application, when started in this process. **Throws** - When `frontend` is given without `clientOutput`, or the dashboard would use the app's port. *Source: [packages/cli/src/runner.ts:236](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/runner.ts#L236)* ## Interfaces ### BuildApplicationOptions ```ts interface BuildApplicationOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `entry` | `string` | A module whose default export is a BrickApp, without starting a server. | | `outdir?` | `string` | | | `port?` | `number` | | | `aot?` | `boolean` | Precompile Elysia's HTTP handlers at build time (default true). | *Source: [packages/cli/src/build.ts:7](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/build.ts#L7)* ### DevServerOptions ```ts interface DevServerOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `port?` | `number` | | | `entry?` | `string` | | | `prefix?` | `string` | | | `clientOutput?` | `string` | Generate a browser contract before starting the backend and frontend. | | `frontend?` | `string` | Frontend command, launched once after the first successful generation. | | `dashboard?` | `{ port: number; historyDays?: number } \| false` | Local dashboard with live requests, traces, errors, and reloads. Needs an entry. `historyDays` is how long finished requests are kept on disk (default 90); zero keeps live data only. | *Source: [packages/cli/src/runner.ts:26](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/runner.ts#L26)* --- # @brickkit/cli/compiler Compile and bind a Brick application's routes ahead of time. Source: /docs/api-reference/cli-compiler `@brickkit/cli/compiler` exports 1 name: 1 function. ```ts import { emitCompiledApplication } from "@brickkit/cli/compiler"; ``` ## Functions | Function | Description | | --- | --- | | [`emitCompiledApplication`](#emitcompiledapplication) | An ES module of real JS functions; it never stringifies application callbacks. | ### emitCompiledApplication ```ts function emitCompiledApplication( ir: BrickIR, runtimeImport = "@brickkit/core", ): string ``` An ES module of real JS functions; it never stringifies application callbacks. **Parameters** | Name | Type | Description | | --- | --- | --- | | `ir` | [`BrickIR`](/docs/api-reference/core#brickir) | | | `runtimeImport?` | `string` default `"@brickkit/core"` | | **Returns** `string` *Source: [packages/cli/src/compiler/emit.ts:4](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/compiler/emit.ts#L4)* --- # @brickkit/cli/vite The Vite plugin that generates the typed client contract and serves the dashboard. Source: /docs/api-reference/cli-vite `@brickkit/cli/vite` exports 2 names: 1 function, 1 interface. ```ts import { brickClient } from "@brickkit/cli/vite"; ``` ## Functions | Function | Description | | --- | --- | | [`brickClient`](#brickclient) | Load a BrickApp through Vite's server module graph, including dependency HMR. | ### brickClient ```ts function brickClient(options: BrickClientPluginOptions): Plugin ``` Load a BrickApp through Vite's server module graph, including dependency HMR. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`BrickClientPluginOptions`](#brickclientpluginoptions) | | **Returns** `Plugin` *Source: [packages/cli/src/vite.ts:33](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/vite.ts#L33)* ## Interfaces ### BrickClientPluginOptions ```ts interface BrickClientPluginOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `entry` | `string` | Any module whose default export is a BrickApp; do not call listen() there. | | `output?` | `string` | Generated browser contract, relative to the Vite root (default _brick/contract.ts). | | `dashboard?` | `{ port?: number; historyDays?: number } \| false` | Local dashboard for the app's requests, traces, errors, and reloads, served during `vite dev` (default: on at port 9400). Apps must pass `telemetry: true` to brick() to record requests. `historyDays` is how long finished requests are kept in `.brick/` (default 90, 0 for none). Set to false to disable. | *Source: [packages/cli/src/vite.ts:17](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/vite.ts#L17)* --- # @brickkit/cli/dev-reporter Report a worker process's requests to a running dev dashboard. Source: /docs/api-reference/cli-dev-reporter `@brickkit/cli/dev-reporter` exports 2 names: 1 function, 1 interface. ```ts import { installDevReporter } from "@brickkit/cli/dev-reporter"; ``` ## Functions | Function | Description | | --- | --- | | [`installDevReporter`](#installdevreporter) | Report this process's spans to a running dashboard. | ### installDevReporter ```ts function installDevReporter(options: DevReporterOptions): boolean ``` Report this process's spans to a running dashboard. Dev servers that evaluate the app in a worker (Vite with Nitro or TanStack Start) cannot share memory with the dashboard, so the worker posts its spans over loopback HTTP. Call before the app is constructed. Reporting is best effort: a dashboard that is not running never affects the app. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`DevReporterOptions`](#devreporteroptions) | | **Returns** `boolean` *Source: [packages/cli/src/dev-reporter.ts:15](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/dev-reporter.ts#L15)* ## Interfaces ### DevReporterOptions ```ts interface DevReporterOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `url` | `string` | The dashboard's ingest endpoint, for example http://127.0.0.1:9400/api/ingest. | *Source: [packages/cli/src/dev-reporter.ts:3](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/cli/src/dev-reporter.ts#L3)* --- # @brickkit/crud CRUD actions for a resource, with access rules, filters, and hooks. Source: /docs/api-reference/crud `@brickkit/crud` exports 17 names: 1 function, 5 interfaces, 11 types. ```ts import { crud } from "@brickkit/crud"; ``` ## Functions | Function | Description | | --- | --- | | [`crud`](#crud) | A plugin that gives a resource its CRUD actions: `list`, `get`, `create`, `update`, `delete`, `findOne`, `count`, and `exists`, with validated input and output… | ### crud ```ts function crud< const F extends Fields, const O extends Operations = {}, >( options: CrudOptions, ): CrudPlugin ``` A plugin that gives a resource its CRUD actions: `list`, `get`, `create`, `update`, `delete`, `findOne`, `count`, and `exists`, with validated input and output, filters and sorting, and access rules. Install it with `resource.use(crud({ ... }))`. The actions are named `.` and routed under `/api/`. It denies by default: `access` is required. A column is hidden from reads, writes, and filters until its `fields` entry allows it. **Parameters** | Name | Type | Description | | --- | --- | --- | | `options` | [`CrudOptions`](#crudoptions)`` | `fields` (per column: `read`, `create`, `update`, `filter` operators, and `sort`), `access` (`"public"`, or `{ authorize, scope }`), `operations` to switch some off, `routes` to move or disable routes, `hooks` around create, update, and delete, and `defaultLimit` and `maxLimit` for lists. | **Returns** [`CrudPlugin`](#crudplugin)`` — The plugin, for `resource.use`, which returns the resource with its actions attached so they can be called in-process. **Example** ```ts import { crud } from "@brickkit/crud"; export const page = pagesService .resource({ name: "page", table: pages, id: pages.id }) .use( crud({ access: "public", fields: { id: { read: true, filter: ["eq", "in"] }, title: { read: true, create: true, update: true, sort: true }, }, }), ); ``` *Source: [packages/crud/src/index.ts:225](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/index.ts#L225)* ## Interfaces ### ColumnFieldPolicy ```ts interface ColumnFieldPolicy ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `read?` | `boolean` | | | `create?` | `boolean` | | | `update?` | `boolean` | | | `filter?` | `readonly` [`FilterOperator`](#filteroperator)`[]` | | | `sort?` | `boolean` | | | `schema?` | `TSchema` | | | `resolve?` | `never` | | | `scrub?` | `(args: { value: any; ctx:` [`ActionContext`](/docs/api-reference/core#actioncontext)`; }) => unknown \| Promise` | | *Source: [packages/crud/src/types.ts:26](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L26)* ### CrudOptions ```ts interface CrudOptions ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `fields` | `F` | | | `operations?` | `O` | | | `routes?` | [`PluginRoutes`](/docs/api-reference/core#pluginroutes) | | | `defaultLimit?` | `number` | | | `maxLimit?` | `number` | | | `access` | `\| "public" \| { authorize?: (args:` [`CrudAuthorizeArgs`](#crudauthorizeargs)`) => boolean \| Promise; scope?: (args: { operation:` [`Operation`](#operation)`; ctx:` [`ActionContext`](/docs/api-reference/core#actioncontext)`; }) => SQL \| undefined \| Promise; }` | | | `hooks?` | `{ beforeCreate?: (args: { data: Record; ctx:` [`ActionContext`](/docs/api-reference/core#actioncontext)`; error:` [`ErrorBuilder`](/docs/api-reference/core#errorbuilder)`>; }) => void \| Promise; afterCreate?: (args: { row: Record; ctx: ActionContex…` | | *Source: [packages/crud/src/types.ts:65](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L65)* ### CrudTypes ```ts interface CrudTypes ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `readonly table` | `unknown` | | | `readonly context` | `unknown` | | | `readonly resource` | `unknown` | | | `readonly api` | `this["table"] extends Table ?` [`CrudAPI`](#crudapi)` : this["table"]["$inferSelect"][keyof this["table…` | | *Source: [packages/crud/src/types.ts:394](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L394)* ### DerivedFieldPolicy ```ts interface DerivedFieldPolicy ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `read` | `true` | | | `schema` | `TSchema` | | | `resolve` | `(args: { rows: readonly Readonly>[]; ctx:` [`ActionContext`](/docs/api-reference/core#actioncontext)`; }) => readonly unknown[] \| Promise` | One value per authorized parent row, in the same order. | | `create?` | `false` | | | `update?` | `false` | | | `filter?` | `readonly []` | | | `sort?` | `false` | | | `scrub?` | [`ColumnFieldPolicy`](#columnfieldpolicy)`["scrub"]` | | *Source: [packages/crud/src/types.ts:39](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L39)* ### Query ```ts interface Query[] | undefined = undefined, I extends readonly IncludeKeys[] | undefined = undefined> ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `select?` | `S` | | | `include?` | `I` | | | `where?` | [`Where`](#where)`` | | | `orderBy?` | `readonly { field: Extract, keyof Row>; direction: "asc" \| "desc"; }[]` | | | `limit?` | `number` | | | `offset?` | `number` | | | `includeTotal?` | `boolean` | | *Source: [packages/crud/src/types.ts:179](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L179)* ## Types ### CrudAPI ```ts type CrudAPI[keyof Row]> = [ O, ] extends [false] ? {} : Pick< AllAPI, { [K in Operation]: O extends Record ? never : K }[Operation] >; ``` *Source: [packages/crud/src/types.ts:386](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L386)* ### CrudAuthorizeArgs ```ts type CrudAuthorizeArgs = { [K in Operation]: { operation: K; input: CrudInputs[K]; ctx: ActionContext; }; }[Operation]; ``` Narrow operation and input together in an authorize callback. *Source: [packages/crud/src/types.ts:242](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L242)* ### CrudInputs ```ts type CrudInputs = { list: { [K in keyof AuthorizeQuery]: AuthorizeQuery[K] }; findOne: { [K in keyof AuthorizeQuery]: AuthorizeQuery[K] }; get: { id: unknown; select?: readonly Exclude, IncludeKeys>[]; include?: readonly IncludeKeys[]; }; create: AuthorizeWrite; update: { id: unknown; data: AuthorizeWrite }; delete: { id: unknown }; count: { where?: AuthorizeQuery["where"] }; exists: { where?: AuthorizeQuery["where"] }; }; ``` Validated authorization inputs, indexed by CRUD operation. **Properties** | Name | Type | Description | | --- | --- | --- | | `list` | `{ [K in keyof AuthorizeQuery]: AuthorizeQuery[K] }` | | | `findOne` | `{ [K in keyof AuthorizeQuery]: AuthorizeQuery[K] }` | | | `get` | `{ id: unknown; select?: readonly Exclude, IncludeKeys>[]; include?: readonly IncludeKeys[]; }` | | | `create` | `AuthorizeWrite` | | | `update` | `{ id: unknown; data: AuthorizeWrite }` | | | `delete` | `{ id: unknown }` | | | `count` | `{ where?: AuthorizeQuery["where"] }` | | | `exists` | `{ where?: AuthorizeQuery["where"] }` | | *Source: [packages/crud/src/types.ts:227](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L227)* ### CrudPlugin ```ts type CrudPlugin = ResourcePlugin< any, any, object > & { readonly types: CrudTypes }; ``` *Source: [packages/crud/src/types.ts:410](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L410)* ### FieldPolicy ```ts type FieldPolicy = ColumnFieldPolicy | DerivedFieldPolicy; ``` *Source: [packages/crud/src/types.ts:53](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L53)* ### Fields ```ts type Fields = Readonly>; ``` *Source: [packages/crud/src/types.ts:54](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L54)* ### FilterOperator ```ts type FilterOperator = | "eq" | "ne" | "in" | "notIn" | "gt" | "gte" | "lt" | "lte" | "between" | "contains" | "startsWith" | "endsWith" | "isNull" | "isNotNull"; ``` *Source: [packages/crud/src/types.ts:11](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L11)* ### Operation ```ts type Operation = | "list" | "get" | "create" | "update" | "delete" | "findOne" | "count" | "exists"; ``` *Source: [packages/crud/src/types.ts:55](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L55)* ### Operations ```ts type Operations = false | Partial>; ``` *Source: [packages/crud/src/types.ts:64](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L64)* ### ReadRow ```ts type ReadRow = { [K in ReadKeys]: FieldValue; }; ``` *Source: [packages/crud/src/types.ts:140](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L140)* ### Where ```ts type Where = FilterWhere>; ``` *Source: [packages/crud/src/types.ts:178](https://github.com/Chandraprakash-Darji/brick/blob/main/packages/crud/src/types.ts#L178)* --- # Related Tables Modeling parent-child relationships and joins with Drizzle ORM. Source: /docs/child-tables > This guide has been moved to **[Relational Data Modeling & > Joins](/docs/relations)**. Brick-TS models child tables and relational joins using pure Drizzle ORM and standard relational database constraints. Please see the canonical guide: **[Relational Data Modeling & Joins](/docs/relations)**. ### Schema The related-table example uses these application-owned tables. `target_id` is a logical reference; the example does not declare a SQL foreign key. #### Table Name: `articles` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | #### Table Name: `comments` | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `target_id` | `text` | Logical parent article identifier; no SQL foreign-key constraint. | | `body` | `text` | Text content. | --- # Type Inference Without Codegen Static types from schemas and tables with no build step. Source: /docs/codegen > This guide has been moved to **[Zero-Codegen Type > Inference](/docs/type-inference)**. Brick-TS delivers instant static typing from runtime TypeBox schemas and Drizzle models without intermediate code generation. Please see the canonical guide: **[Zero-Codegen Type Inference](/docs/type-inference)**. --- # Schema Management Creating tables in development and managing production migrations. Source: /docs/migrations > This guide has been moved to **[Database Management & > Migrations](/docs/database)**. Learn how to configure databases, run development table synchronization with `syncSchema`, and manage production PostgreSQL migrations with Drizzle Kit. Please see the canonical guide: **[Database Management & Migrations](/docs/database)**. ### Schema This is the example table in [Database Management & Migrations](/docs/database). `syncSchema` creates your declared tables; it does not add framework columns. #### Table Name: `users` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `email` | `text` | Unique email address. Unique | --- # Resource Lifecycle Lifecycle phases, preparation, and hooks for resources and services. Source: /docs/resource-lifecycle > This guide has been consolidated into **[Resources and > CRUD](/docs/resources)** and **[Lifecycle Hooks](/docs/hooks)**. Learn about resource identity, field policies, prepared statement optimizations, and CRUD mutation hooks. - See **[Resources and CRUD](/docs/resources)** for resource configuration and operations. - See **[Lifecycle Hooks](/docs/hooks)** for before and after write hooks. ### Schema Brick does not generate owner or timestamp columns. Declare them in your table and populate them with Drizzle defaults or CRUD hooks when needed. These columns match the Drizzle tables in the example. Defaults marked as application defaults run in Drizzle, rather than in SQL. #### Table Name: `pages` | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `title` | `text` | Display title. | | `content` | `text` | Page content. Optional | | `internal_notes` | `text` | Internal notes, hidden by the CRUD field policy. Optional | > **CRUD is Deny-by-Default**: `access` is a required option. `access: "public"` allows every call. `access: { authorize, scope }` calls your `authorize` on every operation and adds your `scope` condition to queries. An access object without `authorize` rejects every call with `FORBIDDEN` (HTTP 403). --- # Singleton Records Model singleton constraints and single-row tables in the database. Source: /docs/singletons > This guide has been moved to **[Relational Data Modeling & > Joins](/docs/relations)**. In Brick-TS, singleton configuration rows are enforced using standard database uniqueness constraints and retrieved via `findOne`. Please see the canonical guide: **[Relational Data Modeling & Joins](/docs/relations)**. ### Schema These columns match the singleton example in [Relational Data Modeling & Joins](/docs/relations). The unique discriminator rejects duplicate values, but the application must keep it at `1` to maintain one row. #### Table Name: `app_settings` | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------------ | | `id` | `text` | Row identifier. PK Default: global_settings (application) | | `singleton_key` | `integer` | Singleton discriminator. Unique Optional Default: 1 | | `site_name` | `text` | Site display name. | | `maintenanceMode` | `boolean` | Whether maintenance mode is enabled. Optional Default: false | --- # AI Tools and Model Context Protocol Exposing actions to AI agents via Model Context Protocol (MCP). Source: /docs/tools > This guide has been moved to **[AI Tools and Model Context Protocol > (MCP)](/docs/tools-and-mcp)**. Define specialized tools with object schemas and serve them via JSON-RPC 2.0. Please see the canonical guide: **[AI Tools and Model Context Protocol (MCP)](/docs/tools-and-mcp)**. --- # Input and Output Validation Validating actions with TypeBox schemas and error helpers. Source: /docs/validate-hook > This guide has been moved to **[Validation and > Errors](/docs/validation-and-errors)**. In Brick-TS, validation is powered directly by TypeBox schemas on actions (`input` and `output`) and the injected `error` helpers. Please see the canonical guide: **[Validation and Errors](/docs/validation-and-errors)**. ## Error responses Input validation throws `ValidationError` with code `VALIDATION_FAILED` and status 400. Output validation uses the same code with status 500. Authorization failures use `UNAUTHORIZED` with status 401 when `ctx.user` is absent, and `FORBIDDEN` with status 403 when a signed-in user lacks access. HTTP errors share `{ name, code, message, status, requestId, details?, errors? }`. Unexpected errors return `INTERNAL_SERVER_ERROR` with a generic message. The server logs the original error and its cause with the request ID. Error helpers accept an optional third argument, such as `error.CONFLICT("Already exists", { id }, { cause })`; causes are never serialized. --- # Generators Generate OpenAPI specifications and typed browser client contracts. Source: /docs/brick-cli/generate > This guide has been moved to **[Code & Spec > Generators](/docs/brick-cli/gen)**. Generate OpenAPI 3.1 specifications and typed client contracts with `brick gen`. Please see the canonical guide: **[Code & Spec Generators](/docs/brick-cli/gen)**. --- # Database Migrations Database configuration in development and caller-owned PostgreSQL migrations. Source: /docs/brick-cli/migrate > This guide has been moved to **[Database Management & > Migrations](/docs/database)**. Brick-TS uses explicit `syncSchema` helpers for SQLite in development and caller-owned migrations via Drizzle Kit for PostgreSQL. Please see the canonical guide: **[Database Management & Migrations](/docs/database)**. ### Schema The database guide uses this application-owned table. Apply migrations to the tables declared in your Drizzle schema. #### Table Name: `users` | Field | Type | Description | | ------- | ------ | ------------------------------------------------------------- | | `id` | `text` | Row identifier. PK Default: crypto.randomUUID() (application) | | `email` | `text` | Unique email address. Unique | --- # Architecture Export Export and inspect the service mesh architecture with brick info. Source: /docs/brick-cli/schema-dump > This guide has been moved to **[Architecture > Introspection](/docs/brick-cli/info)**. Export the service graph and mounted endpoints using `brick info` and the `/_brick/services` route. Please see the canonical guide: **[Architecture Introspection](/docs/brick-cli/info)**.