Skip to main content

Model policy and schema

Policy explicitly declares the case-sensitive HTTP fields and operations an endpoint exposes. Engine construction binds it once to immutable GORM metadata.

policy := query.Schema[User]().
Expose("displayName",
query.GoField("Name"),
query.Filterable(query.Eq, query.Contains),
query.Sortable(),
)

When GoField is omitted, gotq resolves an exact JSON tag or exported Go field. The active GORM schema supplies the database column, primary key, and naming strategy. Column is an explicit validated override.

Supported inferred kinds are strings, booleans, signed/unsigned integers, floats, time.Time, DateValue, UUIDValue, DecimalValue, named forms, and pointers to them. uintptr, slices, maps, and relations are rejected unless a custom codec explicitly validates the scalar target.

type CodeCodec struct{}

func (CodeCodec) Name() string { return "code" }
func (CodeCodec) ValidateType(target reflect.Type) error { /* ... */ }
func (CodeCodec) ParseLiteral(l query.Literal, target reflect.Type) (any, error) {
// Convert syntax only; no SQL or identifier access exists here.
}

policy.Expose("code", query.WithCodec(CodeCodec{}), query.Filterable())

For unusual low-level mappings, Field(name, kind, options...) retains an explicit kind. Build supports that pre-release explicit schema workflow; Bind(db) and New use actual GORM metadata and support Expose.

Configuration failures use invalid_schema and occur before an engine is returned. A field present in the model but absent from policy is inaccessible.

Relationship policy

Relation("company", companyPolicy) explicitly exposes one GORM association and its nested whitelist. RelationGoField maps a public name to a differently named Go association. Relationship binding always requires the active GORM database; Build alone cannot validate tables, keys, join tables, cardinality, or soft-delete metadata.

Association fields that are not declared with Relation remain private even when GORM discovers them. Polymorphic and cyclic policy graphs are rejected.