Grove's plugin system lets you extend the ORM with custom behavior at lifecycle boundaries. Plugins are simpler than hooks — they respond to high-level events rather than intercepting individual queries.
Base Interface01
Every plugin must implement the Plugin interface:
import "github.com/xraph/grove/plugin"
type Plugin interface {
Name() string
}Lifecycle Hooks02
Plugins opt into events by implementing additional interfaces:
Model Events
type AfterRegisterModel interface {
OnModelRegistered(ctx context.Context, table *schema.Table)
}Connection Events
type AfterConnect interface {
OnConnected(ctx context.Context, db *grove.DB)
}
type BeforeClose interface {
OnClosing(ctx context.Context, db *grove.DB) error
}Migration Events
type AfterMigrate interface {
OnMigrated(ctx context.Context, migrations []migrate.AppliedMigration)
}Registering Plugins03
// Step 1: Create and connect the driver
pgdb := pgdriver.New()
pgdb.Open(ctx, "postgres://localhost:5432/mydb", driver.WithPoolSize(20))
// Step 2: Open grove.DB with plugins
db, _ := grove.Open(pgdb,
grove.WithPlugin(&MyAuditPlugin{}),
grove.WithPlugin(&MyMetricsPlugin{}),
)Type-Assertion Discovery04
The plugin registry uses type assertions to discover which events each plugin handles. Implement only the interfaces you need:
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "my-plugin" }
// Only implement OnConnected — other events are ignored
func (p *MyPlugin) OnConnected(ctx context.Context, db *grove.DB) {
log.Printf("connected to database")
}Built-in Plugins05
Chronicle Audit Plugin
Emits database events to Chronicle for audit trails:
import "github.com/xraph/grove/audit"
auditPlugin := audit.New(chronicleClient)
db, _ := grove.Open(pgdb,
grove.WithPlugin(auditPlugin),
)Events emitted:
grove.query.select/grove.query.insert/grove.query.update/grove.query.deletegrove.migration.applied/grove.migration.rolled_backgrove.connection.opened/grove.connection.closed
Prometheus Metrics Plugin
Exports query performance metrics:
import "github.com/xraph/grove/observability"
metricsPlugin := observability.New()
db, _ := grove.Open(pgdb,
grove.WithPlugin(metricsPlugin),
)Metrics:
grove_queries_total— Total queries by operation and tablegrove_query_duration_seconds— Query latency histogramgrove_connections_active— Active connection countgrove_migrations_total— Migration execution count
KV Store Plugin Compatibility06
The KV module (grove/kv) uses the same grove/plugin package for plugin registration. The grove/hook package is shared between the ORM and KV store, so middleware written as hooks works seamlessly across both. See the KV Middleware Overview for KV-specific middleware patterns.