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
| Requirement | Place it here |
|---|---|
| Field or relation belongs to the domain | KSML model, then regenerate |
| Entity validation or normalization | Generated checker/behavior contract |
| Tenant filtering or page-size limits | Request policy installed in context |
| Database selection and connection | Context initialization |
| Authenticated actor and permissions | Trusted server adapter into context |
| Immutable mutation evidence | Runtime row audit sink |
| Application outbox or custom audit | App audit/event sink |
| HTTP JSON field/operator allowlist | Application 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.