Pop — API Surface#
API types#
- Library — the primary surface; one Go import (
github.com/gobuffalo/pop/v6) gives access to all ORM, migration, and config functionality. - CLI —
sodabinary built on Cobra; wraps the library API for database management tasks.
Library API#
API style#
Fluent / method-chaining on two central types (*Connection and *Query), supplemented by package-level free functions for bootstrapping and configuration. The style mirrors ActiveRecord idioms (Rails heritage) rather than the functional-options or builder-struct style common in newer Go libraries.
Public packages exported for consumer use#
| Package | Purpose |
|---|---|
github.com/gobuffalo/pop/v6 | Root package — everything (Connection, Query, Model, Migrator, config, callbacks). Single import for all ORM functionality. |
github.com/gobuffalo/pop/v6/associations | Eager-loading association types and interfaces (Association, AssociationSortable, etc.). Typically not imported directly by consumers. |
github.com/gobuffalo/pop/v6/columns | Maps struct fields to database columns; exported for tooling that needs column metadata (ForStruct, ForStructWithAlias, NewColumns). |
github.com/gobuffalo/pop/v6/slices | Scannable slice types (Map, etc.) for use as model field types. |
github.com/gobuffalo/pop/v6/fix | Migration file fixup utilities used by the soda fix command. |
github.com/gobuffalo/pop/v6/genny/… | Code-generation generators (config, fizz, model) used by soda generate. |
github.com/gobuffalo/pop/v6/logging | Log level constants; consumers can override the logger via pop.SetLogger. |
Connection — primary user-facing type#
All CRUD operations are methods on *Connection. Consumers obtain one via pop.Connect(env) or pop.Connections[env].
Bootstrapping#
func LoadConfigFile() error // reads database.yml, populates Connections map
func LookupPaths() []string // paths searched for database.yml
func AddLookupPaths(paths ...string) error // add custom lookup paths
func LoadFrom(r io.Reader) error // load config from reader
func ParseConfig(r io.Reader) (map[string]*ConnectionDetails, error)
func NewConnection(deets *ConnectionDetails) (*Connection, error) // low-level; prefer Connect
func Connect(e string) (*Connection, error) // shorthand: LoadConfigFile + get named connectionConnection lifecycle#
func (c *Connection) Open() error
func (c *Connection) Close() error
func (c *Connection) String() string
func (c *Connection) URL() string
func (c *Connection) Context() context.Context
func (c *Connection) MigrationURL() string
func (c *Connection) MigrationTableName() string
func (c *Connection) WithContext(ctx context.Context) *Connection // context propagationTransactions#
func (c *Connection) Transaction(fn func(tx *Connection) error) error // auto-commit/rollback
func (c *Connection) Rollback(fn func(tx *Connection)) // always rollback (test helper)
func (c *Connection) NewTransaction() (*Connection, error) // manual transaction
func (c *Connection) NewTransactionContext(ctx context.Context) (*Connection, error)
func (c *Connection) NewTransactionContextOptions(ctx context.Context, options *sql.TxOptions) (*Connection, error)Query building (shortcut forms on Connection)#
func (c *Connection) Q() *Query // blank query on this connection
func Q(c *Connection) *Query // package-level alias
func (c *Connection) RawQuery(stmt string, args ...interface{}) *Query
func (c *Connection) Where(stmt string, args ...interface{}) *Query
func (c *Connection) Order(stmt string, args ...interface{}) *Query
func (c *Connection) Limit(limit int) *Query
func (c *Connection) Select(fields ...string) *Query
func (c *Connection) Eager(fields ...string) *Connection // eager-load associations
func (c *Connection) EagerPreload(fields ...string) *Query // preload path
func (c *Connection) Scope(sf ScopeFunc) *Query // reusable query modifierCRUD — finders#
func (c *Connection) Find(model interface{}, id interface{}) error
func (c *Connection) First(model interface{}) error
func (c *Connection) Last(model interface{}) error
func (c *Connection) All(models interface{}) error
func (c *Connection) Count(model interface{}) (int, error)
func (c *Connection) Load(model interface{}, fields ...string) error // post-load association fillCRUD — executors#
func (c *Connection) Save(model interface{}, excludeColumns ...string) error
func (c *Connection) ValidateAndSave(model interface{}, excludeColumns ...string) (*validate.Errors, error)
func (c *Connection) Create(model interface{}, excludeColumns ...string) error
func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...string) (*validate.Errors, error)
func (c *Connection) Update(model interface{}, excludeColumns ...string) error
func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...string) (*validate.Errors, error)
func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) error
func (c *Connection) Destroy(model interface{}) error
func (c *Connection) Reload(model interface{}) error
func (c *Connection) TruncateAll() errorAssociations (BelongsTo helpers)#
func (c *Connection) BelongsTo(model interface{}) *Query
func (c *Connection) BelongsToAs(model interface{}, as string) *Query
func (c *Connection) BelongsToThrough(bt, thru interface{}) *QueryPagination#
func (c *Connection) Paginate(page int, perPage int) *Query
func (c *Connection) PaginateFromParams(params PaginationParams) *Query
func NewPaginator(page int, perPage int) *Paginator
func NewPaginatorFromParams(params PaginationParams) *PaginatorQuery — fluent builder type#
*Query accumulates SQL clauses and terminates with a finder or executor call. Methods return *Query for chaining.
Clause builders#
func (q *Query) Where(stmt string, args ...interface{}) *Query
func (q *Query) Order(stmt string, args ...interface{}) *Query
func (q *Query) Limit(limit int) *Query
func (q *Query) Select(fields ...string) *Query
func (q *Query) GroupBy(field string, fields ...string) *Query
func (q *Query) Having(condition string, args ...interface{}) *Query
func (q *Query) Join(table, on string, args ...interface{}) *Query
func (q *Query) LeftJoin(table, on string, args ...interface{}) *Query
func (q *Query) RightJoin(table, on string, args ...interface{}) *Query
func (q *Query) InnerJoin(table, on string, args ...interface{}) *Query
func (q *Query) LeftOuterJoin(table, on string, args ...interface{}) *Query
func (q *Query) RightOuterJoin(table, on string, args ...interface{}) *Query
func (q *Query) RawQuery(stmt string, args ...interface{}) *Query
func (q *Query) Eager(fields ...string) *Query
func (q *Query) EagerPreload(fields ...string) *Query
func (q *Query) Scope(sf ScopeFunc) *Query
func (q *Query) BelongsTo(model interface{}) *Query
func (q *Query) BelongsToAs(model interface{}, as string) *Query
func (q *Query) BelongsToThrough(bt, thru interface{}) *Query
func (q *Query) Paginate(page int, perPage int) *Query
func (q *Query) PaginateFromParams(params PaginationParams) *QueryTerminal operations#
func (q *Query) Find(model interface{}, id interface{}) error
func (q *Query) First(model interface{}) error
func (q *Query) Last(model interface{}) error
func (q *Query) All(models interface{}) error
func (q *Query) Exists(model interface{}) (bool, error)
func (q *Query) Exec() error
func (q *Query) ExecWithCount() (int, error)
func (q *Query) Delete(model interface{}) error
func (q *Query) UpdateQuery(model interface{}, columnNames ...string) (int64, error)Migrations#
func NewMigrator(c *Connection) Migrator
func NewFileMigrator(path string, c *Connection) (FileMigrator, error)
func NewMigrationBox(fsys fs.FS, c *Connection) (MigrationBox, error) // embed.FS support
func CreateSchemaMigrations(c *Connection) error
func MigrationContent(mf Migration, c *Connection, r io.Reader, usingTemplate bool) (string, error)Migrator exposes: Up(step int), Down(step int), Status(w io.Writer), Reset().
Database management#
func CreateDB(c *Connection) error
func DropDB(c *Connection) errorGlobal configuration#
var Debug bool // enable SQL logging
var ConfigName string // override "database.yml" filename
var Connections map[string]*Connection // populated by LoadConfigFile
func SetLogger(logger func(level logging.Level, s string, args ...interface{}))
func SetTxLogger(logger func(level logging.Level, anon interface{}, s string, args ...interface{}))
func SetEagerMode(eagerMode EagerMode)
func DialectSupported(d string) bool
func CanonicalDialect(synonym string) stringExtension interfaces (implement on model structs)#
Pop’s extensibility point for consumers is a rich set of optional interfaces that model structs can implement. Pop checks for these via type assertions during CRUD operations; no registration is needed.
Table naming#
type TableNameAble interface {
TableName() string
}
type TableNameAbleWithContext interface {
TableName(ctx context.Context) string
}Lifecycle callbacks (callbacks.go)#
type BeforeValidateable interface { BeforeValidate(*Connection) error }
type AfterFindable interface { AfterFind(*Connection) error }
type AfterEagerFindable interface { AfterEagerFind(*Connection) error }
type BeforeSaveable interface { BeforeSave(*Connection) error }
type AfterSaveable interface { AfterSave(*Connection) error }
type BeforeCreateable interface { BeforeCreate(*Connection) error }
type AfterCreateable interface { AfterCreate(*Connection) error }
type BeforeUpdateable interface { BeforeUpdate(*Connection) error }
type AfterUpdateable interface { AfterUpdate(*Connection) error }
type BeforeDestroyable interface { BeforeDestroy(*Connection) error }
type AfterDestroyable interface { AfterDestroy(*Connection) error }Validation hooks (validations.go)#
type validateable interface { Validate(*Connection) (*validate.Errors, error) }
type validateCreateable interface { ValidateCreate(*Connection) (*validate.Errors, error) }
type validateSaveable interface { ValidateSave(*Connection) (*validate.Errors, error) }
type validateUpdateable interface { ValidateUpdate(*Connection) (*validate.Errors, error) }Note:
Validate,ValidateCreate,ValidateSave,ValidateUpdateare the public names consumers implement; the interfaces themselves are private but the method signatures are public and documented in generated model stubs.
Scope function type#
type ScopeFunc func(*Query) *Query // reusable query modifierBackward compatibility#
Pop is currently on v6 (module path github.com/gobuffalo/pop/v6). The v5→v6 transition introduced breaking changes (context support, contextStore). Within a major version, the public API is stable. The root package acts as a stable facade; internal dialect details are hidden behind private interfaces.
CLI (soda)#
Framework#
Cobra (github.com/spf13/cobra)
Global flags#
-v, --version Print pop version
-c, --config string Path to database.yml
-e, --env string Target environment (default "development"; falls back to $GO_ENV)
-d, --debug Enable verbose SQL loggingCommand tree#
soda
├── create Create all databases in database.yml
├── drop Drop all databases in database.yml
├── migrate
│ ├── up [-n N] Run N pending migrations (default: all)
│ ├── down [-n N] Rollback N migrations (default: 1)
│ ├── status Show migration status table
│ └── reset Drop, create, and migrate
├── generate (alias: g)
│ ├── config Scaffold a database.yml configuration file
│ ├── fizz [name] Create an empty .fizz migration pair (up/down)
│ ├── sql [name] Create an empty .sql migration pair (up/down)
│ └── model [name] [attrs...] Generate Go model + migration (alias: m)
│ --struct-tag json|xml|jsonapi (default: json)
│ --migration-type fizz|sql (default: fizz)
│ --skip-migration -s Skip migration generation
│ --models-path string (default: "models")
├── schema
│ ├── dump [-o file] Dump current schema to stdout or file
│ └── load [-i file] Load schema from file
├── fix Migrate deprecated .fizz syntax
└── version Print pop versionFlag patterns#
- Persistent flags (
--config,--env,--debug) are defined onRootCmdand inherited by all subcommands. PersistentPreRunon the root command handles config loading for all subcommands;migrateusesPersistentPreRunEto additionally validate the migration directory exists.- Environment priority:
--envflag >$GO_ENVenv var >"development"default.
Plugin / Extension system#
There is no plugin loader at runtime. Extensibility operates through two mechanisms:
Interface-based model hooks — the callback and validation interfaces listed above let consumer model structs inject behavior at each lifecycle point without modifying pop’s code.
Dialect registration via
init()— new database dialects can be added by implementing the privatedialectinterface and registering a factory in thenewConnectionmap from aninit()function. This is how the five built-in dialects (PostgreSQL, MySQL, MariaDB, SQLite, CockroachDB) are wired in, and how a third-party dialect could be added at compile time.gennycode-generation generators — thegenny/…sub-packages exposeNew(*Options) (*genny.Generator, error)constructors that third-party tools (e.g., Buffalo) can compose into their own scaffolding pipelines.
Notable API design observations#
- Mirror API on
ConnectionandQuery: Most read operations (Find,First,All,Where, …) exist on both*Connection(immediate shorthand) and*Query(chainable form). This creates a dual-entry API that is convenient but slightly redundant. interface{}instead of generics: All model parameters areinterface{}; Pop predates generics and uses reflection throughout. A generics-based API (Find[T any](...)) would be a natural future direction.- No HTTP or gRPC surface: Pop is purely a database library; no network server is exposed. The only RPC-style communication is the optional instrumented SQL driver for distributed tracing.
*validate.Errorsreturn style: Rather than returning a singleerrorfor validation failures,ValidateAndCreateand siblings return a separate*validate.Errorsstruct alongside the Goerror. Consumers must check both.