Documentation
¶
Index ¶
- func FormatValidationErrors(errors map[string]string) []string
- func SeedDataFromContext(ctx context.Context) ([]byte, error)
- func WithSeedDataContext(ctx context.Context, sfs fs.FS, path string) context.Context
- type AccessControl
- type BoolColumn
- type Condition
- type Endpoint
- type Entity
- type EntityConfig
- type EntityDeclaration
- type FieldDeclaration
- type FloatColumn
- func (c FloatColumn) Asc() Order
- func (c FloatColumn) Desc() Order
- func (c FloatColumn) Eq(v float64) Condition
- func (c FloatColumn) Gt(v float64) Condition
- func (c FloatColumn) Gte(v float64) Condition
- func (c FloatColumn) IsNotNull() Condition
- func (c FloatColumn) IsNull() Condition
- func (c FloatColumn) Lt(v float64) Condition
- func (c FloatColumn) Lte(v float64) Condition
- func (c FloatColumn) Neq(v float64) Condition
- type Index
- type IntColumn
- func (c IntColumn) Asc() Order
- func (c IntColumn) Desc() Order
- func (c IntColumn) Eq(v int) Condition
- func (c IntColumn) Gt(v int) Condition
- func (c IntColumn) Gte(v int) Condition
- func (c IntColumn) In(values ...int) Condition
- func (c IntColumn) IsNotNull() Condition
- func (c IntColumn) IsNull() Condition
- func (c IntColumn) Lt(v int) Condition
- func (c IntColumn) Lte(v int) Condition
- func (c IntColumn) Neq(v int) Condition
- type Order
- type Registry
- type Relation
- type RelationType
- type StringColumn
- func (c StringColumn) Asc() Order
- func (c StringColumn) Desc() Order
- func (c StringColumn) Eq(v string) Condition
- func (c StringColumn) In(values ...string) Condition
- func (c StringColumn) IsNotNull() Condition
- func (c StringColumn) IsNull() Condition
- func (c StringColumn) Like(pattern string) Condition
- func (c StringColumn) Neq(v string) Condition
- func (c StringColumn) NotLike(pattern string) Condition
- type TimestampColumn
- func (c TimestampColumn) Asc() Order
- func (c TimestampColumn) Desc() Order
- func (c TimestampColumn) Eq(v any) Condition
- func (c TimestampColumn) Gt(v any) Condition
- func (c TimestampColumn) Gte(v any) Condition
- func (c TimestampColumn) IsNotNull() Condition
- func (c TimestampColumn) IsNull() Condition
- func (c TimestampColumn) Lt(v any) Condition
- func (c TimestampColumn) Lte(v any) Condition
- type UUIDColumn
- type ValidationRegistry
- type ValidatorFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FormatValidationErrors ¶
FormatValidationErrors formats a map of field errors into a user-friendly string slice.
func SeedDataFromContext ¶
SeedDataFromContext returns the bytes referenced by the entity's SeedFS + SeedPath. Use inside a Seed function:
Seed: func(ctx context.Context, db *sql.DB) error {
data, err := entity.SeedDataFromContext(ctx)
if err != nil {
return err
}
var rows []FoodRow
if err := json.Unmarshal(data, &rows); err != nil {
return err
}
// ...insert rows...
}
Returns an error when no SeedFS was configured on the EntityConfig. Name matches the framework convention (TxFromContext, SessionFromContext, RegistryFromContext); the older *FromCtx shape is a battery/auth outlier.
func WithSeedDataContext ¶
WithSeedDataContext attaches a SeedFS + SeedPath pair to ctx for retrieval by SeedDataFromContext inside a Seed function. The framework calls this internally; hosts should not need to invoke it directly.
Types ¶
type AccessControl ¶
AccessControl declares the RBAC permission required for each CRUD operation on an entity. Each field holds a permission string (e.g. "posts:write"); blank means that operation is not RBAC-gated. Read covers both List and Get.
Permissions are plain strings here so the entity package stays decoupled from framework/access; the CRUD layer converts them to access.Permission and enforces them via access.Can against the policy + roles in the request context.
type BoolColumn ¶
type BoolColumn struct {
// contains filtered or unexported fields
}
BoolColumn represents a BOOLEAN column.
func NewBoolColumn ¶
func NewBoolColumn(name string) BoolColumn
func (BoolColumn) Eq ¶
func (c BoolColumn) Eq(v bool) Condition
func (BoolColumn) IsFalse ¶
func (c BoolColumn) IsFalse() Condition
func (BoolColumn) IsTrue ¶
func (c BoolColumn) IsTrue() Condition
type Condition ¶
type Condition struct {
// contains filtered or unexported fields
}
Condition is a where-clause fragment plus its bound arguments.
func And ¶
And combines conditions with AND. Useful inside Or(...) to nest a group of ANDed predicates: Or(And(a, b), And(c, d)).
func Or ¶
Or combines conditions with OR. Each conjunct keeps its own internal argument order; placeholders are renumbered at QueryBuilder.Build time so "$1" in a fragment doesn't collide with another fragment's "$1".
func (Condition) Apply ¶
func (c Condition) Apply(qb *query.QueryBuilder)
Apply appends this condition to the query builder.
type Endpoint ¶
type Endpoint struct {
Method string `json:"method"`
Path string `json:"path"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
MCP bool `json:"mcp,omitempty"`
Handler http.Handler `json:"-"`
MCPHandler mcp.ToolHandler `json:"-"`
}
Endpoint declares a custom route owned by an entity.
Path may be absolute ("/posts/{id}/publish") or relative to the entity table path ("{id}/publish"). Both Go 1.22 "{id}" and older ":id" parameter syntax are accepted. Handler is used for HTTP. MCPHandler is optional and is only registered when MCP is true.
type Entity ¶
type Entity struct {
Config EntityConfig
DB *sql.DB
PrimaryKey string // defaults to "id"
}
Entity represents a registered domain entity with its config and DB handle.
func Define ¶
func Define(name string, config EntityConfig) *Entity
Define creates a new Entity with the given name and configuration. It applies defaults (Table, Timestamps=true) and stores the name. It also injects system fields (id, timestamps) with AutoGenerate flags unless the user has already defined them.
type EntityConfig ¶
type EntityConfig struct {
Name string // entity name (e.g. "users")
Table string // DB table name (defaults to snake_case of Name)
Fields []schema.Field // typed field definitions
Relations []Relation // entity relationships
Endpoints []Endpoint // custom HTTP endpoints for this entity
SoftDelete bool // enable soft-delete (deleted_at column)
MultiTenant bool // scope queries by the tenant column (see TenantField)
TenantField string // tenant-scoping column name when MultiTenant; defaults to "tenant_id"
Timestamps bool // add created_at / updated_at columns
CRUD *bool // auto-generate CRUD routes. nil=auto(true when DB set), &true=always, &false=never
MCP bool // auto-generate MCP tools
CursorField string // optional: single-field keyset cursor; defaults to PrimaryKey
CursorFields []string // optional: composite cursor — ORDER BY each field in order with tuple-compared keyset. Wins over CursorField when non-empty.
Indices []Index // additional CREATE INDEX statements emitted by AutoMigrate
Unmanaged bool // when true, the migration system never emits DDL for this object (it is created elsewhere — e.g. a view, an FTS virtual table, or a legacy/external table). The ORM still queries it.
Properties map[string]any // caller-owned metadata for generators, plugins, and app conventions
MaxListLimit int // opt-in cap for ?limit and the streaming list path. 0 = use default (100); negative = no streaming cap above default.
// OwnerField names the DB column that holds the row's owner id (e.g.
// "user_id"). When set AND an owner extractor is registered (typically
// by battery/auth), auto-CRUD scopes List/Get/Update/Delete by the
// current request's owner and auto-stamps Create. Leave empty to keep
// pre-existing behaviour.
OwnerField string
// Access declares the RBAC permission required for each CRUD operation.
// A blank permission leaves that operation un-gated by RBAC (owner and
// tenant scoping still apply). When set, auto-CRUD refuses a request
// whose context lacks the permission with 403. Roles + policy must be
// present in the request context — wire them once with access.Middleware
// (or battery/auth). See framework/docs/content/access-control.md.
Access AccessControl
// Seed runs once per entity after AutoMigrate creates the table. The
// framework tracks completion in the _gofastr_seeded ledger; subsequent
// App.Start() calls skip the entity. Errors abort App.Start.
//
// Go-only: function values cannot be expressed in JSON entity
// declarations. Apps that load entities via EntityFromFile /
// EntitiesFromDir must wire seeding from Go after loading.
//
// Concurrency: RunSeeds is NOT safe for concurrent invocation across
// multiple processes. The framework assumes serialized startup (one
// process / replica calls App.Start at a time). For HA setups, gate
// seeding behind an external mechanism (init container, one-shot
// job, advisory lock). Seed implementations should be idempotent
// (INSERT … ON CONFLICT DO NOTHING) so accidental re-runs cannot
// duplicate data.
Seed func(ctx context.Context, db *sql.DB) error
// SeedFS is an optional fs.FS (typically a //go:embed embed.FS) that
// the framework attaches to the Seed function's context. Use with
// SeedPath to point at a single file within the FS.
//
// Go-only: like Seed itself, an fs.FS cannot be expressed in JSON
// entity declarations.
SeedFS fs.FS
// SeedPath is the path within SeedFS that Seed should consume.
// Ignored when SeedFS is nil.
SeedPath string
// contains filtered or unexported fields
}
EntityConfig holds the declarative configuration for an entity. Name is set via Define(); Fields declare the schema. Timestamps defaults to true — use WithTimestamps(false) to disable.
func (EntityConfig) TenantColumn ¶
func (c EntityConfig) TenantColumn() string
TenantColumn returns the tenant-scoping column name for this entity: TenantField when set, otherwise the framework default "tenant_id". This is the single source of the column name across injection, auto-migrate, and the CRUD insert/scope/filter paths.
func (EntityConfig) WithTimestamps ¶
func (c EntityConfig) WithTimestamps(v bool) EntityConfig
WithTimestamps returns a copy of the config with Timestamps set to the given value. Use this to opt out of the default (true).
type EntityDeclaration ¶
type EntityDeclaration struct {
Name string `json:"name"`
Table string `json:"table,omitempty"`
Fields []FieldDeclaration `json:"fields"`
Relations []Relation `json:"relations,omitempty"`
Endpoints []Endpoint `json:"endpoints,omitempty"`
SoftDelete bool `json:"soft_delete,omitempty"`
MultiTenant bool `json:"multi_tenant,omitempty"`
// OwnerField names the DB column that holds the row's owner id
// (e.g. "user_id"). When set AND an owner extractor is registered
// by a battery, auto-CRUD scopes List/Get/Update/Delete by the
// current request's owner and auto-stamps Create. Mirrors
// EntityConfig.OwnerField; leave empty to keep pre-existing behaviour.
OwnerField string `json:"owner_field,omitempty"`
Timestamps *bool `json:"timestamps,omitempty"`
CRUD *bool `json:"crud,omitempty"`
MCP bool `json:"mcp,omitempty"`
CursorField string `json:"cursor_field,omitempty"`
CursorFields []string `json:"cursor_fields,omitempty"`
Indices []Index `json:"indices,omitempty"`
Properties map[string]any `json:"properties,omitempty"`
}
EntityDeclaration is the JSON shape accepted by EntityFromFile and the CLI code generator. It mirrors EntityConfig while keeping field types readable.
func LoadEntityDeclaration ¶
func LoadEntityDeclaration(path string) (EntityDeclaration, error)
LoadEntityDeclaration reads and validates one entity declaration file.
func LoadEntityDeclarations ¶
func LoadEntityDeclarations(dir string) ([]EntityDeclaration, error)
LoadEntityDeclarations reads all *.json declarations in dir in stable order.
func (EntityDeclaration) Config ¶
func (d EntityDeclaration) Config() (EntityConfig, error)
Config converts a declaration into an EntityConfig.
type FieldDeclaration ¶
type FieldDeclaration struct {
Name string `json:"name"`
Type string `json:"type"`
Required bool `json:"required,omitempty"`
Unique bool `json:"unique,omitempty"`
Default any `json:"default,omitempty"`
AutoGenerate string `json:"auto_generate,omitempty"`
ReadOnly bool `json:"read_only,omitempty"`
Hidden bool `json:"hidden,omitempty"`
Max *float64 `json:"max,omitempty"`
Min *float64 `json:"min,omitempty"`
Pattern string `json:"pattern,omitempty"`
Values []string `json:"values,omitempty"`
To string `json:"to,omitempty"`
Many bool `json:"many,omitempty"`
}
FieldDeclaration is a JSON-friendly schema.Field.
type FloatColumn ¶
type FloatColumn struct {
// contains filtered or unexported fields
}
FloatColumn represents a REAL/DOUBLE PRECISION/DECIMAL column.
func NewFloatColumn ¶
func NewFloatColumn(name string) FloatColumn
func (FloatColumn) Eq ¶
func (c FloatColumn) Eq(v float64) Condition
func (FloatColumn) Gt ¶
func (c FloatColumn) Gt(v float64) Condition
func (FloatColumn) Gte ¶
func (c FloatColumn) Gte(v float64) Condition
func (FloatColumn) Lt ¶
func (c FloatColumn) Lt(v float64) Condition
func (FloatColumn) Lte ¶
func (c FloatColumn) Lte(v float64) Condition
func (FloatColumn) Neq ¶
func (c FloatColumn) Neq(v float64) Condition
type Index ¶
type Index struct {
Name string `json:"name,omitempty"`
Columns []string `json:"columns,omitempty"`
Unique bool `json:"unique,omitempty"`
Expression string `json:"expression,omitempty"`
}
Index declares a secondary index on an entity. Both dialects accept the same CREATE INDEX syntax; AutoMigrate emits CREATE INDEX IF NOT EXISTS so re-runs are safe.
Name is optional — when empty, AutoMigrate synthesises one as "idx_<table>_<col1>_<col2>". Unique indices reject duplicate rows for the chosen column set; for single-column uniqueness prefer the Field-level Unique flag which lives on the column definition.
Expression covers the case the column-list form can't express: a functional or partial index, e.g. `UNIQUE(user_id, lower(food))` to dedupe case-insensitively. When non-empty, Expression is rendered verbatim inside the index body (replacing Columns) — Name is REQUIRED in that case because there's no safe deterministic slug for an arbitrary expression. Use Columns for plain identifier indices; reach for Expression when SQL functions or constants need to participate in the indexed key.
type IntColumn ¶
type IntColumn struct {
// contains filtered or unexported fields
}
IntColumn represents an INTEGER column.
func NewIntColumn ¶
type Order ¶
type Order struct {
// contains filtered or unexported fields
}
Order is an order-by-clause fragment.
func (Order) Apply ¶
func (o Order) Apply(qb *query.QueryBuilder)
Apply appends this order to the query builder.
type Registry ¶
type Registry interface {
// All returns a snapshot of every registered entity keyed by name.
// Map iteration order is randomised by Go; for stable iteration use
// AllSorted().
All() map[string]*Entity
// AllSorted returns every registered entity in alphabetical order
// by name. Use this when emitting bytes whose ordering matters
// (OpenAPI, generated code, golden-file tests, ETag-cached
// responses).
AllSorted() []*Entity
// Get retrieves one entity by name, or an error when no such entity
// is registered.
Get(name string) (*Entity, error)
}
Registry is the minimal contract subpackages need from the framework's entity registry: enumerate every registered entity.
All() returns the entities keyed by name. Go's map iteration is randomised, so callers that emit order-sensitive output (OpenAPI tags, LLM markdown, generated code) must use AllSorted() to keep output stable across runs. Callers that only care about presence (counts, hash lookups, contains-checks) can use All() directly.
The concrete *framework.Registry type satisfies this implicitly. Splitting it out here lets framework/migrate, framework/dsl, and others depend on the entity model without pulling in the full framework package.
type Relation ¶
type Relation struct {
Type RelationType `json:"type"`
Name string `json:"name"` // logical name for this relation (e.g. "author", "comments")
Entity string `json:"entity"` // target entity/table name
ForeignKey string `json:"foreign_key"` // FK column name
Through string `json:"through,omitempty"` // pivot table name (ManyToMany only)
LocalKey string `json:"local_key,omitempty"` // column on the local side of a ManyToMany pivot
ForeignKeyTarget string `json:"foreign_key_target,omitempty"`
}
Relation describes a relationship between two entities.
func BelongsTo ¶
BelongsTo declares a many-to-one relationship. The source entity holds a foreign-key column that references the target entity's primary key.
func HasMany ¶
HasMany declares a one-to-many relationship. The target entity holds a foreign-key column that references the source entity's primary key.
func HasOne ¶
HasOne declares a one-to-one relationship. The target entity holds a foreign-key column that references the source entity's primary key.
func ManyToMany ¶
ManyToMany declares a many-to-many relationship through a pivot/join table.
type RelationType ¶
type RelationType int
RelationType enumerates the kinds of entity relationships.
const ( RelHasOne RelationType = iota // target has a FK pointing back to us RelHasMany // target has a FK pointing back to us (many rows) RelManyToOne // we hold a FK pointing to the target (BelongsTo) RelManyToMany // linked through a pivot/join table )
type StringColumn ¶
type StringColumn struct {
// contains filtered or unexported fields
}
StringColumn represents a TEXT/VARCHAR column. Use the methods to build Conditions: PostsTitle.Eq("hello"), PostsTitle.Like("%foo%"), etc.
func NewStringColumn ¶
func NewStringColumn(name string) StringColumn
NewStringColumn constructs a StringColumn for the given DB column name. Codegen calls this; user code rarely needs to.
func (StringColumn) Eq ¶
func (c StringColumn) Eq(v string) Condition
func (StringColumn) In ¶
func (c StringColumn) In(values ...string) Condition
func (StringColumn) Like ¶
func (c StringColumn) Like(pattern string) Condition
func (StringColumn) Neq ¶
func (c StringColumn) Neq(v string) Condition
func (StringColumn) NotLike ¶
func (c StringColumn) NotLike(pattern string) Condition
type TimestampColumn ¶
type TimestampColumn struct {
// contains filtered or unexported fields
}
TimestampColumn represents a TIMESTAMP/TIMESTAMPTZ column. Method semantics mirror IntColumn but accept any value the driver knows how to bind (time.Time, RFC3339 strings, etc.) so callers don't have to choose a canonical form here.
func NewTimestampColumn ¶
func NewTimestampColumn(name string) TimestampColumn
func (TimestampColumn) Eq ¶
func (c TimestampColumn) Eq(v any) Condition
func (TimestampColumn) Gt ¶
func (c TimestampColumn) Gt(v any) Condition
func (TimestampColumn) Gte ¶
func (c TimestampColumn) Gte(v any) Condition
func (TimestampColumn) Lt ¶
func (c TimestampColumn) Lt(v any) Condition
func (TimestampColumn) Lte ¶
func (c TimestampColumn) Lte(v any) Condition
type UUIDColumn ¶
type UUIDColumn struct {
// contains filtered or unexported fields
}
UUIDColumn represents a UUID/text-shaped identity column.
func NewUUIDColumn ¶
func NewUUIDColumn(name string) UUIDColumn
func (UUIDColumn) Eq ¶
func (c UUIDColumn) Eq(v string) Condition
func (UUIDColumn) In ¶
func (c UUIDColumn) In(values ...string) Condition
func (UUIDColumn) Neq ¶
func (c UUIDColumn) Neq(v string) Condition
type ValidationRegistry ¶
type ValidationRegistry struct {
// contains filtered or unexported fields
}
ValidationRegistry holds a chain of validator functions.
func NewValidationRegistry ¶
func NewValidationRegistry() *ValidationRegistry
NewValidationRegistry creates an empty ValidationRegistry.
func (*ValidationRegistry) RegisterValidator ¶
func (vr *ValidationRegistry) RegisterValidator(fn ValidatorFunc)
RegisterValidator appends a validator function to the chain.
func (*ValidationRegistry) Validate ¶
Validate runs all registered validators and collects every field error. The returned map is field name → error message. A nil/empty map means valid.
func (*ValidationRegistry) Validators ¶
func (vr *ValidationRegistry) Validators() int
Validators returns the number of registered validators (for testing).
type ValidatorFunc ¶
ValidatorFunc validates entity data and returns field-level errors. The returned map is field name → error message (empty map means valid).
func Custom ¶
func Custom(name string, fn func(ctx context.Context, data map[string]any) map[string]string) ValidatorFunc
Custom returns a validator with a given name that runs the provided function. The fn returns a map of field→error for any violations found.
func Required ¶
func Required(fields ...string) ValidatorFunc
Required returns a validator that checks the given fields are present and non-zero.