database

package
v0.8.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 3, 2026 License: Apache-2.0 Imports: 33 Imported by: 4

Documentation

Overview

Package database provides unified database access with support for SQL (Postgres, MySQL, SQLite) and NoSQL (MongoDB) databases. It exposes native drivers for maximum flexibility while providing production-ready features like connection pooling, health checks, and observability.

Index

Constants

View Source
const (
	// ManagerKey is the DI key for the database manager.
	ManagerKey = "forge.database.manager"
	// DatabaseKey is the DI key for the default database.
	DatabaseKey = "forge.database.database"
	// SQLKey is the DI key for the default SQL database (Bun DB).
	SQLKey = "forge.database.sql"
	// MongoKey is the DI key for the default MongoDB client.
	MongoKey = "forge.database.mongo"
	// RedisKey is the DI key for the default Redis client.
	RedisKey = "forge.database.redis"
)

DI container keys for database extension services.

View Source
const (
	CodeDatabaseError       = "DATABASE_ERROR"
	CodeDatabaseNotFound    = "DATABASE_NOT_FOUND"
	CodeDatabaseExists      = "DATABASE_ALREADY_EXISTS"
	CodeDatabaseNotOpened   = "DATABASE_NOT_OPENED"
	CodeDatabaseInvalidType = "DATABASE_INVALID_TYPE"
	CodeDatabaseConnection  = "DATABASE_CONNECTION_ERROR"
	CodeDatabaseQuery       = "DATABASE_QUERY_ERROR"
	CodeDatabaseTransaction = "DATABASE_TRANSACTION_ERROR"
	CodeDatabasePanic       = "DATABASE_PANIC_RECOVERED"
	CodeDatabaseConfig      = "DATABASE_CONFIG_ERROR"
)

Error codes for database operations.

View Source
const DefaultBatchSize = 1000

DefaultBatchSize is the default number of records to process in a single batch. This balances between performance and parameter limits in SQL databases.

View Source
const MaxTransactionDepth = 10

MaxTransactionDepth is the maximum nesting level for transactions. This prevents infinite recursion and stack overflow.

Variables

View Source
var (
	// Migrations is the global migration collection
	// All migrations should register themselves here using init().
	Migrations = migrate.Migrations

	// RegisterMigration is a helper to register a migration.
	RegisterMigration = migrate.RegisterMigration

	// RegisterModel adds a model to the auto-registration list.
	RegisterModel = migrate.RegisterModel

	// Models is the list of all models that should be auto-registered.
	Models = &migrate.Models
)

Re-export migrate package for convenience This allows users to import "github.com/xraph/forge/extensions/database" and use database.Migrations instead of importing the migrate subpackage.

Functions

func AssertDatabaseError added in v0.7.3

func AssertDatabaseError(t testing.TB, err error)

AssertDatabaseError is a helper to verify an error occurred.

func AssertNoDatabaseError added in v0.7.3

func AssertNoDatabaseError(t testing.TB, err error)

AssertNoDatabaseError is a helper to check for database errors in tests.

func AssertRecordCount added in v0.7.3

func AssertRecordCount[T any](t testing.TB, db *bun.DB, expected int)

AssertRecordCount verifies the total number of records in a table.

Example:

database.AssertRecordCount[User](t, db, 5)

func AssertRecordCountWhere added in v0.7.3

func AssertRecordCountWhere[T any](t testing.TB, db *bun.DB, expected int, where string, args ...any)

AssertRecordCountWhere verifies the number of records matching a condition.

Example:

database.AssertRecordCountWhere[User](t, db, 3, "active = ?", true)

func AssertRecordExists added in v0.7.3

func AssertRecordExists[T any](t testing.TB, db *bun.DB, id any)

AssertRecordExists verifies that a record with the given ID exists.

Example:

database.AssertRecordExists[User](t, db, 123)

func AssertRecordNotExists added in v0.7.3

func AssertRecordNotExists[T any](t testing.TB, db *bun.DB, id any)

AssertRecordNotExists verifies that a record with the given ID does not exist.

Example:

database.AssertRecordNotExists[User](t, db, 999)

func BulkDelete added in v0.7.3

func BulkDelete[T any](ctx context.Context, db bun.IDB, ids []any) error

BulkDelete deletes multiple records by their IDs. More efficient than deleting one at a time.

Example:

ids := []int64{1, 2, 3, 4, 5}
err := database.BulkDelete[User](ctx, db, ids)

func BulkInsert added in v0.7.3

func BulkInsert[T any](ctx context.Context, db bun.IDB, records []T, batchSize int) error

BulkInsert inserts multiple records in batches for optimal performance. This is significantly faster than inserting records one at a time.

Performance: ~50-100x faster than individual inserts for 1000 records.

Example:

users := []User{{Name: "Alice"}, {Name: "Bob"}, {Name: "Charlie"}}
err := database.BulkInsert(ctx, db, users, 0) // Use default batch size

func BulkInsertWithOptions added in v0.7.3

func BulkInsertWithOptions[T any](ctx context.Context, db bun.IDB, records []T, opts BulkInsertOptions) error

BulkInsertWithOptions inserts multiple records with custom options.

Example with conflict resolution:

opts := database.BulkInsertOptions{
    BatchSize:  500,
    OnConflict: "DO NOTHING",
}
err := database.BulkInsertWithOptions(ctx, db, users, opts)

func BulkInsertWithProgress added in v0.7.3

func BulkInsertWithProgress[T any](ctx context.Context, db bun.IDB, records []T, batchSize int, callback ProgressCallback) error

BulkInsertWithProgress inserts records in batches with progress updates. The callback is called after each batch completes.

Example:

err := database.BulkInsertWithProgress(ctx, db, users, 100, func(p database.BulkOperationProgress) {
    fmt.Printf("Progress: %d/%d (%d failed)\n", p.Processed, p.Total, p.Failed)
})

func BulkSoftDelete added in v0.7.3

func BulkSoftDelete[T any](ctx context.Context, db bun.IDB, ids []any) error

BulkSoftDelete soft deletes multiple records by their IDs. Only works with models that have a DeletedAt field with soft_delete tag.

Example:

ids := []int64{1, 2, 3, 4, 5}
err := database.BulkSoftDelete[User](ctx, db, ids)

func BulkUpdate added in v0.7.3

func BulkUpdate[T any](ctx context.Context, db bun.IDB, records []T, columns []string, batchSize int) error

BulkUpdate updates multiple records by their primary keys. Only the specified columns are updated.

Example:

users := []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
err := database.BulkUpdate(ctx, db, users, []string{"name"}, 0)

func BulkUpdateWithProgress added in v0.7.3

func BulkUpdateWithProgress[T any](ctx context.Context, db bun.IDB, records []T, columns []string, batchSize int, callback ProgressCallback) error

BulkUpdateWithProgress updates records in batches with progress updates.

func BulkUpsert added in v0.7.3

func BulkUpsert[T any](ctx context.Context, db bun.IDB, records []T, conflictColumns []string, batchSize int) error

BulkUpsert performs bulk insert with conflict resolution (upsert). If a record with the same key exists, it's updated instead of inserted.

Example for Postgres:

err := database.BulkUpsert(ctx, db, users, []string{"email"}, 0)
// ON CONFLICT (email) DO UPDATE SET ...

func ChunkSlice added in v0.7.3

func ChunkSlice[T any](slice []T, chunkSize int) [][]T

ChunkSlice splits a slice into chunks of the specified size. Useful for custom bulk operations.

Example:

chunks := database.ChunkSlice(records, 100)
for _, chunk := range chunks {
    // Process chunk
}

func CleanupDB added in v0.7.3

func CleanupDB(t testing.TB, db *bun.DB)

CleanupDB closes the database connection and cleans up resources. This is automatically called by t.Cleanup() when using NewTestDB.

func CreateTestModel added in v0.7.3

func CreateTestModel[T any](t testing.TB, db *bun.DB, model T) T

CreateTestModel creates a single test record.

Example:

user := database.CreateTestModel(t, db, User{Name: "Alice"})

func CreateTestModels added in v0.7.3

func CreateTestModels[T any](t testing.TB, db *bun.DB, models []T) []T

CreateTestModels is a helper to create multiple test records at once.

Example:

users := database.CreateTestModels(t, db, []User{
    {Name: "Alice"},
    {Name: "Bob"},
})

func EnableAutoExplain added in v0.7.3

func EnableAutoExplain(db *bun.DB, threshold time.Duration)

EnableAutoExplain is a convenience function to enable auto-EXPLAIN on a database. This should be called during database initialization.

Example:

db := database.MustGetSQL(c)
database.EnableAutoExplain(db, 1*time.Second)

func ErrConnectionFailed added in v0.4.0

func ErrConnectionFailed(dbName string, dbType DatabaseType, cause error) error

func ErrDatabaseAlreadyExists

func ErrDatabaseAlreadyExists(name string) error

func ErrDatabaseNotFound

func ErrDatabaseNotFound(name string) error

func ErrDatabaseNotOpened

func ErrDatabaseNotOpened(name string) error

func ErrInvalidDSN

func ErrInvalidDSN(dsn string) error

func ErrInvalidDatabaseName

func ErrInvalidDatabaseName(name string) error

func ErrInvalidDatabaseType

func ErrInvalidDatabaseType(dbType string) error

func ErrInvalidDatabaseTypeOp

func ErrInvalidDatabaseTypeOp(name string, expectedType, actualType DatabaseType) error

func ErrInvalidPoolConfig

func ErrInvalidPoolConfig(reason string) error

func ErrNoDatabasesConfigured

func ErrNoDatabasesConfigured() error

Error constructors for common database errors.

func ErrPanicRecovered added in v0.4.0

func ErrPanicRecovered(dbName string, dbType DatabaseType, panicValue any) error

func ErrQueryFailed added in v0.4.0

func ErrQueryFailed(dbName string, dbType DatabaseType, cause error) error

func ErrTransactionFailed added in v0.4.0

func ErrTransactionFailed(dbName string, dbType DatabaseType, cause error) error

func FilterSlice added in v0.7.3

func FilterSlice[T any](slice []T, predicate func(T) bool) []T

FilterSlice filters a slice based on a predicate function. Useful for post-query filtering.

Example:

activeUsers := database.FilterSlice(users, func(u User) bool {
    return u.Active
})

func FormatQueryPlan added in v0.7.3

func FormatQueryPlan(plan *QueryPlan) string

FormatQueryPlan formats a query plan for human-readable output.

func GetDB added in v0.7.3

func GetDB(ctx context.Context, defaultDB *bun.DB) bun.IDB

GetDB returns the appropriate database connection from context. If a transaction is active in the context, it returns the transaction. Otherwise, it returns the provided default database.

