database

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 2, 2026 License: MIT Imports: 12 Imported by: 0

README

database

GORM wrapper with connection pooling, migrations, query builder, and component lifecycle management.

Install

go get github.com/kbukum/gokit/database@latest

Quick Start

Production (PostgreSQL, MySQL, etc.)
import (
    "github.com/kbukum/gokit/database"
    "github.com/kbukum/gokit/logger"
    "gorm.io/driver/postgres"  // Import your chosen driver
)

cfg := database.Config{
    Enabled:     true,
    DSN:         "host=localhost user=app dbname=mydb sslmode=disable",
    AutoMigrate: true,
}
cfg.ApplyDefaults()

log := logger.New()
comp := database.NewComponent(cfg, log).
    WithDriver(postgres.Open).  // Specify driver function
    WithAutoMigrate(&User{})

// Start as a managed component
comp.Start(ctx)
defer comp.Stop(ctx)

// Use the DB wrapper
db := comp.DB()
db.WithContext(ctx).Find(&users)
db.Transaction(func(tx *gorm.DB) error {
    return tx.Create(&User{Name: "alice"}).Error
})
Optional Components (Disable via Config)
// Database is optional - set Enabled: false to skip initialization
cfg := database.Config{
    Enabled: false,  // Component skips Start(), returns healthy status
    DSN:     "...",
}

comp := database.NewComponent(cfg, log)
comp.Start(ctx)  // No-op, logs "Database component is disabled"
comp.Health(ctx) // Returns StatusHealthy with "disabled" message
Tests/Development (SQLite default)
import (
    "github.com/kbukum/gokit/database"
)

// No driver import needed - SQLite is the default
comp := database.NewComponent(database.Config{
    DSN: ":memory:",  // In-memory SQLite
}, log)

Driver Pattern

Clean, flexible driver injection with no forced dependencies.

How It Works
type DriverFunc func(dsn string) gorm.Dialector

// Pass the driver function (not the result of calling it)
db := database.NewComponent(cfg, log).WithDriver(postgres.Open)

// Component calls it during Start() to control lifecycle
dialector := driverFunc(cfg.DSN)
Supported Drivers

All standard GORM drivers work:

import "gorm.io/driver/postgres"
WithDriver(postgres.Open)

import "gorm.io/driver/mysql"
WithDriver(mysql.Open)

import "gorm.io/driver/sqlite"
WithDriver(sqlite.Open)  // or omit for default

import "gorm.io/driver/sqlserver"
WithDriver(sqlserver.Open)
Key Benefits
  • No forced dependencies - Only SQLite imported by default
  • User controls driver - Import what you need
  • Lifecycle control - Driver called during Start(), enabling retry logic
  • Clean defaults - SQLite works out of the box for tests

Key Types & Functions

Symbol Description
Component Managed lifecycle wrapper — Start, Stop, Health, Describe
Config DSN, pool sizes, slow-query threshold, auto-migrate flag, enabled flag
DB GORM wrapper — WithContext, Transaction, PingContext, AutoMigrate
BaseModel UUID primary key, timestamps, soft-delete
NewComponent(cfg, log) Create a managed database component (defaults to SQLite)
WithDriver(fn) Specify database driver (postgres, mysql, etc.)
WithAutoMigrate(models...) Register models for auto-migration on startup
New(cfg, log, dialector) Create a standalone *DB without lifecycle management
NewWithContext(ctx, dialector, cfg, log) Create *DB with context support (recommended)
IsNotFoundError(err) Check for record-not-found
IsDuplicateError(err) Check for unique constraint violation
FromDatabase(err, resource) Convert database error to AppError
Sub-packages

The database module follows a clean, standardized structure:

Package Description
database/errors Database error utilities and translation to AppError
database/types Common database types like BaseModel
database/migration Driver-agnostic file-based migrations using golang-migrate. Import your database's migrate driver (e.g., migrate/v4/database/postgres). Functions: MigrateUp, MigrateDown, MigrateSteps, MigrateVersion, MigrateReset.
database/query HTTP query parsing and advanced filtering: ParseFromRequestParams; ApplyToGorm[T]*Result[T] with pagination, filtering, sorting, facets, and includes
database/testutil In-memory test components with snapshot/restore capabilities for testing

Migration Usage

Migrations are now driver-agnostic. Import the appropriate golang-migrate driver for your database:

PostgreSQL Migrations
import (
    "database/sql"
    "embed"
    "github.com/kbukum/gokit/database/migration"
    "github.com/golang-migrate/migrate/v4/database"
    migratepg "github.com/golang-migrate/migrate/v4/database/postgres"
)

//go:embed migrations/*.sql
var migrationsFS embed.FS

// Define driver function
driverFunc := func(sqlDB *sql.DB) (database.Driver, error) {
    return migratepg.WithInstance(sqlDB, &migratepg.Config{})
}

// Run migrations
err := migration.MigrateUp(db.GormDB, migrationsFS, "migrations", driverFunc)
if err != nil {
    log.Fatal("Migration failed", err)
}
MySQL Migrations
import migratemysql "github.com/golang-migrate/migrate/v4/database/mysql"

driverFunc := func(sqlDB *sql.DB) (database.Driver, error) {
    return migratemysql.WithInstance(sqlDB, &migratemysql.Config{})
}

err := migration.MigrateUp(db.GormDB, migrationsFS, "migrations", driverFunc)
SQLite Migrations
import migratesqlite "github.com/golang-migrate/migrate/v4/database/sqlite3"

driverFunc := func(sqlDB *sql.DB) (database.Driver, error) {
    return migratesqlite.WithInstance(sqlDB, &migratesqlite.Config{})
}

err := migration.MigrateUp(db.GormDB, migrationsFS, "migrations", driverFunc)
Migration Files

Create migration files in your migrations/ directory:

migrations/
  ├── 001_create_users.up.sql
  ├── 001_create_users.down.sql
  ├── 002_add_email_index.up.sql
  └── 002_add_email_index.down.sql
Other Migration Functions
// Roll back all migrations
migration.MigrateDown(db.GormDB, migrationsFS, "migrations", driverFunc)

// Apply/rollback specific number of migrations
migration.MigrateSteps(db.GormDB, migrationsFS, "migrations", 2, driverFunc)  // up 2
migration.MigrateSteps(db.GormDB, migrationsFS, "migrations", -1, driverFunc) // down 1

// Get current version
version, dirty, err := migration.MigrateVersion(db.GormDB, migrationsFS, "migrations", driverFunc)

// Reset database (development only - destroys all data!)
migration.MigrateReset(db.GormDB, migrationsFS, "migrations", driverFunc)

← Back to main gokit README

Documentation

Overview

Package database provides a GORM-based database component with connection pooling, health checks, transactions, and migration support.

Architecture

The database module follows gokit's component pattern with a driver-agnostic design. Users provide the database driver (postgres, mysql, sqlite, etc.) via WithDriver(), keeping the module lightweight and flexible.

Quick Start

Bootstrap the database component in your application:

import (
    "github.com/kbukum/gokit/bootstrap"
    "github.com/kbukum/gokit/database"
    "gorm.io/driver/postgres"
)

func main() {
    app := bootstrap.New()
    cfg := database.Config{Enabled: true, DSN: "host=localhost user=myuser password=mypass dbname=mydb"}
    app.Register(database.NewComponent(cfg, log).
        WithDriver(func(dsn string) gorm.Dialector {
            return postgres.Open(dsn)
        }))
    app.Start(context.Background())
}

Subpackages

  • errors: Database error utilities and translation to AppError
  • types: Common database types like BaseModel
  • migration: File-based database migrations using golang-migrate
  • query: Advanced query builders and helpers
  • testutil: Testing utilities for database-dependent tests

Optional Component

The database component respects the Enabled flag in configuration. When disabled, Start() returns immediately without initializing the connection, and Health() reports "disabled" status.

cfg := database.Config{Enabled: false}  // Component will be disabled

See component.go for full lifecycle documentation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Component

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

Component wraps DB and implements component.Component for lifecycle management.

func NewComponent

func NewComponent(cfg Config, log *logger.Logger) *Component

NewComponent creates a database component for use with the component registry. By default, SQLite is used. Call WithDriver to specify a different database. The Config.Enabled flag can be used to skip initialization at runtime.

func (*Component) DB

func (c *Component) DB() *DB

DB returns the underlying *DB, or nil if not started.

func (*Component) Describe

func (c *Component) Describe() component.Description

Describe returns infrastructure summary info for the bootstrap display.

func (*Component) Health

func (c *Component) Health(ctx context.Context) component.Health

Health returns the current health status of the database. If Config.Enabled is false, returns StatusHealthy with "disabled" message. The context is used for the ping operation and honors cancellation.

func (*Component) Name

func (c *Component) Name() string

Name returns the component name.

func (*Component) Start

func (c *Component) Start(ctx context.Context) error

Start connects to the database and optionally runs auto-migration. If Config.Enabled is false, this method returns immediately without error. The context is used for connection retries and can be canceled to abort startup.

func (*Component) Stop

func (c *Component) Stop(_ context.Context) error

Stop gracefully closes the database connection.

func (*Component) WithAutoMigrate

func (c *Component) WithAutoMigrate(models ...interface{}) *Component

WithAutoMigrate registers models for auto-migration on Start. Models are only migrated if Config.AutoMigrate is true and the component is enabled.

func (*Component) WithDriver

func (c *Component) WithDriver(fn DriverFunc) *Component

WithDriver sets the database driver function. Pass the Open function from your chosen driver (not the result of calling it).

Example:

import "gorm.io/driver/postgres" db := database.NewComponent(cfg, log).

WithDriver(postgres.Open).
WithAutoMigrate(&User{}, &Post{})

If not set, SQLite is used as the default driver (useful for tests).

type Config

type Config struct {
	// Name identifies this adapter instance (used by provider.Provider interface).
	Name string `yaml:"name" mapstructure:"name"`

	// Enabled controls whether the database component is active.
	Enabled bool `yaml:"enabled" mapstructure:"enabled"`

	// DSN is the database connection string.
	DSN string `yaml:"dsn" mapstructure:"dsn"`

	// MaxOpenConns sets the maximum number of open connections to the database.
	MaxOpenConns int `yaml:"max_open_conns" mapstructure:"max_open_conns"`

	// MaxIdleConns sets the maximum number of idle connections in the pool.
	MaxIdleConns int `yaml:"max_idle_conns" mapstructure:"max_idle_conns"`

	// ConnMaxLifetime is the maximum time a connection may be reused (e.g. "1h", "30m").
	ConnMaxLifetime string `yaml:"conn_max_lifetime" mapstructure:"conn_max_lifetime"`

	// ConnMaxIdleTime is the maximum time a connection may sit idle (e.g. "5m", "10m").
	// If empty, no idle timeout is set.
	ConnMaxIdleTime string `yaml:"conn_max_idle_time" mapstructure:"conn_max_idle_time"`

	// MaxRetries is the number of connection attempts before giving up.
	MaxRetries int `yaml:"max_retries" mapstructure:"max_retries"`

	// AutoMigrate controls whether GORM auto-migration runs on startup.
	AutoMigrate bool `yaml:"auto_migrate" mapstructure:"auto_migrate"`

	// SlowQueryThreshold is the duration above which queries are logged as slow (e.g. "200ms").
	SlowQueryThreshold string `yaml:"slow_query_threshold" mapstructure:"slow_query_threshold"`

	// LogLevel controls GORM's log verbosity: "silent", "error", "warn", "info" (default).
	LogLevel string `yaml:"log_level" mapstructure:"log_level"`
}

Config holds database connection configuration.

func (*Config) ApplyDefaults

func (c *Config) ApplyDefaults()

ApplyDefaults sets sensible defaults for zero-valued fields.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that required fields are present and parseable.

type DB

type DB struct {
	GormDB *gorm.DB
	// contains filtered or unexported fields
}

DB wraps a GORM database with gokit logging.

func New

func New(cfg Config, log *logger.Logger, dialector gorm.Dialector) (*DB, error)

New opens a database connection with retry logic and connection pooling. For most use cases, use Component instead which provides driver flexibility via WithDriver().

func NewWithContext

func NewWithContext(ctx context.Context, dialector interface{}, cfg Config, log *logger.Logger) (*DB, error)

NewWithContext creates a database connection with context-aware retry logic. The context allows cancellation of connection attempts during retries.

func (*DB) AutoMigrate

func (d *DB) AutoMigrate(models ...interface{}) error

AutoMigrate runs GORM auto-migration for the given models.

func (*DB) Close

func (d *DB) Close() error

Close closes the underlying sql.DB connection pool. Safe to call multiple times.

func (*DB) IsAvailable

func (db *DB) IsAvailable(ctx context.Context) bool

IsAvailable checks if the database connection is healthy (implements provider.Provider).

func (*DB) Name

func (db *DB) Name() string

Name returns the adapter name (implements provider.Provider).

func (*DB) Ping

func (d *DB) Ping() error

Ping verifies the database connection is alive.

func (*DB) PingContext

func (d *DB) PingContext(ctx context.Context) error

PingContext verifies the database connection is alive, respecting the context.

func (*DB) Transaction

func (d *DB) Transaction(fn func(*gorm.DB) error) error

Transaction executes fn inside a database transaction.

func (*DB) WithContext

func (d *DB) WithContext(ctx context.Context) *gorm.DB

WithContext returns a GORM session scoped to the given context.

func (*DB) WithReadOnlyTransaction

func (d *DB) WithReadOnlyTransaction(ctx context.Context, fn TransactionFunc) error

WithReadOnlyTransaction executes fn in a read-only transaction (always rolls back).

func (*DB) WithTransaction

func (d *DB) WithTransaction(ctx context.Context, fn TransactionFunc) error

WithTransaction executes fn within a transaction with panic recovery.

type DriverFunc

type DriverFunc func(dsn string) gorm.Dialector

DriverFunc is a factory function that creates a GORM dialector. Standard GORM drivers (postgres.Open, mysql.Open, sqlite.Open) all match this signature.

type TransactionFunc

type TransactionFunc func(tx *gorm.DB) error

TransactionFunc defines a function that runs within a transaction.

Directories

Path Synopsis
Package errors provides database error utilities and translation to gokit AppError types for consistent error handling.
Package errors provides database error utilities and translation to gokit AppError types for consistent error handling.
Package migration provides file-based database migration utilities for GORM.
Package migration provides file-based database migration utilities for GORM.
Package query provides a PostgREST-style query builder for GORM with pagination, sorting, filtering, facets, and eager-loading support.
Package query provides a PostgREST-style query builder for GORM with pagination, sorting, filtering, facets, and eager-loading support.
Package types provides common database types and models used across database operations.
Package types provides common database types and models used across database operations.

Jump to

Keyboard shortcuts

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