Grove is designed for seamless migration from bun. Existing bun models work with Grove unchanged thanks to the dual tag system.
Step 1: Add Grove Dependency01
go get github.com/xraph/groveStep 2: Swap the Import02
Replace bun.BaseModel with grove.BaseModel:
// Before
import "github.com/uptrace/bun"
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:"id,pk,autoincrement"`
Name string `bun:"name,notnull"`
}
// After — bun tags still work!
import "github.com/xraph/grove"
type User struct {
grove.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:"id,pk,autoincrement"`
Name string `bun:"name,notnull"`
}Step 3: Replace the DB Connection03
// Before (bun)
sqldb, _ := sql.Open("pgx", dsn)
db := bun.NewDB(sqldb, pgdialect.New())
// After (grove)
pgdb := pgdriver.New()
pgdb.Open(ctx, dsn, driver.WithPoolSize(20))
db, _ := grove.Open(pgdb)Step 4: Update Query Syntax04
Most bun query patterns have direct Grove equivalents:
| Bun | Grove |
db.NewSelect(&users).Where("? = ?", bun.Ident("active"), true) | db.NewSelect(&users).Where("active = $1", true) |
db.NewInsert(&user).Exec(ctx) | db.NewInsert(&user).Exec(ctx) |
db.NewUpdate(&user).WherePK().Exec(ctx) | db.NewUpdate(&user).WherePK().Exec(ctx) |
db.NewDelete(&user).WherePK().Exec(ctx) | db.NewDelete(&user).WherePK().Exec(ctx) |
Key difference: Grove uses native PostgreSQL $1 placeholders instead of bun's ? with bun.Ident().
Step 5: Incrementally Adopt Grove Tags05
Once your project works with Grove, you can gradually migrate tags to access extended features:
type User struct {
grove.BaseModel `grove:"table:users,alias:u"` // Migrated
ID int64 `grove:"id,pk,autoincrement"` // Migrated
Name string `bun:"name,notnull"` // Still bun — works fine
Email string `grove:"email,notnull,unique"` // Migrated
SSN string `grove:"ssn,privacy:pii"` // New: privacy tag
}Step 6: Add Grove Features06
After migration, take advantage of Grove-specific features:
Privacy Hooks
db.Hooks().AddHook(&hook.TenantIsolation{Column: "tenant_id"})Modular Migrations
var Migrations = migrate.NewGroup("myapp")
// Register Go-code migrations with dependency orderingMulti-Driver Support
// Same models work across databases
pgdb := pgdriver.New()
pgdb.Open(ctx, pgDSN, driver.WithPoolSize(20))
pgDB, _ := grove.Open(pgdb)
mydb := mysqldriver.New()
mydb.Open(ctx, myDSN, driver.WithPoolSize(20))
myDB, _ := grove.Open(mydb)What Changes07
| Feature | Bun | Grove |
| Placeholders | ? | Native per driver ($1, ?) |
| Tag namespace | bun:"..." | grove:"..." (bun fallback) |
| Privacy tags | Not available | privacy:pii, privacy:sensitive |
| Multi-driver | PostgreSQL-focused | PG, MySQL, SQLite, MongoDB |
| Migrations | SQL files | Go code with dependency ordering |
| Hooks | Query hooks | Privacy hooks with deny/modify/filter |
What Stays the Same08
Model structure and field definitions
Relation tags (
has-one,has-many,belongs-to,many-to-many)Query builder patterns (NewSelect, NewInsert, NewUpdate, NewDelete)
Soft delete behavior
Transaction API