Skip to content

Using with Bun

By dropping down to common database/sql constructs, River can share connections and transactions with Bun, a well known ORM (Object Relational Mapper) in the Go ecosystem.


Sharing a database handle

The same *sql.DB handle can be configured on Bun and a River client:

import (
"database/sql"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/riverqueue/river"
"github.com/riverqueue/river/riverdriver/riverdatabasesql"
)
sqlDB, err := sql.Open("pgx", "postgres://localhost/river")
if err != nil {
return nil, err
}
// Providing a separate listener pool is optional, but gives the client
// access to listen/notify and more responsive to incoming jobs.
listenerPool, err := pgxpool.New(ctx, "postgres://localhost/river")
if err != nil {
return nil, err
}
defer listenerPool.Close()
bunDB := bun.NewDB(sqlDB, pgdialect.New())
riverClient, err := river.NewClient(
riverdatabasesql.NewWithPgxListener(sqlDB, listenerPool),
&river.Config{
Workers: workers,
},
)
if err != nil {
return nil, err
}

Pgx provides LISTEN/NOTIFY

The database/sql package doesn't expose Postgres LISTEN, so NewWithPgxListener uses listenerPool to receive notifications while all queries and transactions continue through sqlDB.

Use riverdatabasesql.New instead to intentionally run in poll-only mode.

Sharing a transaction

Transactions are shareable by starting them from Bun, then accessing bun.Tx's embedded *sql.Tx and using it with a River client's InsertTx:

tx, err := bunDB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return nil, err
}
_, err = riverClient.InsertTx(ctx, tx.Tx, SortArgs{ // tx.Tx is *sql.Tx
Strings: []string{
"whale", "tiger", "bear",
},
}, nil)
if err != nil {
return nil, err
}
if err := tx.Commit(); err != nil {
return nil, err
}