brick.ts

@brickkit/core/client

The typed HTTP client a frontend uses to call a Brick application, inferred from its contract.

llms.txt

@brickkit/core/client exports 14 names: 3 functions, 2 classes, 3 interfaces, 6 types.

import { BrickServerError, BrickTransportError, createBrickClient } from "@brickkit/core/client";

Functions

FunctionDescription
createBrickClientCreate an Eden/tRPC-style typed client for the application contract TApp.
isBrickServerErrorWhether 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…
isBrickTransportErrorWhether 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

function createBrickClient<TApp extends Record<string, any>>(
  options: BrickClientOptions & { contract: TApp },
): BrickClient<TApp>

function createBrickClient<TApp extends Record<string, any>>(
  options: BrickClientOptions,
): BrickClient<TApp>

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

NameTypeDescription
optionsBrickClientOptions & { contract: TApp }

Returns BrickClient<TApp>

Source: packages/core/src/client.ts:572

isBrickServerError

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

NameTypeDescription
errunknownWhat a client call threw.

Returns err is BrickServerError — true for a server error, narrowing its type.

Source: packages/core/src/client.ts:187

isBrickTransportError

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

NameTypeDescription
errunknownWhat a client call threw.

Returns err is BrickTransportError — true for a transport error, narrowing its type.

Source: packages/core/src/client.ts:173

Classes

BrickServerError

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

NameTypeDescription
constructornew (message: string, opts: { status: number; url: string; body: unknown })

Properties

NameTypeDescription
readonly kind"server"
readonly statusnumber
readonly urlstring
readonly bodyunknown

Source: packages/core/src/client.ts:147

BrickTransportError

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

NameTypeDescription
constructornew ( message: string, opts: { status?: number; url: string; body?: unknown; cause?: unknown }, )

Properties

NameTypeDescription
readonly kind"transport"
readonly status?number
readonly urlstring
readonly body?unknown
readonly cause?unknown

Source: packages/core/src/client.ts:123

Interfaces

BrickCallOptions

interface BrickCallOptions

Properties

NameTypeDescription
headers?Record<string, string>Extra headers merged over the client defaults for this call only.
signal?AbortSignalAbort signal for this call only. Never wrapped: aborts reject with AbortError.
timeoutMs?numberPer-call timeout in milliseconds. Implemented with AbortSignal.timeout.

Source: packages/core/src/client.ts:109

BrickClientAction

interface BrickClientAction<TInput = unknown, TOutput = unknown, TErrors = {}>

Browser-safe action metadata generated by the Brick Vite plugin.

Properties

NameTypeDescription
kind"action"
namestring
serviceNamestring
plugin?stringOwning plugin descriptor name for plugin actions (e.g. @brickkit/crud).
resource?stringOwning 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

BrickClientOptions

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:

import { createBrickClient } from "@brickkit/core/client";
import type { AppServer } from "./server"; // `import type`: erased at build

const api = createBrickClient<AppServer>({ 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):

// 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 carrying the declared code, status, message, and details. Narrow with instanceof and read err.code.
  • Input/output validation failures → rejected with ValidationError, exactly as local execution throws.
  • Network failures, unparsable bodies, and non-OK responses outside the declared contract → BrickTransportError (4xx) or 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 callHTTP 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/<service>/<action> with dotted action names.
GET/DELETE input is serialized as a query string; other methods send JSON.

Properties

NameTypeDescription
baseUrlstringServer origin, e.g. "http://localhost:4000". No trailing slash required.
prefix?stringRoute prefix the server was mounted with (default "/api"). Must match the prefix passed to brick({ prefix }).
headers?| Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)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<Response>fetch implementation. Defaults to globalThis.fetch.
signal?AbortSignalDefault signal applied to every call (per-call signal wins).
timeoutMs?numberDefault per-call timeout in milliseconds (per-call timeoutMs wins).
contract?Record<string, any>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

Types

BrickActionCaller

type BrickActionCaller<A> =
  undefined extends InferActionInput<A>
    ? (
        input?: InferActionInput<A>,
        options?: BrickCallOptions,
      ) => Promise<InferActionOutput<A>>
    : (
        input: InferActionInput<A>,
        options?: BrickCallOptions,
      ) => Promise<InferActionOutput<A>>;

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

BrickClient

type BrickClient<T> = T extends
  | BrickClientAction<any, any, any>
  | Action<any, any, any, any>
  ? BrickActionCaller<T>
  : T extends (...args: any[]) => any
    ? BrickActionCaller<T>
    : T extends object
      ? { [K in keyof T]: BrickClient<T[K]> }
      : T;

Recursively maps action leaves and their namespaces to browser callers.

Source: packages/core/src/client.ts:271

InferActionErrorCodes

type InferActionErrorCodes<A> =
  A extends BrickClientAction<any, any, infer E>
    ? Extract<keyof E, string>
    : A extends {
          config: { errors?: infer E };
        }
      ? E extends Record<string, ActionErrorDefinition>
        ? Extract<keyof E, string>
        : never
      : never;

Union of error codes declared in an action's errors map.

Source: packages/core/src/client.ts:220

InferActionFailure

type InferActionFailure<A> = ActionExecutionError & {
  code:
    | InferActionErrorCodes<A>
    | "BAD_REQUEST"
    | "UNAUTHORIZED"
    | "FORBIDDEN"
    | "NOT_FOUND"
    | "CONFLICT"
    | "INTERNAL_SERVER_ERROR"
    | "VALIDATION_FAILED";
};

The declared failure shape of an action for catch narrowing:

try { await api.page.create(input); }
catch (err) {
  if (err instanceof ActionExecutionError) {
    const code: InferActionErrorCodes<typeof createPage> | "UNAUTHORIZED" | ... = err.code;
  }
}

(Standard framework codes are always possible alongside declared ones.)

Source: packages/core/src/client.ts:243

InferActionInput

type InferActionInput<A> =
  A extends BrickClientAction<infer I, any, any>
    ? I
    : A extends {
          config: { input?: infer S extends TSchema };
        }
      ? Static<S>
      : 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

InferActionOutput

type InferActionOutput<A> =
  A extends BrickClientAction<any, infer O, any>
    ? O
    : A extends (...args: any[]) => Promise<infer O>
      ? O
      : A extends { execute(params: any): Promise<infer O> }
        ? O
        : unknown;

Resolved output type of an action.

Source: packages/core/src/client.ts:210