sqlc-model

module
v0.0.0-...-2de61a9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: MIT

README

sqlc-model

CI MIT License Latest release Go Reference

sqlc-model is an Eloquent-inspired rich model layer generated over sqlc for Go applications using PostgreSQL and pgx/v5.

sqlc remains the source of truth for SQL, query signatures, parameter types, result types, and driver integration. sqlc-model adds generated sessions, collections, mutable model objects, lifecycle state, dirty tracking, validation, relations, eager loading, transactions, and model-oriented persistence APIs.

The generator is not a dynamic ORM. Every terminal database operation resolves to a statically declared sqlc query.

Features

  • Generated Active Record-style models over sqlc queries.
  • Session and collection APIs for model construction, lookup, persistence, and transactions.
  • Dirty tracking, original snapshots, lifecycle state, and validation errors.
  • Typed relations with lazy loading, eager loading, scopes, cache inspection, and explicit terminal operations.
  • Value-object field conversion hooks.
  • Deterministic generated files and golden snapshot coverage.
  • PostgreSQL integration tests, compile fixtures, race-detector checks, and documentation example validation.

Installation

Install the sqlc process plugin:

go install github.com/macoaure/sqlc-model/cmd/sqlc-model@latest

Add the plugin to sqlc.yaml:

plugins:
  - name: richmodel
    process:
      cmd: sqlc-model

sql:
  - schema: schema.sql
    queries: query.sql
    engine: postgresql
    codegen:
      - plugin: richmodel
        out: internal/models
        options:
          version: 1
          sqlc:
            package: sqlcdb
            import: example.com/project/internal/database/sqlc
            driver: pgx/v5
          contexts:
            - name: content
              package: content
              directory: content
              models: {}

See the configuration reference for the complete options contract.

Usage/Examples

Create a session and save a model:

models := content.New(pool)

user, err := models.Users.Find(ctx, userID)
if err != nil {
    return err
}

user.SetName("Ada Lovelace")
if err := user.Save(ctx); err != nil {
    return err
}

Run related changes atomically:

err := models.Transaction(ctx, func(tx *content.Session) error {
    user := tx.Users.New().SetName("Ada Lovelace")
    if err := user.Save(ctx); err != nil {
        return err
    }

    post := tx.Posts.New().SetTitle("Notes")
    if err := post.Author().Associate(user); err != nil {
        return err
    }
    return post.Save(ctx)
})

Load a scoped relation:

posts, err := user.
    Posts().
    Published().
    Latest().
    Limit(10).
    Get(ctx)

Calling Posts() or a scope method does not execute SQL. Get(ctx) is the operation boundary that may perform lazy loading.

API Reference

Session
func New(pool *pgxpool.Pool, options ...SessionOption) *Session

The generated session owns collections, transaction capability, runtime policies, and model identity.

func (s *Session) Transaction(ctx context.Context, fn func(*Session) error) error

Returning nil commits. Returning an error rolls back. Panics roll back before continuing.

Collection
func (c *UserCollection) New() *User
func (c *UserCollection) Find(ctx context.Context, id UserID) (*User, error)

Collections create attached models and expose configured lookup/query operations.

Model
func (u *User) SetName(value string) *User
func (u *User) IsDirty(fields ...UserField) bool
func (u *User) Save(ctx context.Context) error
func (u *User) Delete(ctx context.Context) error
func (u *User) Refresh(ctx context.Context) error

Models contain current values, original persisted snapshots, lifecycle flags, validation errors, session attachment, and relation caches.

Relation
func (r UserPostsRelation) Get(ctx context.Context) ([]*Post, error)
func (r UserPostsRelation) Reload(ctx context.Context) ([]*Post, error)
func (r UserPostsRelation) Cached() ([]*Post, bool)
func (r UserPostsRelation) Forget() *User

Only the canonical unconstrained relation is cached by default. Scoped results do not overwrite canonical caches.

Running Tests

Run the normal Go test suite:

go test ./...

Run the generator-focused checks:

go test -race ./...
go test ./tests/compile/...
go test ./tests/golden

Run PostgreSQL-backed integration checks with a disposable database:

