Context API Reference
Detailed specification of BaseContext, ActionContext, and context lifecycle guarantees.
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:
export interface BaseContext<TDb = any> {
request: Request;
db: TDb;
logger: Logger;
traceId: string;
requestId?: string;
caller?: string;
tenantId?: string;
services?: Record<string, any>;
metadata?: Record<string, unknown>;
}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<string, any> | Reserved. The HTTP runtime and default context set it to an empty object. |
metadata | Record<string, unknown> | Free-form metadata. The HTTP action runtime sets it to { headers } (the request headers). |
Logger Interface
The logger property on BaseContext adheres to:
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), 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. 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:
export type ActionContext<
TDb = any,
TServiceContext = DefaultServiceContext,
> = BaseContext<TDb> & TServiceContext;Inside an action's execute({ ctx }) or CRUD hook, ctx includes all fields from BaseContext plus whatever your service context resolver returned.
NarrowAuthorizedContext<TCtx>
When an action declares an authorize guard, Brick wraps the context with NarrowAuthorizedContext:
export type NarrowAuthorizedContext<TCtx> = TCtx extends { user: infer U }
? [U] extends [null | undefined]
? TCtx
: Omit<TCtx, "user"> & { user: NonNullable<U> }
: TCtx;This utility narrows ctx.user from User | null to NonNullable<User> 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:
- Envelope allowlist: Only
user,traceId,requestId, andtenantIdfrom the proxy'soptions.contextor per-call context are forwarded, pluscalleras described below. Per-call values take precedence over proxy defaults. Custom fields such assessionandisOwnerare dropped. - 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
nullorundefined. Direct action calls enforce the same envelope allowlist for caller custom fields while accepting runtime fields such asrequest. callerField: The proxy setsctx.callertooptions.from— the name of the service that created the proxy, passed tocreateServiceProxy(target, { from: "..." })— never the target service's name. Whenfromis provided, anycallerinoptions.contextor the per-call context is ignored, so it cannot be spoofed. Withoutfrom, an explicitly suppliedcallerstill passes through; otherwisecalleris 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.