Brick

Resources

Schema → Resource() → ResourceConfig and the CRUD exec pipeline.

Source: resource.go, types.go.

Schema

type Schema[Row, Response, CreateBody, UpdateBody, ListInput any] struct {
    Fields      schema.Fields
    Operations  schema.Operations
    PK          *schema.PK
    Guards      []Guard[map[string]any] // uniform, all ops
    Access      Access                  // per-op slots
    Hooks       Hooks[map[string]any, map[string]any]
    LookupField string                  // Get/Update/Delete addressing
    AutoCreate  bool                    // retained but IGNORED
}

Resource() freezes this into a ResourceConfig:

func Resource[Row, Response, CreateBody, UpdateBody, ListInput any](
    name, table string,
    s Schema[Row, Response, CreateBody, UpdateBody, ListInput],
) ResourceConfig
  • name drives routes (/api/{name}, /api/{name}/{id}) and OpenAPI tags; table defaults to name.
  • Model is (*Row)(nil) — used for reflection (map models take the ListMap path).
  • registerRoutesFn is unexported: the generic type parameters survive into the Huma handlers, and app code cannot replace them.

Operations.Enabled("get"|"list"|"create"|"update"|"delete") — nil/empty means enabled; the DSL can deny-map or allow-list ops.

Routes

MethodPathOperation
GET/api/{name}list
GET/api/{name}/{id}get
POST/api/{name}create
PATCH/api/{name}/{id}update
DELETE/api/{name}/{id}delete

Huma unwraps the Body field, so the OpenAPI schema reflects the inner type directly. Shapes are frozen by the OpenAPI snapshot — do not rename GetInput, CreateInput[B], UpdateInput[B], DeleteInput, or the *Output[R] wrappers.

  • GetInput carries path:id plus an accepted-but-ignored query:expand kept for OpenAPI stability.
  • ListInput must implement ListInputer.ToListOptions() db.ListOptions — generated by codegen.
  • All lists return ListResponse[T]{Data, Total, Page, Limit} (types.go), echoing the effective page/limit after clamping.

LookupField overrides PK addressing (LookupField || PK.Column || "id") — e.g. Frappe tables addressed by name.

Exec order (every op)

Guard.Check → strip unknown/readonly/PK → from: injection → defaults
→ timestamps → validation → Guard.Filter pushdown → single-query tx
  • stripUnknownKeys drops keys not in the schema (mass-assignment guard).
  • Readonly fields are stripped on write; the PK column is stripped on create (except AutonamePrompt, the only client-supplied PK) and the lookup column is never updatable.
  • created_at / updated_at are stamped only when the schema declares them. Frappe tables use creation / modified via app hooks — Brick never invents columns there.
  • Unique violations surface as 409 by SQLSTATE 23505 inspection, never string matching; races that slip past the in-tx precheck still map to 409.
  • Out-of-scope reads/updates/deletes are 404, not 403 (no existence oracle).

Empty update bodies are rejected with 400 nothing to update. Unknown or non-sortable sort values are 400.

On this page