SQLC_RICHMODEL_TEST_DATABASE_URL='postgres://user:pass@localhost:5432/postgres?sslmode=disable' \
    go test ./tests/integration/...

See How to test generated models for the full test-level guide.

FAQ

Is sqlc-model an ORM?

No. It generates model-oriented Go code over named sqlc queries. It does not build dynamic SQL at runtime.

Does sqlc still own database access?

Yes. sqlc owns SQL parsing, static analysis, parameter types, result shapes, driver integration, and transaction-bound query objects.

When does generated code execute SQL?

Only terminal operations execute SQL. Examples include Find(ctx), Save(ctx), Delete(ctx), Refresh(ctx), relation Get(ctx), and relation Reload(ctx).

Where should I start?

Start with Generate the first rich model, then read the configuration reference.

Feedback

Open a GitHub issue for bugs, feature requests, and documentation gaps.

Acknowledgements

License

MIT

Directories

Path Synopsis
cmd
sqlc-model command
Command sqlc-model is a sqlc codegen plugin that generates an Eloquent-inspired rich model layer over sqlc-generated queries.
Command sqlc-model is a sqlc codegen plugin that generates an Eloquent-inspired rich model layer over sqlc-generated queries.
internal
codegen
Package codegen renders every generator-owned file kind (session, field-identifier constants, model, collection, internal store/record adapters) plus the one-time developer-owned extension file, from a GenerationPlan.
Package codegen renders every generator-owned file kind (session, field-identifier constants, model, collection, internal store/record adapters) plus the one-time developer-owned extension file, from a GenerationPlan.
config
Package config decodes and validates the plugin's `options` block: schema version gating, bounded-context/model/field structural validation, and order-preserving decode of the declaration-ordered `models` and `fields` objects.
Package config decodes and validates the plugin's `options` block: schema version gating, bounded-context/model/field structural validation, and order-preserving decode of the declaration-ordered `models` and `fields` objects.
contract
Package contract validates each configured lifecycle operation against sqlc query metadata: the required command (:one, :execrows, ...), whether insert/update return the full persisted row via RETURNING, and whether a query's parameters and result columns can hydrate the fields it needs to.
Package contract validates each configured lifecycle operation against sqlc query metadata: the required command (:one, :execrows, ...), whether insert/update return the full persisted row via RETURNING, and whether a query's parameters and result columns can hydrate the fields it needs to.
diagnostics
Package diagnostics defines the Diagnostic type, severity levels, and the deterministic sort/format rules used to report configuration and generation problems across every validation stage of the pipeline.
Package diagnostics defines the Diagnostic type, severity levels, and the deterministic sort/format rules used to report configuration and generation problems across every validation stage of the pipeline.
generate
Package generate is the thin orchestrator chaining internal/config -> internal/plan -> internal/codegen into the plugin's full request/response cycle, and the FR-017 atomicity check that decides whether any output is emitted at all for a given run.
Package generate is the thin orchestrator chaining internal/config -> internal/plan -> internal/codegen into the plugin's full request/response cycle, and the FR-017 atomicity check that decides whether any output is emitted at all for a given run.
mapping
Package mapping resolves each field's underlying database column and sqlc query result column, either from an explicit override or by exactly one unambiguous automatic match; ambiguous or missing matches are reported as diagnostics rather than guessed.
Package mapping resolves each field's underlying database column and sqlc query result column, either from an explicit override or by exactly one unambiguous automatic match; ambiguous or missing matches are reported as diagnostics rather than guessed.
plan
Package plan builds the deterministic, order-preserved GenerationPlan (contexts -> models -> fields -> operations) from validated configuration, resolved field mappings, and contract-validated operations.
Package plan builds the deterministic, order-preserved GenerationPlan (contexts -> models -> fields -> operations) from validated configuration, resolved field mappings, and contract-validated operations.
relation
Package relation validates the relation graph declared across a bounded context's models: target/inverse resolution and kind compatibility, per-kind query command/cardinality requirements, and scope parameter/type/name-collision compatibility.
Package relation validates the relation graph declared across a bounded context's models: target/inverse resolution and kind compatibility, per-kind query command/cardinality requirements, and scope parameter/type/name-collision compatibility.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL