Documentation
¶
Overview ¶
Package grove is a polyglot Go ORM with native query syntax per database.
Grove provides near-raw performance with driver-specific query builders that expose each database's native idioms. PostgreSQL queries use $1 placeholders and PG-specific features. MySQL queries use ? placeholders and backtick quoting. MongoDB queries use native BSON syntax.
Key Features ¶
- Native query syntax per driver (no unified DSL)
- Dual tag system: grove:"..." primary, bun:"..." fallback
- Zero-reflection hot path (reflect once at registration)
- Modular migrations with multi-module dependency ordering
- Privacy hooks for tenant isolation, PII redaction, audit logging
- Part of the Forge ecosystem (github.com/xraph/forge)
Quick Start ¶
// Define a model
type User struct {
grove.BaseModel `grove:"table:users,alias:u"`
ID int64 `grove:"id,pk,autoincrement"`
Name string `grove:"name,notnull"`
Email string `grove:"email,notnull,unique"`
}
// Connect and register
db, err := grove.Open(pgdriver.New(), "postgres://localhost:5432/mydb")
db.RegisterModel((*User)(nil))
// Query with native PostgreSQL syntax
var users []User
err = db.NewSelect(&users).
Where("email ILIKE $1", "%@example.com").
Scan(ctx)
Index ¶
- Variables
- func Drivers() []string
- func RegisterDriver(name string, factory DriverFactory)
- type BaseModel
- type DB
- func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error)
- func (db *DB) Close() error
- func (db *DB) Driver() GroveDriver
- func (db *DB) Hooks() *hook.Engine
- func (db *DB) NewDelete(model any) any
- func (db *DB) NewInsert(model any) any
- func (db *DB) NewSelect(model ...any) any
- func (db *DB) NewUpdate(model any) any
- func (db *DB) Ping(ctx context.Context) error
- func (db *DB) RegisterModel(models ...any)
- type DriverFactory
- type GroveDriver
- type ModelRegistry
- type Option
- type Tx
- type TxOptions
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoRows is returned when a query expected at least one row but found none. ErrNoRows = errors.New("grove: no rows in result set") // ErrModelNotRegistered is returned when a query references a model type // that was not registered via DB.RegisterModel(). ErrModelNotRegistered = errors.New("grove: model not registered") // ErrNotSupported is returned when an operation is not supported by the // current driver (e.g., RETURNING on MySQL, transactions on some NoSQL drivers). ErrNotSupported = errors.New("grove: operation not supported by driver") // ErrDriverClosed is returned when an operation is attempted on a closed DB. ErrDriverClosed = errors.New("grove: driver connection pool has been closed") // ErrTxDone is returned when an operation is attempted on a committed // or rolled-back transaction. ErrTxDone = errors.New("grove: transaction has already been committed or rolled back") // ErrInvalidDSN is returned when the data source name is malformed. ErrInvalidDSN = errors.New("grove: invalid data source name") // ErrHookDenied is returned when a privacy hook denies the operation. ErrHookDenied = errors.New("grove: hook denied the operation") // ErrHookPanic is returned when a hook panics during execution (recovered). ErrHookPanic = errors.New("grove: hook panicked during execution") // ErrMigrationFailed is returned when a migration function returns an error. ErrMigrationFailed = errors.New("grove: migration failed") // ErrMigrationLocked is returned when another process holds the migration lock. ErrMigrationLocked = errors.New("grove: migration lock held by another process") // ErrCyclicDependency is returned when migration group dependencies form a cycle. ErrCyclicDependency = errors.New("grove: cyclic dependency in migration groups") // ErrDuplicateVersion is returned when two migrations share the same version string. ErrDuplicateVersion = errors.New("grove: duplicate migration version") // ErrInvalidTag is returned when a struct tag has invalid syntax. ErrInvalidTag = errors.New("grove: invalid struct tag syntax") // ErrNoPrimaryKey is returned when a model has no field marked as pk. ErrNoPrimaryKey = errors.New("grove: model has no primary key field") // ErrInvalidRelation is returned when a relation definition is incomplete or incorrect. ErrInvalidRelation = errors.New("grove: invalid relation definition") )
Sentinel errors returned by Grove operations. Use errors.Is() to check for specific error conditions.
Functions ¶
func RegisterDriver ¶
func RegisterDriver(name string, factory DriverFactory)
RegisterDriver registers a named driver factory. It is typically called from a driver package's init() function. Subsequent calls with the same name overwrite the previous registration.
Example (in pgdriver package):
func init() {
grove.RegisterDriver("postgres", func(ctx context.Context, dsn string) (grove.GroveDriver, error) {
db := New()
if err := db.Open(ctx, dsn); err != nil {
return nil, err
}
return db, nil
})
}
Types ¶
type BaseModel ¶
type BaseModel struct{}
BaseModel is embedded in user structs to mark them as Grove models. It carries table-level metadata via struct tags.
Supports both grove:"..." and bun:"..." tags:
type User struct {
grove.BaseModel `grove:"table:users,alias:u"`
ID int64 `grove:"id,pk,autoincrement"`
Name string `grove:"name,notnull"`
}
When both tags are present on the same struct, grove takes precedence. When only bun tags are present, they are used as fallback for zero-cost migration from bun.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB is the top-level database handle. It manages the connection pool, model registry, hook engine, and provides the entry point for all database operations.
Create a DB using Open:
pgdb := pgdriver.New() pgdb.Open(ctx, "postgres://localhost:5432/mydb") db, err := grove.Open(pgdb)
func Open ¶
func Open(drv GroveDriver, opts ...Option) (*DB, error)
Open creates a new DB with the given driver. The driver must already be connected (call driver.Open before grove.Open).
pgdb := pgdriver.New() pgdb.Open(ctx, "postgres://localhost:5432/mydb", driver.WithPoolSize(20)) db, err := grove.Open(pgdb)
func (*DB) BeginTx ¶
BeginTx starts a new transaction. The returned Tx wraps the driver transaction. Use pgdriver.Unwrap(db).BeginTx() for full driver.Tx access.
tx, err := db.BeginTx(ctx, nil) defer tx.Rollback() // ... use pgdriver.Unwrap with tx ... tx.Commit()
func (*DB) Hooks ¶
Hooks returns the hook engine for registering pre/post query and mutation hooks.
db.Hooks().AddHook(&TenantIsolation{}, hook.Scope{Tables: []string{"users"}})
func (*DB) NewDelete ¶
NewDelete creates a new DELETE query builder via the driver. The returned value should be type-asserted to the driver-specific builder.
For typed access, prefer: pgdriver.Unwrap(db).NewDelete(model)
func (*DB) NewInsert ¶
NewInsert creates a new INSERT query builder via the driver. The returned value should be type-asserted to the driver-specific builder.
For typed access, prefer: pgdriver.Unwrap(db).NewInsert(model)
func (*DB) NewSelect ¶
NewSelect creates a new SELECT query builder via the driver. The returned value should be type-asserted to the driver-specific builder (e.g., *pgdriver.SelectQuery).
For typed access, prefer: pgdriver.Unwrap(db).NewSelect(model)
func (*DB) NewUpdate ¶
NewUpdate creates a new UPDATE query builder via the driver. The returned value should be type-asserted to the driver-specific builder.
For typed access, prefer: pgdriver.Unwrap(db).NewUpdate(model)
func (*DB) RegisterModel ¶
RegisterModel registers model types for later use. Reflection is performed once per model type and cached by the driver.
db.RegisterModel((*User)(nil), (*Post)(nil))
type DriverFactory ¶
type DriverFactory func(ctx context.Context, dsn string) (GroveDriver, error)
DriverFactory is a function that creates and opens a GroveDriver given a DSN. Drivers register factories via RegisterDriver so that callers can create drivers by name (e.g., from YAML configuration) without importing driver packages directly.
Each driver module should register its factory in an init() function or provide an explicit Register() function.
type GroveDriver ¶
type GroveDriver interface {
// Name returns the driver identifier (e.g., "pg", "mysql", "mongo").
Name() string
// Close terminates all connections.
Close() error
// Ping checks connectivity.
Ping(ctx context.Context) error
}
GroveDriver is the minimal interface that a database driver must satisfy to work with the top-level DB handle. This avoids importing the driver package directly, which would create a circular dependency (schema -> grove -> driver -> schema).
The full driver.Driver interface (in the driver package) extends this with query execution methods.
func OpenDriver ¶
func OpenDriver(ctx context.Context, name, dsn string) (GroveDriver, error)
OpenDriver creates and opens a driver by its registered name. Returns an error if no factory is registered for the given name.
type ModelRegistry ¶
type ModelRegistry interface {
// Register registers a model and returns its cached metadata.
Register(model any) (any, error)
// Get returns cached metadata for a registered model, or nil.
Get(model any) any
}
ModelRegistry is the interface for model metadata caching. The concrete implementation is schema.Registry. This interface exists to avoid an import cycle between grove and schema packages.
type Option ¶
type Option func(*options)
Option configures a DB instance.
func WithLogger ¶
WithLogger sets a structured logger for the DB instance.
func WithPoolSize ¶
WithPoolSize sets the maximum number of connections in the pool. Default: 10.
func WithQueryTimeout ¶
WithQueryTimeout sets the default timeout for query execution. Default: 30s.
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
Tx wraps a driver transaction, exposing Commit and Rollback.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package audit provides a PostMutationHook that logs all mutations to an audit trail (Chronicle).
|
Package audit provides a PostMutationHook that logs all mutations to an audit trail (Chronicle). |
|
Package crdt provides an optional CRDT (Conflict-Free Replicated Data Type) layer for Grove.
|
Package crdt provides an optional CRDT (Conflict-Free Replicated Data Type) layer for Grove. |
|
Package driver defines the interfaces that every database backend must implement to work with Grove.
|
Package driver defines the interfaces that every database backend must implement to work with Grove. |
|
drivers
|
|
|
esdriver
module
|
|
|
mongodriver
module
|
|
|
pgdriver
module
|
|
|
sqlitedriver
module
|
|
|
extension
module
|
|
|
Package grovetest provides testing utilities for Grove applications.
|
Package grovetest provides testing utilities for Grove applications. |
|
Package hook provides the privacy and lifecycle hook system for Grove.
|
Package hook provides the privacy and lifecycle hook system for Grove. |
|
internal
|
|
|
pool
Package pool provides a sync.Pool-based byte buffer pool for minimizing allocations during query building.
|
Package pool provides a sync.Pool-based byte buffer pool for minimizing allocations during query building. |
|
safe
Package safe provides SQL identifier quoting and sanitization utilities to prevent SQL injection in dynamically constructed queries.
|
Package safe provides SQL identifier quoting and sanitization utilities to prevent SQL injection in dynamically constructed queries. |
|
tagparser
Package tagparser provides a high-performance struct tag parser for grove:"..." and bun:"..." tags.
|
Package tagparser provides a high-performance struct tag parser for grove:"..." and bun:"..." tags. |
|
kv
module
|
|
|
drivers/redisdriver
module
|
|
|
extension
module
|
|
|
Package migrate provides a database-agnostic migration system with multi-module support, dependency ordering, and distributed locking.
|
Package migrate provides a database-agnostic migration system with multi-module support, dependency ordering, and distributed locking. |
|
Package observability provides hooks for exposing query timing and error metrics.
|
Package observability provides hooks for exposing query timing and error metrics. |
|
Package plugin provides the interface for Grove plugins and a registry for managing them.
|
Package plugin provides the interface for Grove plugins and a registry for managing them. |
|
Package scan maps database result sets to Go structs using cached field metadata from the schema package.
|
Package scan maps database result sets to Go structs using cached field metadata from the schema package. |
|
Package stream provides a generic streaming iterator for database results.
|
Package stream provides a generic streaming iterator for database results. |