Search documentation

Find a page in the Andurel docs.

Models

Models are application-owned Go types. They receive storage.Connection through constructors, wrap a narsilc client, and register with Fx via models.Module. Controllers and services call model APIs. They never import models/internal/queries directly.

Entity versus query client

Two layers sit in the same package:

Layer What it is Who uses it
Entity Domain row type (Product, User) with andurel tags Controllers map to payloads; factories sync fields
Plural API Products, Users holding *queries.Queries Controllers, services, seeds
narsilc client Generated under models/internal/queries Only the owning model package

Hand-written SQL lives in models/queries/*.sql. andurel sync queries compiles it into the internal client. The plural API constructs that client with queries.New(db) or queries.New(tx).

Mark the table above the imports so generators and factory sync can find the Entity:

1// andurel:table products
2
3type Product struct {
4    ID         uuid.UUID          `andurel:"id"`
5    Name       string             `andurel:"name"`
6    PriceCents int32              `andurel:"price_cents"`
7    CreatedAt  pgtype.Timestamptz `andurel:"created_at"`
8    UpdatedAt  pgtype.Timestamptz `andurel:"updated_at"`
9}

Nullable columns use pgtype.* when database.nullType is pgtype.Null (the default), or pointers when set to pointer in andurel.toml.

Construct and inject

Generated plural APIs look like this:

 1type Products struct {
 2    queries *queries.Queries
 3}
 4
 5func NewProducts(db storage.Connection) Products {
 6    return Products{queries: queries.New(db)}
 7}
 8
 9func (p Products) WithTx(tx storage.Transaction) Products {
10    return Products{queries: queries.New(tx)}
11}

NewProducts binds the pool. WithTx returns a value copy whose narsilc client uses the same PostgreSQL transaction. Persistence methods on Products call generated query methods and map rows into Product entities.

Register constructors in models.Module so Fx can supply them to controllers and services:

1var Module = fx.Module(
2    "models",
3    fx.Provide(
4        NewUsers,
5        NewProducts,
6    ),
7)

Keep entities, validation, relationships, and persistence methods together in the owning model file.

Generate from migrations

1andurel generate migration create_products_table
2andurel db migrate up
3andurel generate model Product
4andurel generate model Product --mode read-only
5andurel generate model Product --update --yes
Flag Meaning
--mode crud (default), read-only, or create-only
--table-name Override the default table name
--primary-key Skip interactive primary-key detection
--skip-factory Do not create or update models/factories
--update Refresh an existing model from migration changes
--dry-run / --diff / --json Preview structured mutations

Model generation reads migration history, writes (or updates) the model API and matching models/queries/*.sql, and updates models.Module. You own the resulting Go afterward. After editing SQL by hand, run andurel sync queries --json so the internal client matches.

Transactions

For multi-step work, use storage.RunInTransaction and WithTx so every statement shares one PostgreSQL transaction:

1err := storage.RunInTransaction(ctx, db,
2    func(ctx context.Context, tx storage.Transaction) error {
3        products := models.NewProducts(db).WithTx(tx)
4        _, err := products.Create(ctx, data)
5        return err
6    },
7)

Prefer injecting models.Products and calling products.WithTx(tx) over constructing a new API mid-handler when Fx already supplied one.

See Getting Started and Queries.