This allows code to work seamlessly both inside and outside transactions.

Example:

func CreateUser(ctx context.Context, db *bun.DB, user *User) error {
    // Works both in and out of transaction
    repo := database.NewRepository[User](database.GetDB(ctx, db))
    return repo.Create(ctx, user)
}

func GetMongo added in v0.5.0

func GetMongo(c forge.Container) (*mongo.Client, error)

GetMongo retrieves the default MongoDB client from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Returns error if:

  • Database extension not registered
  • Default database is not MongoDB (e.g., it's SQL)

func GetMongoFromApp added in v0.5.0

func GetMongoFromApp(app forge.App) (*mongo.Client, error)

GetMongoFromApp retrieves the default MongoDB client from the app Returns error if not found, type assertion fails, or default is not MongoDB.

func GetNamedMongo added in v0.5.0

func GetNamedMongo(c forge.Container, name string) (*mongo.Client, error)

GetNamedMongo retrieves a named MongoDB database as mongo.Client Returns error if database not found or is not MongoDB.

func GetNamedMongoFromApp added in v0.5.0

func GetNamedMongoFromApp(app forge.App, name string) (*mongo.Client, error)

GetNamedMongoFromApp retrieves a named MongoDB database from the app.

func GetNamedRedis added in v0.7.0

func GetNamedRedis(c forge.Container, name string) (redis.UniversalClient, error)

GetNamedRedis retrieves a named Redis database as redis.UniversalClient Returns error if database not found or is not Redis.

func GetNamedRedisFromApp added in v0.7.0

func GetNamedRedisFromApp(app forge.App, name string) (redis.UniversalClient, error)

GetNamedRedisFromApp retrieves a named Redis database from the app.

func GetNamedSQL added in v0.5.0

func GetNamedSQL(c forge.Container, name string) (*bun.DB, error)

GetNamedSQL retrieves a named SQL database as Bun DB Returns error if database not found or is not a SQL database.

func GetNamedSQLFromApp added in v0.5.0

func GetNamedSQLFromApp(app forge.App, name string) (*bun.DB, error)

GetNamedSQLFromApp retrieves a named SQL database from the app.

func GetRedis added in v0.7.0

func GetRedis(c forge.Container) (redis.UniversalClient, error)

GetRedis retrieves the default Redis client from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Returns error if:

  • Database extension not registered
  • Default database is not Redis (e.g., it's SQL or MongoDB)

func GetRedisFromApp added in v0.7.0

func GetRedisFromApp(app forge.App) (redis.UniversalClient, error)

GetRedisFromApp retrieves the default Redis client from the app Returns error if not found, type assertion fails, or default is not Redis.

func GetSQL added in v0.5.0

func GetSQL(c forge.Container) (*bun.DB, error)

GetSQL retrieves the default Bun SQL database from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Returns error if:

  • Database extension not registered
  • Default database is not a SQL database (e.g., it's MongoDB)

func GetSQLFromApp added in v0.5.0

func GetSQLFromApp(app forge.App) (*bun.DB, error)

GetSQLFromApp retrieves the default Bun SQL database from the app Returns error if not found, type assertion fails, or default is not a SQL database.

func GetTransactionDepth added in v0.7.3

func GetTransactionDepth(ctx context.Context) int32

GetTransactionDepth returns the current nesting depth of transactions. Returns 0 if not in a transaction.

Example:

depth := database.GetTransactionDepth(ctx)
fmt.Printf("Transaction nesting level: %d\n", depth)

func GetUserID added in v0.4.0

func GetUserID(ctx context.Context) (int64, bool)

GetUserID retrieves user ID from context.

func GetXIDUserID added in v0.4.0

func GetXIDUserID(ctx context.Context) (xid.ID, bool)

func GroupBy added in v0.7.3

func GroupBy[T any, K comparable](slice []T, keyFn func(T) K) map[K][]T

GroupBy groups a slice by a key function. Returns a map where each key maps to a slice of items with that key.

Example:

byStatus := database.GroupBy(users, func(u User) string {
    return u.Status
})
activeUsers := byStatus["active"]

func IdempotentInsert added in v0.7.3

func IdempotentInsert[T any](ctx context.Context, db *bun.DB, records *[]T, conflictColumn string) error

IdempotentInsert inserts records only if they don't already exist. This is useful for making seeders idempotent.

Example:

users := []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
err := database.IdempotentInsert(ctx, db, &users, "id")

func IdempotentUpsert added in v0.7.3

func IdempotentUpsert[T any](ctx context.Context, db *bun.DB, records *[]T, conflictColumn string) error

IdempotentUpsert inserts or updates records based on conflict columns. This ensures seeders can be run multiple times safely.

Example:

users := []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
err := database.IdempotentUpsert(ctx, db, &users, "id")

func IndexBy added in v0.7.3

func IndexBy[T any, K comparable](slice []T, keyFn func(T) K) map[K]T

IndexBy creates a map from a slice using a key function. If multiple items have the same key, the last one wins.

Example:

usersByID := database.IndexBy(users, func(u User) int64 {
    return u.ID
})
user := usersByID[123]

func IsInTransaction added in v0.7.3

func IsInTransaction(ctx context.Context) bool

IsInTransaction returns true if the context contains an active transaction.

Example:

if database.IsInTransaction(ctx) {
    // We're in a transaction
}

func LoadAllFixtures added in v0.7.3

func LoadAllFixtures[T any](t testing.TB, db *bun.DB) []T

LoadAllFixtures loads all records of a given type.

Example:

users := database.LoadAllFixtures[User](t, db)
assert.Len(t, users, 5)

func LoadFixture added in v0.7.3

func LoadFixture[T any](t testing.TB, db *bun.DB, id any) *T

LoadFixture loads a single record by ID and returns it. Useful for verifying record state in tests.

Example:

user := database.LoadFixture[User](t, db, 1)
assert.Equal(t, "Alice", user.Name)

func MapError added in v0.7.3

func MapError[From, To any](from From, mapFn func(From) (To, error)) (To, error)

MapError applies a mapper function and returns both the result and any error. Useful when the mapping function can fail.

Example:

dto, err := database.MapError(user, func(u User) (UserDTO, error) {
    if u.Email == "" {
        return UserDTO{}, fmt.Errorf("email is required")
    }
    return UserDTO{ID: u.ID, Name: u.Name, Email: u.Email}, nil
})

func MapPointer added in v0.7.3

func MapPointer[From, To any](from *From, mapFn func(From) To) *To

MapPointer transforms a pointer value, handling nil safely.

Example:

userDTO := database.MapPointer(userPtr, func(u User) UserDTO {
    return UserDTO{ID: u.ID, Name: u.Name}
})

func MapSlice added in v0.7.3

func MapSlice[From, To any](from []From, mapFn func(From) To) []To

MapSlice transforms a slice of values from one type to another. This eliminates the need for manual loops when converting query results.

Example:

users, err := repo.FindAll(ctx)
dtos := database.MapSlice(users, func(u User) UserDTO {
    return UserDTO{ID: u.ID, Name: u.Name}
})

func MapSliceError added in v0.7.3

func MapSliceError[From, To any](from []From, mapFn func(From) (To, error)) ([]To, error)

MapSliceError transforms a slice with a fallible mapper function. If any mapping fails, the function returns immediately with the error.

Example:

dtos, err := database.MapSliceError(users, func(u User) (UserDTO, error) {
    if u.Email == "" {
        return UserDTO{}, fmt.Errorf("user %d: email is required", u.ID)
    }
    return UserDTO{ID: u.ID, Name: u.Name, Email: u.Email}, nil
})

func MapSlicePointers added in v0.7.3

func MapSlicePointers[From, To any](from []*From, mapFn func(From) To) []To

MapSlicePointers transforms a slice of pointers.

Example:

dtos := database.MapSlicePointers(userPtrs, func(u User) UserDTO {
    return UserDTO{ID: u.ID, Name: u.Name}
})

func MapTo added in v0.7.3

func MapTo[From, To any](from From, mapFn func(From) To) To

MapTo transforms a single value from one type to another using a mapper function. This is useful for converting database models to DTOs.

Example:

userDTO := database.MapTo(user, func(u User) UserDTO {
    return UserDTO{
        ID:    u.ID,
        Name:  u.Name,
        Email: u.Email,
    }
})

func MaskDSN added in v0.4.0

func MaskDSN(dsn string, dbType DatabaseType) string

MaskDSN masks sensitive information in DSN for logging.

func MustGetDB added in v0.7.3

func MustGetDB(ctx context.Context, defaultDB *bun.DB) bun.IDB

MustGetDB returns the database connection from context. Panics if no default database is provided and no transaction is in context.

func MustGetMongo added in v0.5.0

func MustGetMongo(c forge.Container) *mongo.Client

MustGetMongo retrieves the default MongoDB client from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Panics if:

  • Database extension not registered
  • Default database is not MongoDB (e.g., it's SQL)

func MustGetMongoFromApp added in v0.5.0

func MustGetMongoFromApp(app forge.App) *mongo.Client

MustGetMongoFromApp retrieves the default MongoDB client from the app Panics if not found, type assertion fails, or default is not MongoDB.

func MustGetNamedMongo added in v0.5.0

func MustGetNamedMongo(c forge.Container, name string) *mongo.Client

MustGetNamedMongo retrieves a named MongoDB database as mongo.Client Panics if database not found or is not MongoDB.

func MustGetNamedMongoFromApp added in v0.5.0

func MustGetNamedMongoFromApp(app forge.App, name string) *mongo.Client

MustGetNamedMongoFromApp retrieves a named MongoDB database from the app Panics if not found.

func MustGetNamedRedis added in v0.7.0

func MustGetNamedRedis(c forge.Container, name string) redis.UniversalClient

MustGetNamedRedis retrieves a named Redis database as redis.UniversalClient Panics if database not found or is not Redis.

func MustGetNamedRedisFromApp added in v0.7.0

func MustGetNamedRedisFromApp(app forge.App, name string) redis.UniversalClient

MustGetNamedRedisFromApp retrieves a named Redis database from the app Panics if not found.

func MustGetNamedSQL added in v0.5.0

func MustGetNamedSQL(c forge.Container, name string) *bun.DB

MustGetNamedSQL retrieves a named SQL database as Bun DB Panics if database not found or is not a SQL database.

func MustGetNamedSQLFromApp added in v0.5.0

func MustGetNamedSQLFromApp(app forge.App, name string) *bun.DB

MustGetNamedSQLFromApp retrieves a named SQL database from the app Panics if not found.

func MustGetRedis added in v0.7.0

func MustGetRedis(c forge.Container) redis.UniversalClient

MustGetRedis retrieves the default Redis client from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Panics if:

  • Database extension not registered
  • Default database is not Redis (e.g., it's SQL or MongoDB)

func MustGetRedisFromApp added in v0.7.0

func MustGetRedisFromApp(app forge.App) redis.UniversalClient

MustGetRedisFromApp retrieves the default Redis client from the app Panics if not found, type assertion fails, or default is not Redis.

func MustGetSQL added in v0.5.0

func MustGetSQL(c forge.Container) *bun.DB

MustGetSQL retrieves the default Bun SQL database from the container.

Safe to call anytime - automatically ensures DatabaseManager is started first.

Panics if:

  • Database extension not registered
  • Default database is not a SQL database (e.g., it's MongoDB)

func MustGetSQLFromApp added in v0.5.0

func MustGetSQLFromApp(app forge.App) *bun.DB

MustGetSQLFromApp retrieves the default Bun SQL database from the app Panics if not found, type assertion fails, or default is not a SQL database.

func NewExtension

func NewExtension(opts ...ConfigOption) forge.Extension

NewExtension creates a new database extension with variadic options.

func NewExtensionWithConfig added in v0.4.0

func NewExtensionWithConfig(config Config) forge.Extension

NewExtensionWithConfig creates a new database extension with a complete config.

func NewTestDB added in v0.7.3

func NewTestDB(t testing.TB, opts ...TestDBOption) *bun.DB

NewTestDB creates a test database instance with automatic cleanup. By default, it uses an in-memory SQLite database.

Example:

func TestUserRepo(t *testing.T) {
    db := database.NewTestDB(t,
        database.WithAutoMigrate(&User{}),
    )

    repo := database.NewRepository[User](db)
    // Test repo methods
}

func Partition added in v0.7.3

func Partition[T any](slice []T, predicate func(T) bool) ([]T, []T)

Partition splits a slice into two slices based on a predicate. The first slice contains items where predicate returns true, the second contains items where it returns false.

Example:

active, inactive := database.Partition(users, func(u User) bool {
    return u.Active
})

func Pluck added in v0.7.3

func Pluck[T any, R any](slice []T, fn func(T) R) []R

Pluck extracts a specific field from each item in a slice.

Example:

ids := database.Pluck(users, func(u User) int64 {
    return u.ID
})

func ResetTransactionStats added in v0.7.3

func ResetTransactionStats()

ResetTransactionStats resets global transaction statistics. Primarily useful for testing.

func SeedFixtures added in v0.7.3

func SeedFixtures(t testing.TB, db *bun.DB, fixtures ...any) error

SeedFixtures inserts test data into the database. Records are inserted as-is without modification.

Example:

users := []User{
    {ID: 1, Name: "Alice"},
    {ID: 2, Name: "Bob"},
}
err := database.SeedFixtures(t, db, &users)

func SetUserID added in v0.4.0

func SetUserID(ctx context.Context, userID int64) context.Context

SetUserID is a helper to set user ID in context for audit tracking.

func SetXIDUserID added in v0.4.0

func SetXIDUserID(ctx context.Context, userID xid.ID) context.Context

func TruncateTables added in v0.7.3

func TruncateTables(t testing.TB, db *bun.DB, models ...any) error

TruncateTables removes all data from the specified model tables. Useful for cleaning up between tests.

Example:

err := database.TruncateTables(t, db, (*User)(nil), (*Post)(nil))

func Unique added in v0.7.3

func Unique[T any, K comparable](slice []T, keyFn func(T) K) []T

Unique removes duplicate values from a slice based on a key function.

Example:

uniqueUsers := database.Unique(users, func(u User) int64 {
    return u.ID
})

func WithNestedTransaction added in v0.7.3

func WithNestedTransaction(ctx context.Context, fn TxFunc) error

WithNestedTransaction explicitly creates a nested transaction using savepoints. This must be called within an existing transaction context.

Example:

database.WithTransaction(ctx, db, func(ctx1 context.Context) error {
    // Outer transaction
    repo.Create(ctx1, &user)

    // Inner transaction with savepoint
    err := database.WithNestedTransaction(ctx1, func(ctx2 context.Context) error {
        // This can be rolled back independently
        return repo.Create(ctx2, &profile)
    })

    return err
})

func WithReadOnlyTransaction added in v0.7.3

func WithReadOnlyTransaction(ctx context.Context, db *bun.DB, fn TxFunc) error

WithReadOnlyTransaction runs a function in a read-only transaction. This is useful for complex queries that need a consistent view.

func WithRepeatableReadTransaction added in v0.7.3

func WithRepeatableReadTransaction(ctx context.Context, db *bun.DB, fn TxFunc) error

WithRepeatableReadTransaction runs a function in a repeatable read transaction. This prevents non-repeatable reads but allows phantom reads.

func WithSerializableTransaction added in v0.7.3

func WithSerializableTransaction(ctx context.Context, db *bun.DB, fn TxFunc) error

WithSerializableTransaction runs a function in a serializable transaction. This provides the highest isolation level.

func WithTestTransaction added in v0.7.3

func WithTestTransaction(t testing.TB, db *bun.DB, fn func(txDB *bun.DB))

WithTestTransaction runs a test function within a transaction that's rolled back. This is useful for tests that need isolation without affecting the database.

Example:

database.WithTestTransaction(t, db, func(txDB *bun.DB) {
    // All database operations here will be rolled back
    repo := database.NewRepository[User](txDB)
    repo.Create(ctx, &user)
})

func WithTransaction added in v0.7.3

func WithTransaction(ctx context.Context, db *bun.DB, fn TxFunc) error

WithTransaction executes a function within a database transaction. If the function returns an error, the transaction is rolled back. If the function panics, the transaction is rolled back and the panic is converted to an error. If the function succeeds, the transaction is committed.

For nested calls, savepoints are used to support partial rollbacks.

Example:

err := database.WithTransaction(ctx, db, func(txCtx context.Context) error {
    // Use GetDB to get the transaction-aware database
    repo := database.NewRepository[User](database.GetDB(txCtx, db))
    return repo.Create(txCtx, &user)
})

func WithTransactionFromApp added in v0.7.3

func WithTransactionFromApp(ctx context.Context, app forge.App, fn TxFunc) error

WithTransactionFromApp is a convenience wrapper that gets the DB from app.

func WithTransactionFromContainer added in v0.7.3

func WithTransactionFromContainer(ctx context.Context, c forge.Container, fn TxFunc) error

WithTransactionFromContainer is a convenience wrapper that gets the DB from container.

Example:

err := database.WithTransactionFromContainer(ctx, c, func(txCtx context.Context) error {
    // Transaction code
    return nil
})

func WithTransactionOptions added in v0.7.3

func WithTransactionOptions(ctx context.Context, db *bun.DB, opts *sql.TxOptions, fn TxFunc) error

WithTransactionOptions executes a function within a transaction with custom options. This allows control over isolation level and read-only mode.

Example:

opts := &sql.TxOptions{
    Isolation: sql.LevelSerializable,
    ReadOnly:  false,
}
err := database.WithTransactionOptions(ctx, db, opts, func(txCtx context.Context) error {
    // Transaction code
    return nil
})

Types

type AppliedMigration added in v0.4.0

type AppliedMigration struct {
	Name      string
	GroupID   int64
	AppliedAt time.Time
}

AppliedMigration represents an applied migration.

type AuditModel added in v0.4.0

type AuditModel struct {
	ID        int64      `bun:"id,pk,autoincrement"                                   json:"id"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	CreatedBy *int64     `bun:"created_by"                                            json:"created_by,omitempty"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	UpdatedBy *int64     `bun:"updated_by"                                            json:"updated_by,omitempty"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero"                       json:"deleted_at,omitempty"`
	DeletedBy *int64     `bun:"deleted_by"                                            json:"deleted_by,omitempty"`
}

AuditModel provides comprehensive audit trail with user tracking Use this when you need to track who created/updated records.

func (*AuditModel) BeforeDelete added in v0.4.0

func (m *AuditModel) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error

BeforeDelete hook - performs soft delete and tracks deleter.

func (*AuditModel) BeforeInsert added in v0.4.0

func (m *AuditModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - sets timestamps and tracks creator.

func (*AuditModel) BeforeUpdate added in v0.4.0

func (m *AuditModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt and tracks updater.

func (*AuditModel) IsDeleted added in v0.4.0

func (m *AuditModel) IsDeleted() bool

IsDeleted checks if the record is soft deleted.

func (*AuditModel) Restore added in v0.4.0

func (m *AuditModel) Restore()

Restore restores a soft-deleted record.

type BaseModel added in v0.4.0

type BaseModel struct {
	ID        int64     `bun:"id,pk,autoincrement"                                   json:"id"`
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
}

BaseModel provides common fields and hooks for all models Use this for models that need ID, timestamps, and standard hooks.

func (*BaseModel) BeforeInsert added in v0.4.0

func (m *BaseModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - sets timestamps on insert.

func (*BaseModel) BeforeUpdate added in v0.4.0

func (m *BaseModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt on every update.

type BulkInsertOptions added in v0.7.3

type BulkInsertOptions struct {
	// BatchSize is the number of records to insert per query. Defaults to 1000.
	BatchSize int

	// OnConflict specifies the conflict resolution strategy.
	// Example: "DO NOTHING", "DO UPDATE SET name = EXCLUDED.name"
	OnConflict string
}

BulkInsertOptions configures bulk insert behavior.

type BulkOperationProgress added in v0.7.3

type BulkOperationProgress struct {
	Total     int // Total number of records to process
	Processed int // Number of records processed so far
	Failed    int // Number of records that failed
}

BulkOperationProgress provides progress updates during bulk operations.

type Config

type Config struct {
	// List of database configurations
	Databases []DatabaseConfig `json:"databases" mapstructure:"databases" yaml:"databases"`

	// Default database name (first one if not specified)
	Default string `json:"default" mapstructure:"default" yaml:"default"`

	// Config loading flags
	RequireConfig bool `json:"-" yaml:"-"`
}

Config is the configuration for the database extension.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns default configuration.

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the configuration.

type ConfigOption added in v0.4.0

type ConfigOption func(*Config)

ConfigOption is a functional option for Config.

func WithConfig added in v0.4.0

func WithConfig(config Config) ConfigOption

WithConfig replaces the entire config.

func WithDatabase added in v0.4.0

func WithDatabase(db DatabaseConfig) ConfigOption

WithDatabase adds a single database configuration.

func WithDatabases added in v0.4.0

func WithDatabases(databases ...DatabaseConfig) ConfigOption

WithDatabases sets the list of database configurations.

func WithDefault added in v0.4.0

func WithDefault(name string) ConfigOption

WithDefault sets the default database name.

func WithRequireConfig added in v0.4.0

func WithRequireConfig(require bool) ConfigOption

WithRequireConfig requires config from YAML.

type ConnectionState added in v0.4.0

type ConnectionState int32

ConnectionState represents the state of a database connection.

const (
	StateDisconnected ConnectionState = iota
	StateConnecting
	StateConnected
	StateError
	StateReconnecting
)

func (ConnectionState) String added in v0.4.0

func (s ConnectionState) String() string

type CursorPagination added in v0.7.3

type CursorPagination struct {
	// Cursor is the base64-encoded position to start from.
	// Empty string starts from the beginning (forward) or end (backward).
	Cursor string `json:"cursor,omitempty"`

	// PageSize is the number of records to return. Defaults to 20.
	PageSize int `json:"page_size"`

	// Sort is the column to sort by. Required for cursor pagination.
	Sort string `json:"sort"`

	// Direction is either "forward" (next page) or "backward" (previous page).
	// Defaults to "forward".
	Direction string `json:"direction,omitempty"`
}

CursorPagination configures cursor-based pagination. This approach is more efficient for large datasets and provides stable pagination even when data changes between requests.

Cursor format: base64(sort_value:id)

Example:

params := database.CursorPagination{
    Cursor:    "eyJjcmVhdGVkX2F0IjoiMjAyNC0wMS0wMVQxMDowMDowMFoiLCJpZCI6MTIzfQ==",
    PageSize:  20,
    Sort:      "created_at",
    Direction: "forward",
}

func (*CursorPagination) Normalize added in v0.7.3

func (c *CursorPagination) Normalize()

Normalize ensures cursor pagination parameters are valid.

type CursorResult added in v0.7.3

type CursorResult[T any] struct {
	// Data contains the records for the current page
	Data []T `json:"data"`

	// NextCursor is the cursor for the next page (nil if no next page)
	NextCursor *string `json:"next_cursor,omitempty"`

	// PrevCursor is the cursor for the previous page (nil if no previous page)
	PrevCursor *string `json:"prev_cursor,omitempty"`

	// HasNext indicates if there's a next page
	HasNext bool `json:"has_next"`

	// HasPrev indicates if there's a previous page
	HasPrev bool `json:"has_prev"`
}

CursorResult contains cursor-paginated data and navigation cursors.

func MapCursor added in v0.7.3

func MapCursor[From, To any](from *CursorResult[From], mapFn func(From) To) *CursorResult[To]

MapCursor transforms a CursorResult from one type to another. The cursor metadata is preserved while the data is transformed.

Example:

result, err := database.PaginateCursor[User](ctx, query, params)
dtoResult := database.MapCursor(result, func(u User) UserDTO {
    return UserDTO{ID: u.ID, Name: u.Name}
})

func MapCursorError added in v0.7.3

func MapCursorError[From, To any](from *CursorResult[From], mapFn func(From) (To, error)) (*CursorResult[To], error)

MapCursorError transforms a CursorResult with a fallible mapper.

Example:

dtoResult, err := database.MapCursorError(result, func(u User) (UserDTO, error) {
    return toDTO(u)
})

func PaginateCursor added in v0.7.3

func PaginateCursor[T any](ctx context.Context, query *bun.SelectQuery, params CursorPagination) (*CursorResult[T], error)

PaginateCursor performs cursor-based pagination on a Bun SelectQuery. This is more efficient for large datasets and provides stable pagination.

Example:

query := db.NewSelect().Model((*User)(nil))
result, err := database.PaginateCursor[User](ctx, query, database.CursorPagination{
    Cursor:    cursor,
    PageSize:  20,
    Sort:      "created_at",
    Direction: "forward",
})

type Database

type Database interface {
	// Identity
	Name() string
	Type() DatabaseType

	// Lifecycle
	Open(ctx context.Context) error
	Close(ctx context.Context) error
	Ping(ctx context.Context) error

	// State
	IsOpen() bool
	State() ConnectionState

	// Health
	Health(ctx context.Context) HealthStatus
	Stats() DatabaseStats

	// Access to native driver/ORM
	Driver() any
}

Database represents a database connection.

func GetDatabase added in v0.5.0

func GetDatabase(c forge.Container) (Database, error)

GetDatabase retrieves the default Database from the container Returns error if not found or type assertion fails. Automatically ensures DatabaseManager is started before resolving.

func GetDatabaseFromApp added in v0.5.0

func GetDatabaseFromApp(app forge.App) (Database, error)

GetDatabaseFromApp retrieves the default Database from the app Returns error if not found or type assertion fails.

func GetNamedDatabase added in v0.5.0

func GetNamedDatabase(c forge.Container, name string) (Database, error)

GetNamedDatabase retrieves a named database through the DatabaseManager This is useful when you have multiple databases configured.

func GetNamedDatabaseFromApp added in v0.5.0

func GetNamedDatabaseFromApp(app forge.App, name string) (Database, error)

GetNamedDatabaseFromApp retrieves a named database from the app.

func MustGetDatabase added in v0.5.0

func MustGetDatabase(c forge.Container) Database

MustGetDatabase retrieves the default Database from the container Panics if not found or type assertion fails. Automatically ensures DatabaseManager is started before resolving.

func MustGetDatabaseFromApp added in v0.5.0

func MustGetDatabaseFromApp(app forge.App) Database

MustGetDatabaseFromApp retrieves the default Database from the app Panics if not found or type assertion fails.

func MustGetNamedDatabase added in v0.5.0

func MustGetNamedDatabase(c forge.Container, name string) Database

MustGetNamedDatabase retrieves a named database through the DatabaseManager Panics if manager not found or database not found.

func MustGetNamedDatabaseFromApp added in v0.5.0

func MustGetNamedDatabaseFromApp(app forge.App, name string) Database

MustGetNamedDatabaseFromApp retrieves a named database from the app Panics if not found.

type DatabaseConfig

type DatabaseConfig struct {
	Name string       `json:"name" yaml:"name" mapstructure:"name"`
	Type DatabaseType `json:"type" yaml:"type" mapstructure:"type"`
	DSN  string       `json:"dsn"  yaml:"dsn"  mapstructure:"dsn"`

	// Connection pool settings
	MaxOpenConns    int           `default:"25" json:"max_open_conns"     yaml:"max_open_conns"     mapstructure:"max_open_conns"`
	MaxIdleConns    int           `default:"5"  json:"max_idle_conns"     yaml:"max_idle_conns"     mapstructure:"max_idle_conns"`
	ConnMaxLifetime time.Duration `default:"5m" json:"conn_max_lifetime"  yaml:"conn_max_lifetime"  mapstructure:"conn_max_lifetime"`
	ConnMaxIdleTime time.Duration `default:"5m" json:"conn_max_idle_time" yaml:"conn_max_idle_time" mapstructure:"conn_max_idle_time"`

	// Retry settings
	MaxRetries int           `default:"3"  json:"max_retries" yaml:"max_retries" mapstructure:"max_retries"`
	RetryDelay time.Duration `default:"1s" json:"retry_delay" yaml:"retry_delay" mapstructure:"retry_delay"`

	// Timeout settings
	ConnectionTimeout time.Duration `default:"10s" json:"connection_timeout" yaml:"connection_timeout" mapstructure:"connection_timeout"`
	QueryTimeout      time.Duration `default:"30s" json:"query_timeout"      yaml:"query_timeout"      mapstructure:"query_timeout"`

	// Observability settings
	SlowQueryThreshold      time.Duration `default:"100ms" json:"slow_query_threshold"       yaml:"slow_query_threshold"       mapstructure:"slow_query_threshold"`
	DisableSlowQueryLogging bool          `default:"false" json:"disable_slow_query_logging" yaml:"disable_slow_query_logging" mapstructure:"disable_slow_query_logging"`
	AutoExplainThreshold    time.Duration `default:"0"     json:"auto_explain_threshold"    yaml:"auto_explain_threshold"    mapstructure:"auto_explain_threshold"` // 0 = disabled

	// Health check
	HealthCheckInterval time.Duration `default:"30s" json:"health_check_interval" yaml:"health_check_interval" mapstructure:"health_check_interval"`

	// Additional config (database-specific)
	Config map[string]any `json:"config" yaml:"config" mapstructure:"config"`
}

DatabaseConfig is the configuration for a database connection.

type DatabaseError added in v0.4.0

type DatabaseError struct {
	DBName    string
	DBType    DatabaseType
	Operation string
	Code      string
	Err       error
}

DatabaseError wraps database-specific errors with context.

func NewDatabaseError added in v0.4.0

func NewDatabaseError(dbName string, dbType DatabaseType, operation string, err error) *DatabaseError

NewDatabaseError creates a new database error with context.

func (*DatabaseError) Error added in v0.4.0

func (e *DatabaseError) Error() string

func (*DatabaseError) Unwrap added in v0.4.0

func (e *DatabaseError) Unwrap() error

type DatabaseManager

type DatabaseManager struct {
	// contains filtered or unexported fields
}

DatabaseManager manages multiple database connections.

func GetManager added in v0.5.0

func GetManager(c forge.Container) (*DatabaseManager, error)

GetManager retrieves the DatabaseManager from the container Returns error if not found or type assertion fails.

func GetManagerFromApp added in v0.5.0

func GetManagerFromApp(app forge.App) (*DatabaseManager, error)

GetManagerFromApp retrieves the DatabaseManager from the app Returns error if not found or type assertion fails.

func MustGetManager added in v0.5.0

func MustGetManager(c forge.Container) *DatabaseManager

MustGetManager retrieves the DatabaseManager from the container Panics if not found or type assertion fails.

func MustGetManagerFromApp added in v0.5.0

func MustGetManagerFromApp(app forge.App) *DatabaseManager

MustGetManagerFromApp retrieves the DatabaseManager from the app Panics if not found or type assertion fails.

func NewDatabaseManager

func NewDatabaseManager(logger forge.Logger, metrics forge.Metrics) *DatabaseManager

NewDatabaseManager creates a new database manager.

func (*DatabaseManager) CloseAll

func (m *DatabaseManager) CloseAll(ctx context.Context) error

CloseAll closes all registered databases, collecting errors without stopping.

func (*DatabaseManager) Get

func (m *DatabaseManager) Get(name string) (Database, error)

Get retrieves a database by name.

func (*DatabaseManager) Health added in v0.4.0

func (m *DatabaseManager) Health(ctx context.Context) error

func (*DatabaseManager) HealthCheckAll

func (m *DatabaseManager) HealthCheckAll(ctx context.Context) map[string]HealthStatus

HealthCheckAll performs health checks on all databases.

func (*DatabaseManager) List

func (m *DatabaseManager) List() []string

List returns the names of all registered databases.

func (*DatabaseManager) Mongo

func (m *DatabaseManager) Mongo(name string) (*mongo.Client, error)

Mongo retrieves a MongoDB client by name.

func (*DatabaseManager) MongoDatabase

func (m *DatabaseManager) MongoDatabase(name string) (*MongoDatabase, error)

MongoDatabase retrieves a MongoDB database wrapper by name.

func (*DatabaseManager) Name added in v0.8.0

func (m *DatabaseManager) Name() string

Name returns the service name for the DI container. Implements shared.Service interface.

func (*DatabaseManager) OpenAll

func (m *DatabaseManager) OpenAll(ctx context.Context) error

OpenAll opens all registered databases, collecting errors without stopping.

func (*DatabaseManager) Redis added in v0.7.0

func (m *DatabaseManager) Redis(name string) (redis.UniversalClient, error)

Redis retrieves a Redis client by name.

func (*DatabaseManager) RedisDatabase added in v0.7.0

func (m *DatabaseManager) RedisDatabase(name string) (*RedisDatabase, error)

RedisDatabase retrieves a Redis database wrapper by name.

func (*DatabaseManager) Register

func (m *DatabaseManager) Register(name string, db Database) error

Register adds a database to the manager.

func (*DatabaseManager) SQL

func (m *DatabaseManager) SQL(name string) (*bun.DB, error)

SQL retrieves an SQL database with Bun ORM by name.

func (*DatabaseManager) Start added in v0.8.0

func (m *DatabaseManager) Start(ctx context.Context) error

Start opens all registered database connections. Implements shared.Service interface - called by the DI container during Start().

func (*DatabaseManager) Stop added in v0.8.0

func (m *DatabaseManager) Stop(ctx context.Context) error

Stop closes all registered database connections. Implements shared.Service interface - called by the DI container during Stop().

type DatabaseStats

type DatabaseStats struct {
	OpenConnections   int           `json:"open_connections"`
	InUse             int           `json:"in_use"`
	Idle              int           `json:"idle"`
	WaitCount         int64         `json:"wait_count"`
	WaitDuration      time.Duration `json:"wait_duration"`
	MaxIdleClosed     int64         `json:"max_idle_closed"`
	MaxLifetimeClosed int64         `json:"max_lifetime_closed"`
}

DatabaseStats provides connection pool statistics.

type DatabaseType

type DatabaseType string

DatabaseType represents the type of database.

const (
	TypePostgres DatabaseType = "postgres"
	TypeMySQL    DatabaseType = "mysql"
	TypeSQLite   DatabaseType = "sqlite"
	TypeMongoDB  DatabaseType = "mongodb"
	TypeRedis    DatabaseType = "redis"
)

type ExampleSeeder added in v0.7.3

type ExampleSeeder struct {
}

ExampleSeeder demonstrates how to create a seeder. This can be used as a template for your own seeders.

func (*ExampleSeeder) Name added in v0.7.3

func (s *ExampleSeeder) Name() string

Name returns the unique identifier for this seeder.

func (*ExampleSeeder) Seed added in v0.7.3

func (s *ExampleSeeder) Seed(ctx context.Context, db *bun.DB) error

Seed executes the seeding logic.

type Extension

type Extension struct {
	*forge.BaseExtension
	// contains filtered or unexported fields
}

Extension implements the database extension.

func (*Extension) Health

func (e *Extension) Health(ctx context.Context) error

Health checks the extension health.

func (*Extension) Register

func (e *Extension) Register(app forge.App) error

Register registers the extension with the application.

func (*Extension) Start

func (e *Extension) Start(ctx context.Context) error

Start starts the extension. Note: Database connections are opened by the DI container when it calls DatabaseManager.Start() during container.Start(). This ensures connections are available to other extensions that depend on the database.

func (*Extension) Stop

func (e *Extension) Stop(ctx context.Context) error

Stop stops the extension. Note: Database connections are closed by the DI container when it calls DatabaseManager.Stop() during container.Stop().

type HealthStatus

type HealthStatus struct {
	Healthy   bool          `json:"healthy"`
	Message   string        `json:"message"`
	Latency   time.Duration `json:"latency"`
	CheckedAt time.Time     `json:"checked_at"`
}

HealthStatus provides database health status.

type IDB added in v0.7.3

type IDB interface {
	bun.IDB
}

IDB is an interface that both *bun.DB and bun.Tx implement. This allows Repository to work with both direct database access and transactions.

type Logger added in v0.4.0

type Logger interface {
	Info(msg string, fields ...any)
	Error(msg string, fields ...any)
	Warn(msg string, fields ...any)
}

Logger interface for migration logging.

type MigrationManager added in v0.4.0

type MigrationManager struct {
	// contains filtered or unexported fields
}

MigrationManager manages database migrations.

func NewMigrationManager added in v0.4.0

func NewMigrationManager(db *bun.DB, migrations *migrate.Migrations, logger Logger) *MigrationManager

NewMigrationManager creates a new migration manager.

func (*MigrationManager) AutoMigrate added in v0.4.0

func (m *MigrationManager) AutoMigrate(ctx context.Context, models ...any) error

AutoMigrate automatically creates/updates tables for registered models This is a development convenience - use migrations for production.

func (*MigrationManager) CreateMigration added in v0.4.0

func (m *MigrationManager) CreateMigration(ctx context.Context) error

CreateMigration creates the migration tables and initial structure.

func (*MigrationManager) CreateTables added in v0.4.0

func (m *MigrationManager) CreateTables(ctx context.Context) error

CreateTables creates the migrations table.

func (*MigrationManager) Migrate added in v0.4.0

func (m *MigrationManager) Migrate(ctx context.Context) error

Migrate runs all pending migrations.

func (*MigrationManager) Reset added in v0.4.0

func (m *MigrationManager) Reset(ctx context.Context) error

Reset drops all tables and re-runs all migrations.

func (*MigrationManager) Rollback added in v0.4.0

func (m *MigrationManager) Rollback(ctx context.Context) error

Rollback rolls back the last migration group.

func (*MigrationManager) Status added in v0.4.0

Status returns the current migration status.

type MigrationStatus

type MigrationStatus struct {
	ID        int64     `json:"id"`
	Applied   bool      `json:"applied"`
	AppliedAt time.Time `json:"applied_at"`
}

MigrationStatus provides migration status.

type MigrationStatusResult added in v0.4.0

type MigrationStatusResult struct {
	Applied []AppliedMigration
	Pending []string
}

MigrationStatusResult represents the current state of migrations.

type MongoDatabase

type MongoDatabase struct {
	// contains filtered or unexported fields
}

MongoDatabase wraps MongoDB client.

func NewMongoDatabase

func NewMongoDatabase(config DatabaseConfig, logger forge.Logger, metrics forge.Metrics) (*MongoDatabase, error)

NewMongoDatabase creates a new MongoDB database instance.

func (*MongoDatabase) Client

func (d *MongoDatabase) Client() *mongo.Client

Client returns the MongoDB client.

func (*MongoDatabase) Close

func (d *MongoDatabase) Close(ctx context.Context) error

Close closes the MongoDB connection.

func (*MongoDatabase) Collection

func (d *MongoDatabase) Collection(name string) *mongo.Collection

Collection returns a MongoDB collection.

func (*MongoDatabase) Database

func (d *MongoDatabase) Database() *mongo.Database

Database returns the MongoDB database.

func (*MongoDatabase) Driver

func (d *MongoDatabase) Driver() any

Driver returns the *mongo.Client for native driver access.

func (*MongoDatabase) Health

func (d *MongoDatabase) Health(ctx context.Context) HealthStatus

Health returns the health status.

func (*MongoDatabase) IsOpen added in v0.4.0

func (d *MongoDatabase) IsOpen() bool

IsOpen returns whether the database is connected.

func (*MongoDatabase) Name

func (d *MongoDatabase) Name() string

Name returns the database name.

func (*MongoDatabase) Open

func (d *MongoDatabase) Open(ctx context.Context) error

Open establishes the MongoDB connection with retry logic.

func (*MongoDatabase) Ping

func (d *MongoDatabase) Ping(ctx context.Context) error

Ping checks MongoDB connectivity.

func (*MongoDatabase) State added in v0.4.0

func (d *MongoDatabase) State() ConnectionState

State returns the current connection state.

func (*MongoDatabase) Stats

func (d *MongoDatabase) Stats() DatabaseStats

Stats returns MongoDB statistics.

func (*MongoDatabase) Transaction

func (d *MongoDatabase) Transaction(ctx context.Context, fn func(sessCtx mongo.SessionContext) error) (err error)

Transaction executes a function in a MongoDB transaction with panic recovery.

func (*MongoDatabase) TransactionWithOptions

func (d *MongoDatabase) TransactionWithOptions(ctx context.Context, opts *options.TransactionOptions, fn func(sessCtx mongo.SessionContext) error) (err error)

TransactionWithOptions executes a function in a MongoDB transaction with options and panic recovery.

func (*MongoDatabase) Type

func (d *MongoDatabase) Type() DatabaseType

Type returns the database type.

type MultiError added in v0.4.0

type MultiError struct {
	Errors map[string]error
}

MultiError represents multiple database errors.

func (*MultiError) Error added in v0.4.0

func (e *MultiError) Error() string

func (*MultiError) HasErrors added in v0.4.0

func (e *MultiError) HasErrors() bool

HasErrors returns true if there are any errors.

type ObservabilityQueryHook added in v0.7.3

type ObservabilityQueryHook struct {
	// contains filtered or unexported fields
}

ObservabilityQueryHook is an enhanced query hook that can automatically EXPLAIN slow queries.

func NewObservabilityQueryHook added in v0.7.3

func NewObservabilityQueryHook(logger forge.Logger, metrics forge.Metrics, dbName string, dbType DatabaseType, slowQueryThreshold time.Duration, disableSlowQueryLogging bool) *ObservabilityQueryHook

NewObservabilityQueryHook creates a new observability query hook.

func (*ObservabilityQueryHook) AfterQuery added in v0.7.3

func (h *ObservabilityQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent)

AfterQuery is called after query execution.

func (*ObservabilityQueryHook) BeforeQuery added in v0.7.3

func (h *ObservabilityQueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context

BeforeQuery is called before query execution.

func (*ObservabilityQueryHook) WithAutoExplain added in v0.7.3

func (h *ObservabilityQueryHook) WithAutoExplain(threshold time.Duration) *ObservabilityQueryHook

WithAutoExplain enables automatic EXPLAIN for queries slower than the threshold.

type OffsetPagination added in v0.7.3

type OffsetPagination struct {
	// Page number (1-indexed). Defaults to 1 if not specified.
	Page int `json:"page"`

	// Number of records per page. Defaults to 20 if not specified.
	PageSize int `json:"page_size"`

	// Column to sort by. If empty, no ordering is applied.
	Sort string `json:"sort,omitempty"`

	// Sort order: "asc" or "desc". Defaults to "asc".
	Order string `json:"order,omitempty"`
}

OffsetPagination configures offset-based pagination. This is the traditional page number approach, suitable for most use cases with moderate data sizes (up to hundreds of thousands of records).

Example:

params := database.OffsetPagination{
    Page:     1,
    PageSize: 20,
    Sort:     "created_at",
    Order:    "desc",
}

func (*OffsetPagination) Normalize added in v0.7.3

func (p *OffsetPagination) Normalize()

Normalize ensures pagination parameters are within valid ranges.

func (*OffsetPagination) Offset added in v0.7.3

func (p *OffsetPagination) Offset() int

Offset calculates the SQL OFFSET value from page and page size.

type PaginatedResult added in v0.7.3

type PaginatedResult[T any] struct {
	// Data contains the records for the current page
	Data []T `json:"data"`

	// Page is the current page number (1-indexed)
	Page int `json:"page"`

	// PageSize is the number of records per page
	PageSize int `json:"page_size"`

	// TotalPages is the total number of pages
	TotalPages int `json:"total_pages"`

	// TotalCount is the total number of records across all pages
	TotalCount int64 `json:"total_count"`

	// HasNext indicates if there's a next page
	HasNext bool `json:"has_next"`

	// HasPrev indicates if there's a previous page
	HasPrev bool `json:"has_prev"`
}

PaginatedResult contains the paginated data and metadata.

func MapPaginated added in v0.7.3

func MapPaginated[From, To any](from *PaginatedResult[From], mapFn func(From) To) *PaginatedResult[To]

MapPaginated transforms a PaginatedResult from one type to another. The pagination metadata is preserved while the data is transformed.

Example:

result, err := database.Paginate[User](ctx, query, params)
dtoResult := database.MapPaginated(result, func(u User) UserDTO {
    return UserDTO{ID: u.ID, Name: u.Name}
})

func MapPaginatedError added in v0.7.3

func MapPaginatedError[From, To any](from *PaginatedResult[From], mapFn func(From) (To, error)) (*PaginatedResult[To], error)

MapPaginatedError transforms a PaginatedResult with a fallible mapper.

Example:

dtoResult, err := database.MapPaginatedError(result, func(u User) (UserDTO, error) {
    return toDTO(u)
})

func Paginate added in v0.7.3

func Paginate[T any](ctx context.Context, query *bun.SelectQuery, params OffsetPagination) (*PaginatedResult[T], error)

Paginate performs offset-based pagination on a Bun SelectQuery. It executes two queries: one for the total count and one for the data.

Example:

query := db.NewSelect().Model((*User)(nil))
result, err := database.Paginate[User](ctx, query, database.OffsetPagination{
    Page:     2,
    PageSize: 20,
    Sort:     "created_at",
    Order:    "desc",
})
if err != nil {
    return err
}
// result.Data contains the users for page 2
// result.TotalCount contains the total number of users

type ProgressCallback added in v0.7.3

type ProgressCallback func(progress BulkOperationProgress)

ProgressCallback is called periodically during bulk operations.

type QueryHook

type QueryHook struct {
	// contains filtered or unexported fields
}

QueryHook provides observability for Bun queries.

func (*QueryHook) AfterQuery

func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent)

AfterQuery is called after query execution.

func (*QueryHook) BeforeQuery

func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context

BeforeQuery is called before query execution.

type QueryOption added in v0.7.3

type QueryOption func(*bun.SelectQuery) *bun.SelectQuery

QueryOption is a function that modifies a SelectQuery. This pattern allows flexible, composable query building.

Example:

func WhereActive() QueryOption {
    return func(q *bun.SelectQuery) *bun.SelectQuery {
        return q.Where("deleted_at IS NULL")
    }
}

func WhereActive added in v0.7.3

func WhereActive() QueryOption

WhereActive returns a QueryOption that filters out soft-deleted records. This is explicit - records are NOT filtered automatically.

func WhereDeleted added in v0.7.3

func WhereDeleted() QueryOption

WhereDeleted returns a QueryOption that only returns soft-deleted records.

func WithLimit added in v0.7.3

func WithLimit(limit int) QueryOption

WithLimit returns a QueryOption that limits the number of results.

func WithOffset added in v0.7.3

func WithOffset(offset int) QueryOption

WithOffset returns a QueryOption that skips the first n results.

func WithOrder added in v0.7.3

func WithOrder(column string, direction ...string) QueryOption

WithOrder returns a QueryOption that orders results by the specified column.

func WithRelation added in v0.7.3

func WithRelation(relation string) QueryOption

WithRelation returns a QueryOption that eager loads a relation.

func WithRelations added in v0.7.3

func WithRelations(relations ...string) QueryOption

WithRelations returns a QueryOption that eager loads multiple relations.

type QueryPlan added in v0.7.3

type QueryPlan struct {
	// Query is the SQL query that was analyzed
	Query string `json:"query"`

	// Plan is the formatted execution plan
	Plan string `json:"plan"`

	// Cost is the estimated query cost (database-specific)
	Cost float64 `json:"cost"`

	// Duration is how long the query took to execute
	Duration time.Duration `json:"duration"`

	// Database type (postgres, mysql, sqlite)
	DBType DatabaseType `json:"db_type"`
}

QueryPlan represents the execution plan for a query.

func ExplainQuery added in v0.7.3

func ExplainQuery(ctx context.Context, db *bun.DB, query *bun.SelectQuery) (*QueryPlan, error)

ExplainQuery executes EXPLAIN on a query and returns the execution plan. This is useful for debugging slow queries and optimizing performance.

Example:

query := db.NewSelect().Model((*User)(nil)).Where("email = ?", "test@example.com")
plan, err := database.ExplainQuery(ctx, db, query)
if err != nil {
    log.Error("failed to explain query", err)
}
log.Info("Query plan", "plan", plan.Plan, "cost", plan.Cost)

type QueryStats added in v0.7.3

type QueryStats struct {
	TotalQueries    int64         `json:"total_queries"`
	SlowQueries     int64         `json:"slow_queries"`
	FailedQueries   int64         `json:"failed_queries"`
	AverageDuration time.Duration `json:"average_duration"`
	MaxDuration     time.Duration `json:"max_duration"`
}

QueryStats provides statistics about query execution.

type RedisCommandHook added in v0.7.0

type RedisCommandHook struct {
	// contains filtered or unexported fields
}

RedisCommandHook provides observability for Redis commands.

func (*RedisCommandHook) DialHook added in v0.7.0

func (h *RedisCommandHook) DialHook(next redis.DialHook) redis.DialHook

DialHook is called when a new connection is established.

func (*RedisCommandHook) ProcessHook added in v0.7.0

func (h *RedisCommandHook) ProcessHook(next redis.ProcessHook) redis.ProcessHook

ProcessHook is called for each command.

func (*RedisCommandHook) ProcessPipelineHook added in v0.7.0

ProcessPipelineHook is called for pipelined commands.

type RedisDatabase added in v0.7.0

type RedisDatabase struct {
	// contains filtered or unexported fields
}

RedisDatabase wraps go-redis client with support for standalone, cluster, and sentinel modes.

func NewRedisDatabase added in v0.7.0

func NewRedisDatabase(config DatabaseConfig, logger forge.Logger, metrics forge.Metrics) (*RedisDatabase, error)

NewRedisDatabase creates a new Redis database instance.

func (*RedisDatabase) Client added in v0.7.0

func (d *RedisDatabase) Client() redis.UniversalClient

Client returns the Redis universal client.

func (*RedisDatabase) Close added in v0.7.0

func (d *RedisDatabase) Close(ctx context.Context) error

Close closes the Redis connection.

func (*RedisDatabase) Driver added in v0.7.0

func (d *RedisDatabase) Driver() any

Driver returns the redis.UniversalClient for native driver access.

func (*RedisDatabase) Health added in v0.7.0

func (d *RedisDatabase) Health(ctx context.Context) HealthStatus

Health returns the health status.

func (*RedisDatabase) IsOpen added in v0.7.0

func (d *RedisDatabase) IsOpen() bool

IsOpen returns whether the database is connected.

func (*RedisDatabase) Name added in v0.7.0

func (d *RedisDatabase) Name() string

Name returns the database name.

func (*RedisDatabase) Open added in v0.7.0

func (d *RedisDatabase) Open(ctx context.Context) error

Open establishes the Redis connection with retry logic.

func (*RedisDatabase) Ping added in v0.7.0

func (d *RedisDatabase) Ping(ctx context.Context) error

Ping checks Redis connectivity.

func (*RedisDatabase) Pipeline added in v0.7.0

func (d *RedisDatabase) Pipeline() redis.Pipeliner

Pipeline creates a Redis pipeline for batching commands.

func (*RedisDatabase) Pipelined added in v0.7.0

func (d *RedisDatabase) Pipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error)

Pipelined executes commands in a pipeline with panic recovery.

func (*RedisDatabase) State added in v0.7.0

func (d *RedisDatabase) State() ConnectionState

State returns the current connection state.

func (*RedisDatabase) Stats added in v0.7.0

func (d *RedisDatabase) Stats() DatabaseStats

Stats returns connection pool statistics.

func (*RedisDatabase) Transaction added in v0.7.0

func (d *RedisDatabase) Transaction(ctx context.Context, watchKeys []string, fn func(tx *redis.Tx) error) (err error)

Transaction executes a function in a Redis transaction with panic recovery. Uses WATCH/MULTI/EXEC pattern for optimistic locking.

func (*RedisDatabase) TxPipeline added in v0.7.0

func (d *RedisDatabase) TxPipeline() redis.Pipeliner

TxPipeline creates a Redis transaction pipeline (MULTI/EXEC).

func (*RedisDatabase) TxPipelined added in v0.7.0

func (d *RedisDatabase) TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error)

TxPipelined executes commands in a transaction pipeline (MULTI/EXEC) with panic recovery.

func (*RedisDatabase) Type added in v0.7.0

func (d *RedisDatabase) Type() DatabaseType

Type returns the database type.

type RedisMode added in v0.7.0

type RedisMode string

RedisMode represents the Redis connection mode.

const (
	RedisModeStandalone RedisMode = "standalone"
	RedisModeCluster    RedisMode = "cluster"
	RedisModeSentinel   RedisMode = "sentinel"
)

type Repository added in v0.7.3

type Repository[T any] struct {
	// contains filtered or unexported fields
}

Repository provides a generic, type-safe repository pattern for database operations. It eliminates boilerplate CRUD code while maintaining flexibility through QueryOptions.

Example usage:

type User struct {
    ID   int64  `bun:"id,pk,autoincrement"`
    Name string `bun:"name"`
}

repo := database.NewRepository[User](db)
user, err := repo.FindByID(ctx, 123)
users, err := repo.FindAll(ctx, WhereActive())

func NewRepository added in v0.7.3

func NewRepository[T any](db IDB) *Repository[T]

NewRepository creates a new repository instance for type T. The db parameter can be either *bun.DB or bun.Tx, allowing repositories to work seamlessly in and out of transactions.

func NewRepositoryFromApp added in v0.7.3

func NewRepositoryFromApp[T any](app forge.App) *Repository[T]

NewRepositoryFromApp creates a repository using the database from the app.

func NewRepositoryFromContainer added in v0.7.3

func NewRepositoryFromContainer[T any](c forge.Container) *Repository[T]

NewRepositoryFromContainer creates a repository using the database from the container. This is a convenience wrapper for the common pattern of getting DB and creating a repo.

Example:

func NewUserService(c forge.Container) *UserService {
    return &UserService{
        userRepo: database.NewRepositoryFromContainer[User](c),
    }
}

func (*Repository[T]) Count added in v0.7.3

func (r *Repository[T]) Count(ctx context.Context, opts ...QueryOption) (int, error)

Count returns the number of records matching the query options.

Example:

count, err := repo.Count(ctx, func(q *bun.SelectQuery) *bun.SelectQuery {
    return q.Where("age > ?", 18)
})

func (*Repository[T]) Create added in v0.7.3

func (r *Repository[T]) Create(ctx context.Context, entity *T) error

Create inserts a new record into the database. The entity's BeforeInsert hook will be called if it implements it.

Example:

user := &User{Name: "John"}
err := repo.Create(ctx, user)
// user.ID is now populated

func (*Repository[T]) CreateMany added in v0.7.3

func (r *Repository[T]) CreateMany(ctx context.Context, entities []T) error

CreateMany inserts multiple records in a single query. Much more efficient than calling Create in a loop.

Example:

users := []User{{Name: "John"}, {Name: "Jane"}}
err := repo.CreateMany(ctx, users)

func (*Repository[T]) DB added in v0.7.3

func (r *Repository[T]) DB() IDB

DB returns the underlying database connection. Useful when you need direct access to Bun's query builder.

func (*Repository[T]) Delete added in v0.7.3

func (r *Repository[T]) Delete(ctx context.Context, id any) error

Delete permanently deletes a record by ID. For soft deletes, use SoftDelete instead.

Example:

err := repo.Delete(ctx, 123)

func (*Repository[T]) DeleteMany added in v0.7.3

func (r *Repository[T]) DeleteMany(ctx context.Context, ids []any) error

DeleteMany permanently deletes multiple records by IDs.

Example:

err := repo.DeleteMany(ctx, []int64{1, 2, 3})

func (*Repository[T]) Exists added in v0.7.3

func (r *Repository[T]) Exists(ctx context.Context, id any) (bool, error)

Exists checks if a record with the given ID exists.

Example:

exists, err := repo.Exists(ctx, 123)

func (*Repository[T]) FindAll added in v0.7.3

func (r *Repository[T]) FindAll(ctx context.Context, opts ...QueryOption) ([]T, error)

FindAll retrieves all records matching the query options. Returns an empty slice if no records found (not an error).

Example:

users, err := repo.FindAll(ctx,
    func(q *bun.SelectQuery) *bun.SelectQuery {
        return q.Where("age > ?", 18).Order("created_at DESC")
    },
)

func (*Repository[T]) FindAllWithDeleted added in v0.7.3

func (r *Repository[T]) FindAllWithDeleted(ctx context.Context, opts ...QueryOption) ([]T, error)

FindAllWithDeleted retrieves all records including soft-deleted ones. Only works with models that have a DeletedAt field with soft_delete tag.

Example:

allUsers, err := repo.FindAllWithDeleted(ctx)

func (*Repository[T]) FindByID added in v0.7.3

func (r *Repository[T]) FindByID(ctx context.Context, id any, opts ...QueryOption) (*T, error)

FindByID retrieves a single record by its primary key. Returns ErrRecordNotFound if no record exists.

Example:

user, err := repo.FindByID(ctx, 123)
if errors.Is(err, database.ErrRecordNotFound(123)) {
    // Handle not found
}

func (*Repository[T]) FindOne added in v0.7.3

func (r *Repository[T]) FindOne(ctx context.Context, opts ...QueryOption) (*T, error)

FindOne retrieves a single record based on query options. Returns ErrRecordNotFound if no record exists.

Example:

user, err := repo.FindOne(ctx, func(q *bun.SelectQuery) *bun.SelectQuery {
    return q.Where("email = ?", "user@example.com")
})

func (*Repository[T]) Query added in v0.7.3

func (r *Repository[T]) Query() *bun.SelectQuery

Query returns a new SelectQuery for the repository's model type. This is useful when you need to build custom queries that aren't covered by the standard repository methods.

Example:

query := repo.Query().
    Where("age > ?", 18).
    Order("created_at DESC").
    Limit(10)
var users []User
err := query.Scan(ctx, &users)

func (*Repository[T]) RestoreSoftDeleted added in v0.7.3

func (r *Repository[T]) RestoreSoftDeleted(ctx context.Context, id any) error

RestoreSoftDeleted restores a soft-deleted record. Only works with models that have a DeletedAt field with soft_delete tag.

Example:

err := repo.RestoreSoftDeleted(ctx, 123)

func (*Repository[T]) SoftDelete added in v0.7.3

func (r *Repository[T]) SoftDelete(ctx context.Context, id any) error

SoftDelete marks a record as deleted by setting its DeletedAt field. Only works with models that have a DeletedAt field with soft_delete tag.

Example:

err := repo.SoftDelete(ctx, 123)

func (*Repository[T]) Truncate added in v0.7.3

func (r *Repository[T]) Truncate(ctx context.Context) error

Truncate removes all records from the table. WARNING: This is a destructive operation and cannot be undone. Use with extreme caution, typically only in tests.

Example:

err := repo.Truncate(ctx)

func (*Repository[T]) Update added in v0.7.3

func (r *Repository[T]) Update(ctx context.Context, entity *T) error

Update updates an existing record. The entity's BeforeUpdate hook will be called if it implements it. By default, all non-zero fields are updated.

Example:

user.Name = "Jane"
err := repo.Update(ctx, user)

func (*Repository[T]) UpdateColumns added in v0.7.3

func (r *Repository[T]) UpdateColumns(ctx context.Context, entity *T, columns ...string) error

UpdateColumns updates only specific columns of a record. Useful when you only want to update certain fields.

Example:

err := repo.UpdateColumns(ctx, user, "name", "email")

type SQLDatabase

type SQLDatabase struct {
	// contains filtered or unexported fields
}

SQLDatabase wraps Bun ORM for SQL databases.

func NewSQLDatabase

func NewSQLDatabase(config DatabaseConfig, logger forge.Logger, metrics forge.Metrics) (*SQLDatabase, error)

NewSQLDatabase creates a new SQL database instance.

func (*SQLDatabase) Bun

func (d *SQLDatabase) Bun() *bun.DB

Bun returns the Bun ORM instance.

func (*SQLDatabase) Close

func (d *SQLDatabase) Close(ctx context.Context) error

Close closes the database connection.

func (*SQLDatabase) DB

func (d *SQLDatabase) DB() *sql.DB

DB returns the raw *sql.DB.

func (*SQLDatabase) Driver

func (d *SQLDatabase) Driver() any

Driver returns the raw *sql.DB for native driver access.

func (*SQLDatabase) Health

func (d *SQLDatabase) Health(ctx context.Context) HealthStatus

Health returns the health status.

func (*SQLDatabase) IsOpen added in v0.4.0

func (d *SQLDatabase) IsOpen() bool

IsOpen returns whether the database is connected.

func (*SQLDatabase) Name

func (d *SQLDatabase) Name() string

Name returns the database name.

func (*SQLDatabase) Open

func (d *SQLDatabase) Open(ctx context.Context) error

Open establishes the database connection with retry logic.

func (*SQLDatabase) Ping

func (d *SQLDatabase) Ping(ctx context.Context) error

Ping checks database connectivity.

func (*SQLDatabase) State added in v0.4.0

func (d *SQLDatabase) State() ConnectionState

State returns the current connection state.

func (*SQLDatabase) Stats

func (d *SQLDatabase) Stats() DatabaseStats

Stats returns connection pool statistics.

func (*SQLDatabase) Transaction

func (d *SQLDatabase) Transaction(ctx context.Context, fn func(tx bun.Tx) error) (err error)

Transaction executes a function in a SQL transaction with panic recovery.

func (*SQLDatabase) TransactionWithOptions

func (d *SQLDatabase) TransactionWithOptions(ctx context.Context, opts *sql.TxOptions, fn func(tx bun.Tx) error) (err error)

TransactionWithOptions executes a function in a SQL transaction with options and panic recovery.

func (*SQLDatabase) Type

func (d *SQLDatabase) Type() DatabaseType

Type returns the database type.

type Seeder added in v0.7.3

type Seeder interface {
	// Name returns a unique identifier for this seeder.
	Name() string

	// Seed executes the seeding logic.
	// Should return an error if seeding fails.
	Seed(ctx context.Context, db *bun.DB) error
}

Seeder defines the interface for database seeders. Seeders populate the database with initial or test data.

Seeders should be idempotent - safe to run multiple times.

type SeederFunc added in v0.7.3

type SeederFunc struct {
	// contains filtered or unexported fields
}

SeederFunc is a function type that implements the Seeder interface. This allows you to create seeders from functions.

func NewSeederFunc added in v0.7.3

func NewSeederFunc(name string, fn func(ctx context.Context, db *bun.DB) error) *SeederFunc

NewSeederFunc creates a seeder from a function.

Example:

seeder := database.NewSeederFunc("users", func(ctx context.Context, db *bun.DB) error {
    users := []User{{Name: "Alice"}, {Name: "Bob"}}
    return database.BulkInsert(ctx, db, users, 0)
})

func (*SeederFunc) Name added in v0.7.3

func (s *SeederFunc) Name() string

Name implements Seeder interface.

func (*SeederFunc) Seed added in v0.7.3

func (s *SeederFunc) Seed(ctx context.Context, db *bun.DB) error

Seed implements Seeder interface.

type SeederRecord added in v0.7.3

type SeederRecord struct {
	bun.BaseModel `bun:"table:seeders"`

	ID        int64     `bun:"id,pk,autoincrement"`
	Name      string    `bun:"name,unique,notnull"`
	RunAt     time.Time `bun:"run_at,notnull"`
	Duration  int64     `bun:"duration"` // Duration in milliseconds
	Success   bool      `bun:"success"`
	ErrorMsg  string    `bun:"error_msg"`
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
}

SeederRecord tracks which seeders have been run.

type SeederRunner added in v0.7.3

type SeederRunner struct {
	// contains filtered or unexported fields
}

SeederRunner manages and executes database seeders.

func NewSeederRunner added in v0.7.3

func NewSeederRunner(db *bun.DB, logger forge.Logger) *SeederRunner

NewSeederRunner creates a new seeder runner.

Example:

runner := database.NewSeederRunner(db, logger)
runner.Register(&UserSeeder{})
runner.Register(&ProductSeeder{})
err := runner.Run(ctx)

func (*SeederRunner) GetSeeder added in v0.7.3

func (r *SeederRunner) GetSeeder(name string) (Seeder, bool)

GetSeeder returns a seeder by name.

func (*SeederRunner) List added in v0.7.3

func (r *SeederRunner) List() []string

List returns the names of all registered seeders.

func (*SeederRunner) Register added in v0.7.3

func (r *SeederRunner) Register(seeder Seeder)

Register adds a seeder to the runner.

func (*SeederRunner) RegisterMany added in v0.7.3

func (r *SeederRunner) RegisterMany(seeders ...Seeder)

RegisterMany registers multiple seeders at once.

func (*SeederRunner) Reset added in v0.7.3

func (r *SeederRunner) Reset(ctx context.Context, name string) error

Reset clears the seeder tracking for a specific seeder. This allows the seeder to be run again.

Example:

err := runner.Reset(ctx, "users")

func (*SeederRunner) ResetAll added in v0.7.3

func (r *SeederRunner) ResetAll(ctx context.Context) error

ResetAll clears all seeder tracking. This allows all seeders to be run again.

Example:

err := runner.ResetAll(ctx)

func (*SeederRunner) Run added in v0.7.3

func (r *SeederRunner) Run(ctx context.Context) error

Run executes all registered seeders that haven't been run yet. If a seeder has already been run successfully, it's skipped.

Example:

err := runner.Run(ctx)
if err != nil {
    log.Fatal(err)
}

func (*SeederRunner) RunSeeder added in v0.7.3

func (r *SeederRunner) RunSeeder(ctx context.Context, name string) error

RunSeeder executes a specific seeder by name. Unlike Run(), this always executes the seeder even if it has run before.

Example:

err := runner.RunSeeder(ctx, "users")

func (*SeederRunner) WithTracking added in v0.7.3

func (r *SeederRunner) WithTracking(enabled bool) *SeederRunner

WithTracking enables or disables tracking of seeder execution. When enabled, seeder runs are recorded in the database.

type SoftDeleteModel added in v0.4.0

type SoftDeleteModel struct {
	ID        int64      `bun:"id,pk,autoincrement"                                   json:"id"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero"                       json:"deleted_at,omitempty"`
}

SoftDeleteModel provides soft delete functionality with timestamps Soft-deleted records are not permanently removed, just marked as deleted.

func (*SoftDeleteModel) BeforeDelete added in v0.4.0

func (m *SoftDeleteModel) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error

BeforeDelete hook - performs soft delete.

func (*SoftDeleteModel) BeforeInsert added in v0.4.0

func (m *SoftDeleteModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - sets timestamps.

func (*SoftDeleteModel) BeforeUpdate added in v0.4.0

func (m *SoftDeleteModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

func (*SoftDeleteModel) IsDeleted added in v0.4.0

func (m *SoftDeleteModel) IsDeleted() bool

IsDeleted checks if the record is soft deleted.

func (*SoftDeleteModel) Restore added in v0.4.0

func (m *SoftDeleteModel) Restore()

Restore restores a soft-deleted record.

type TestDBOption added in v0.7.3

type TestDBOption func(*testDBConfig)

TestDBOption configures test database behavior.

func WithAutoMigrate added in v0.7.3

func WithAutoMigrate(models ...any) TestDBOption

WithAutoMigrate automatically creates tables for the provided models.

func WithInMemory added in v0.7.3

func WithInMemory() TestDBOption

WithInMemory creates an in-memory SQLite database (default).

func WithLogging added in v0.7.3

func WithLogging() TestDBOption

WithLogging enables query logging for debugging.

func WithTransactionRollback added in v0.7.3

func WithTransactionRollback() TestDBOption

WithTransactionRollback wraps each test in a transaction that's rolled back. This is faster than truncating tables between tests.

type TimestampModel added in v0.4.0

type TimestampModel struct {
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
}

TimestampModel provides only timestamp fields without ID Use this when you want to add your own ID field type.

func (*TimestampModel) BeforeInsert added in v0.4.0

func (m *TimestampModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - sets timestamps.

func (*TimestampModel) BeforeUpdate added in v0.4.0

func (m *TimestampModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

type TransactionStats added in v0.7.3

type TransactionStats struct {
	Active     int32 // Number of active transactions
	Committed  int64 // Total committed transactions
	RolledBack int64 // Total rolled back transactions
	Panics     int64 // Total panics recovered
}

TransactionStats tracks transaction statistics.

func GetTransactionStats added in v0.7.3

func GetTransactionStats() TransactionStats

GetTransactionStats returns global transaction statistics. Useful for monitoring and debugging.

type TxFunc added in v0.7.3

type TxFunc func(ctx context.Context) error

TxFunc is a function that runs within a transaction. The context passed to the function contains the active transaction, which can be retrieved using GetDB.

type UUIDModel added in v0.4.0

type UUIDModel struct {
	ID        uuid.UUID `bun:"id,pk,type:uuid,default:gen_random_uuid()"             json:"id"`
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
}

UUIDModel provides UUID-based primary key with timestamps Use this for distributed systems or when you need globally unique IDs.

func (*UUIDModel) BeforeInsert added in v0.4.0

func (m *UUIDModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - generates UUID and sets timestamps.

func (*UUIDModel) BeforeUpdate added in v0.4.0

func (m *UUIDModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

type UUIDSoftDeleteModel added in v0.4.0

type UUIDSoftDeleteModel struct {
	ID        uuid.UUID  `bun:"id,pk,type:uuid,default:gen_random_uuid()"             json:"id"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero"                       json:"deleted_at,omitempty"`
}

UUIDSoftDeleteModel combines UUID primary key with soft delete.

func (*UUIDSoftDeleteModel) BeforeDelete added in v0.4.0

func (m *UUIDSoftDeleteModel) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error

BeforeDelete hook - performs soft delete.

func (*UUIDSoftDeleteModel) BeforeInsert added in v0.4.0

func (m *UUIDSoftDeleteModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - generates UUID and sets timestamps.

func (*UUIDSoftDeleteModel) BeforeUpdate added in v0.4.0

func (m *UUIDSoftDeleteModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

func (*UUIDSoftDeleteModel) IsDeleted added in v0.4.0

func (m *UUIDSoftDeleteModel) IsDeleted() bool

IsDeleted checks if the record is soft deleted.

func (*UUIDSoftDeleteModel) Restore added in v0.4.0

func (m *UUIDSoftDeleteModel) Restore()

Restore restores a soft-deleted record.

type XIDAuditModel added in v0.4.0

type XIDAuditModel struct {
	ID        xid.ID     `bun:"id,pk,type:varchar(20)"                                json:"id"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	CreatedBy *xid.ID    `bun:"created_by,type:varchar(20)"                           json:"created_by,omitempty"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	UpdatedBy *xid.ID    `bun:"updated_by,type:varchar(20)"                           json:"updated_by,omitempty"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero"                       json:"deleted_at,omitempty"`
	DeletedBy *xid.ID    `bun:"deleted_by,type:varchar(20)"                           json:"deleted_by,omitempty"`
}

XIDAuditModel provides comprehensive audit trail with XID primary key and user tracking Combines XID with full audit capabilities: tracks who created/updated/deleted records.

func (*XIDAuditModel) BeforeDelete added in v0.4.0

func (m *XIDAuditModel) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error

BeforeDelete hook - performs soft delete and tracks deleter.

func (*XIDAuditModel) BeforeInsert added in v0.4.0

func (m *XIDAuditModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - generates XID, sets timestamps and tracks creator.

func (*XIDAuditModel) BeforeUpdate added in v0.4.0

func (m *XIDAuditModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt and tracks updater.

func (*XIDAuditModel) IsDeleted added in v0.4.0

func (m *XIDAuditModel) IsDeleted() bool

IsDeleted checks if the record is soft deleted.

func (*XIDAuditModel) Restore added in v0.4.0

func (m *XIDAuditModel) Restore()

Restore restores a soft-deleted record.

type XIDModel added in v0.4.0

type XIDModel struct {
	ID        xid.ID    `bun:"id,pk,type:varchar(20)"                                json:"id"`
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
}

XIDModel provides XID primary key with timestamps XID is a globally unique, sortable, compact, URL-safe identifier It's shorter than UUID (20 bytes vs 36) and sortable by creation time.

func (*XIDModel) BeforeInsert added in v0.4.0

func (m *XIDModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - generates XID and sets timestamps.

func (*XIDModel) BeforeUpdate added in v0.4.0

func (m *XIDModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

type XIDSoftDeleteModel added in v0.4.0

type XIDSoftDeleteModel struct {
	ID        xid.ID     `bun:"id,pk,type:varchar(20)"                                json:"id"`
	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero"                       json:"deleted_at,omitempty"`
}

XIDSoftDeleteModel combines XID primary key with soft delete.

func (*XIDSoftDeleteModel) BeforeDelete added in v0.4.0

func (m *XIDSoftDeleteModel) BeforeDelete(ctx context.Context, query *bun.DeleteQuery) error

BeforeDelete hook - performs soft delete.

func (*XIDSoftDeleteModel) BeforeInsert added in v0.4.0

func (m *XIDSoftDeleteModel) BeforeInsert(ctx context.Context, query *bun.InsertQuery) error

BeforeInsert hook - generates XID and sets timestamps.

func (*XIDSoftDeleteModel) BeforeUpdate added in v0.4.0

func (m *XIDSoftDeleteModel) BeforeUpdate(ctx context.Context, query *bun.UpdateQuery) error

BeforeUpdate hook - updates UpdatedAt.

func (*XIDSoftDeleteModel) IsDeleted added in v0.4.0

func (m *XIDSoftDeleteModel) IsDeleted() bool

IsDeleted checks if the record is soft deleted.

func (*XIDSoftDeleteModel) Restore added in v0.4.0

func (m *XIDSoftDeleteModel) Restore()

Restore restores a soft-deleted record.

Directories

Path Synopsis
Package migrate provides migration management for the database extension
Package migrate provides migration management for the database extension

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL