Lightweight, SQL-first Golang ORM for PostgreSQL, MySQL, MSSQL, SQLite, and Oracle
Bun is a modern ORM that embraces SQL rather than hiding it. Write complex queries in Go with type safety, powerful scanning capabilities, and database-agnostic code that works across multiple SQL databases.
- SQL-first approach - Write elegant, readable queries that feel like SQL
- Multi-database support - PostgreSQL, MySQL/MariaDB, MSSQL, SQLite, and Oracle
- Type-safe operations - Leverage Go's static typing for compile-time safety
- Flexible scanning - Query results into structs, maps, scalars, or slices
- Performance optimized - Built on
database/sql
with minimal overhead - Rich relationships - Define complex table relationships with struct tags
- Production ready - Migrations, fixtures, soft deletes, and OpenTelemetry support
go get github.com/uptrace/bun
package main
import (
"context"
"database/sql"
"fmt"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/sqlitedialect"
"github.com/uptrace/bun/driver/sqliteshim"
)
func main() {
ctx := context.Background()
// Open database
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:")
if err != nil {
panic(err)
}
// Create Bun instance
db := bun.NewDB(sqldb, sqlitedialect.New())
// Define model
type User struct {
ID int64 `bun:",pk,autoincrement"`
Name string `bun:",notnull"`
}
// Create table
db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
// Insert user
user := &User{Name: "John Doe"}
db.NewInsert().Model(user).Exec(ctx)
// Query user
err = db.NewSelect().Model(user).Where("id = ?", user.ID).Scan(ctx)
fmt.Printf("User: %+v\n", user)
}
Write sophisticated queries that remain readable and maintainable:
regionalSales := db.NewSelect().
ColumnExpr("region").
ColumnExpr("SUM(amount) AS total_sales").
TableExpr("orders").
GroupExpr("region")
topRegions := db.NewSelect().
ColumnExpr("region").
TableExpr("regional_sales").
Where("total_sales > (SELECT SUM(total_sales) / 10 FROM regional_sales)")
var results []struct {
Region string `bun:"region"`
Product string `bun:"product"`
ProductUnits int `bun:"product_units"`
ProductSales int `bun:"product_sales"`
}
err := db.NewSelect().
With("regional_sales", regionalSales).
With("top_regions", topRegions).
ColumnExpr("region, product").
ColumnExpr("SUM(quantity) AS product_units").
ColumnExpr("SUM(amount) AS product_sales").
TableExpr("orders").
Where("region IN (SELECT region FROM top_regions)").
GroupExpr("region, product").
Scan(ctx, &results)
Scan query results into various Go types:
// Into structs
var users []User
db.NewSelect().Model(&users).Scan(ctx)
// Into maps
var userMaps []map[string]interface{}
db.NewSelect().Table("users").Scan(ctx, &userMaps)
// Into scalars
var count int
db.NewSelect().Table("users").ColumnExpr("COUNT(*)").Scan(ctx, &count)
// Into individual variables
var id int64
var name string
db.NewSelect().Table("users").Column("id", "name").Limit(1).Scan(ctx, &id, &name)
Database | Driver | Dialect |
---|---|---|
PostgreSQL | github.com/uptrace/bun/driver/pgdriver |
pgdialect.New() |
MySQL/MariaDB | github.com/go-sql-driver/mysql |
mysqldialect.New() |
SQLite | github.com/uptrace/bun/driver/sqliteshim |
sqlitedialect.New() |
SQL Server | github.com/denisenkom/go-mssqldb |
mssqldialect.New() |
Oracle | github.com/sijms/go-ora/v2 |
oracledialect.New() |
Define complex relationships with struct tags:
type User struct {
ID int64 `bun:",pk,autoincrement"`
Name string `bun:",notnull"`
Posts []Post `bun:"rel:has-many,join:id=user_id"`
Profile Profile `bun:"rel:has-one,join:id=user_id"`
}
type Post struct {
ID int64 `bun:",pk,autoincrement"`
Title string
UserID int64
User *User `bun:"rel:belongs-to,join:user_id=id"`
}
// Load users with their posts
var users []User
err := db.NewSelect().
Model(&users).
Relation("Posts").
Scan(ctx)
Efficient bulk operations for large datasets:
// Bulk insert
users := []User{{Name: "John"}, {Name: "Jane"}, {Name: "Bob"}}
_, err := db.NewInsert().Model(&users).Exec(ctx)
// Bulk update with CTE
_, err = db.NewUpdate().
Model(&users).
Set("updated_at = NOW()").
Where("active = ?", true).
Exec(ctx)
// Bulk delete
_, err = db.NewDelete().
Model((*User)(nil)).
Where("created_at < ?", time.Now().AddDate(-1, 0, 0)).
Exec(ctx)
Version your database schema:
import "github.com/uptrace/bun/migrate"
migrations := migrate.NewMigrations()
migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
_, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
return err
}, func(ctx context.Context, db *bun.DB) error {
_, err := db.NewDropTable().Model((*User)(nil)).Exec(ctx)
return err
})
migrator := migrate.NewMigrator(db, migrations)
err := migrator.Init(ctx)
err = migrator.Up(ctx)
Enable query logging for development:
import "github.com/uptrace/bun/extra/bundebug"
db.AddQueryHook(bundebug.NewQueryHook(
bundebug.WithVerbose(true),
))
Production-ready observability with distributed tracing:
import "github.com/uptrace/bun/extra/bunotel"
db.AddQueryHook(bunotel.NewQueryHook(
bunotel.WithDBName("myapp"),
))
Monitoring made easy: Bun is brought to you by β uptrace/uptrace. Uptrace is an open-source APM tool that supports distributed tracing, metrics, and logs. You can use it to monitor applications and set up automatic alerts to receive notifications via email, Slack, Telegram, and others.
See OpenTelemetry example which demonstrates how you can use Uptrace to monitor Bun.
- Getting Started Guide - Comprehensive tutorial
- API Reference - Complete package documentation
- Examples - Working code samples
- Starter Kit - Production-ready template
- Community Discussions - Get help and share ideas
We welcome contributions! Please see our Contributing Guide for details on how to get started.
Thanks to all our contributors:
- Golang HTTP router - Fast and flexible HTTP router
- Golang msgpack - High-performance MessagePack serialization