dbtx adalah utilitas ringan untuk menjalankan transaksi database (*sql.Tx) secara aman dan idiomatik di Go, dengan dukungan penuh terhadap context.Context. Dirancang untuk digunakan dalam proyek REST API dan clean architecture dengan performa dan reliability tingkat produksi.
- ✅ Context-aware (
BeginTx(ctx, ...)) - ✅ Otomatis rollback saat error/panic
- ✅ Otomatis commit saat berhasil
- ✅ Bebas framework (Gin, Echo, dll)
- ✅ Siap untuk digunakan dalam modular monolith atau microservice
go get github.com/gogaruda/dbtximport "github.com/gogaruda/dbtx"func (r *userRepository) CreateUser(ctx context.Context, user *User) error {
return dbtx.WithTxContext(ctx, r.db, func(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO users (id, name, email) VALUES (?, ?, ?)
`, user.ID, user.Name, user.Email)
return err
})
}Pastikan semua operasi menggunakan
ExecContext,QueryContext, dll — bukan versi tanpa context.
Menjalankan fn di dalam transaksi database. Otomatis melakukan:
tx.Rollback()jika terjadipanicataufnmengembalikan errortx.Commit()jika tidak terjadi error atau panic
| Parameter | Tipe | Keterangan |
|---|---|---|
ctx |
context.Context |
Untuk timeout, cancel, dan propagasi context |
db |
*sql.DB |
Koneksi database pool |
fn |
func(ctx, tx) |
Fungsi yang akan dijalankan di dalam transaksi |
error — error dari fn, Commit(), atau BeginTx().
- Gunakan
WithTxContext()hanya di layer repository. - Jangan panik bila
ctxdibatalkan: semua akan auto-rollback. - Hindari menyimpan
*sql.Txdi struct. - Di service layer, cukup panggil method repository — tetap clean.
func (h *UserHandler) CreateUser(c *gin.Context) {
var input CreateUserRequest
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := h.userService.CreateUser(c.Request.Context(), input)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.Status(http.StatusCreated)
}func (s *UserService) CreateUser(ctx context.Context, req CreateUserRequest) error {
user := &User{
ID: uuid.NewString(),
Name: req.Name,
Email: req.Email,
}
return s.userRepo.CreateUser(ctx, user)
}func (r *userRepo) CreateUser(ctx context.Context, user *User) error {
return dbtx.WithTxContext(ctx, r.db, func(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO users (id, name, email)
VALUES (?, ?, ?)
`, user.ID, user.Name, user.Email)
return err
})
}WithTxContext() tetap bisa digunakan di luar HTTP handler, seperti untuk:
- Seeder (pengisian data awal)
- Migration
- Cron job
- CLI tools (seperti
cobra,urfave/cli, dll)
package main
import (
"context"
"database/sql"
"log"
"time"
"github.com/gogaruda/dbtx"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "your-dsn-here")
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = dbtx.WithTxContext(ctx, db, func(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO roles (id, name) VALUES ('admin', 'Administrator')
`)
return err
})
if err != nil {
log.Fatalf("seeding failed: %v", err)
}
log.Println("Seeding success")
}