Skip to main content

Go Customization

TeaQL customization has two layers: generated entity-scoped hooks for domain behavior, and application-owned UserContext assembly for platform policy and infrastructure. Do not customize by editing generated Request or Entity files.

Choose the extension boundary

RequirementPlace it here
Field or relation belongs to the domainKSML model, then regenerate
Entity validation or normalizationGenerated checker/behavior contract
Tenant filtering or page-size limitsRequest policy installed in context
Database selection and connectionContext initialization
Authenticated actor and permissionsTrusted server adapter into context
Immutable mutation evidenceRuntime row audit sink
Application outbox or custom auditApp audit/event sink
HTTP JSON field/operator allowlistApplication boundary before Request construction

Context factory

Create one application function that translates authenticated server state into a fully initialized context. Handlers should receive this context, not a connection and not client-provided tenant metadata.

type TrustedRequest struct {
TenantID int64
ActorID string
Roles []string
}

func NewRequestContext(base *Infrastructure, trusted TrustedRequest) *runtime.UserContext {
ctx := runtime.NewUserContext()
// Register provider, generated metadata, policy and sinks with the released API.
// Install trusted.TenantID, trusted.ActorID and trusted.Roles here.
return ctx
}

Read the released runtime source for exact registration names. The invariant is more important than a guessed constructor: every terminal receives only ctx.

Policy and dynamic input

Map external JSON to generated calls through an explicit switch. Reject unknown fields, operators, sorts, deep paths, invalid ranges, negative offsets, excessive IN lists and page sizes. Do not reflect a JSON field name into a method name or SQL fragment.

switch filter.Field {
case "order_number":
req = req.WithOrderNumberContaining(requireString(filter.Value))
default:
return nil, fmt.Errorf("unsupported filter field %q", filter.Field)
}

Tenant, actor, permissions, provider, purpose policy, and request policy are not valid dynamic fields even for administrators.

Behavior and checker hooks

Use generated interfaces and skeletons as the contract, but put durable handwritten implementations in an application package when the generator marks generated output as replaceable. Register them during context/module assembly. Tests should call the public generated API and prove the hook, not call the hook directly as the only test.

Two audit paths

An audited save declares business reason through AuditAs. The runtime row/mutation event is immutable platform evidence. A separately registered application event sink can enrich a masked event for outbox, analytics, or observability. Do not substitute an application log line for the runtime event, and never log secrets or unmasked sensitive fields.

Provider customization

Keep provider choice at startup. Qualify every driver/database pair with a real round-trip, optimistic lock, native aggregates, stable pagination, and exact relation Top-N before describing it as Feature PASS. A driver compiling is not provider support.

Regeneration rule

When customization seems to require editing q.go, request.go, entity.go, or expression.go, stop. Change the model, generator template, or runtime extension point, then regenerate and rerun the first-verification suite.