Grove

Migrating from Bun

How to migrate existing bun ORM projects to Grove with zero downtime.

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 Dependency

go get github.com/xraph/grove

Step 2: Swap the Import

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 Connection

// 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 Syntax

Most bun query patterns have direct Grove equivalents:

BunGrove
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 Tags

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 Features

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 ordering

Multi-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 Changes

FeatureBunGrove
Placeholders?Native per driver ($1, ?)
Tag namespacebun:"..."grove:"..." (bun fallback)
Privacy tagsNot availableprivacy:pii, privacy:sensitive
Multi-driverPostgreSQL-focusedPG, MySQL, SQLite, MongoDB
MigrationsSQL filesGo code with dependency ordering
HooksQuery hooksPrivacy hooks with deny/modify/filter

What Stays the Same

  • 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

On this page