Documentation
¶
Overview ¶
Package gokart provides thin wrappers around best-in-class Go packages with sensible defaults.
GoKart is an opinionated service toolkit. Every component must justify its existence - we wrap battle-tested packages, not reinvent them.
Components ¶
- Logger: slog wrapper with JSON/text formatting
- Config: viper wrapper for config files + env vars
- Router: chi wrapper with standard middleware
- HTTP Client: retryablehttp wrapper with automatic retries
- Validator: go-playground/validator with JSON field names
- PostgreSQL: pgx/v5 connection pool
- SQLite: modernc.org/sqlite (zero CGO) with WAL mode
- Templates: a-h/templ HTTP integration helpers
- Cache: go-redis/v9 with convenience methods
- Migrations: goose/v3 schema migrations
Design Principles ¶
- Thin wrappers: No business logic, just factory functions
- Sensible defaults: Zero-config works for development and production
- Best-in-class: Wrap proven packages, don't reinvent
- Fight for inclusion: stdlib-sufficient things stay in stdlib
Quick Start ¶
// Logger
log := gokart.NewLogger(gokart.LogConfig{Level: "info", Format: "json"})
// Config
cfg, err := gokart.LoadConfig[AppConfig]("config.yaml")
// Router
router := gokart.NewRouter(gokart.RouterConfig{Middleware: gokart.StandardMiddleware})
// PostgreSQL
pool, err := gokart.OpenPostgres(ctx, "postgres://localhost/mydb")
// Cache
cache, err := gokart.OpenCache(ctx, "localhost:6379")
// Migrations
gokart.PostgresMigrate(ctx, db, "migrations")
CLI Subpackage ¶
For CLI applications, use the gokart/cli subpackage which wraps cobra and lipgloss:
import "github.com/dotcommander/gokart/cli"
app := cli.NewApp("myapp", "1.0.0").
WithDescription("My application").
WithStandardFlags()
app.Run()
Not Included ¶
GoKart intentionally excludes things where stdlib is sufficient:
- Error handling: use errors.Is/As and fmt.Errorf("%w", err)
- File operations: use os, io, filepath
- String manipulation: use strings
- Environment variables: viper.AutomaticEnv() handles this
Domain-specific packages (AI/ML, document processing) belong in separate modules.
Index ¶
- Variables
- func IsNil(err error) bool
- func LoadConfig[T any](paths ...string) (T, error)
- func LoadConfigWithDefaults[T any](defaults T, paths ...string) (T, error)
- func LoadState[T any](appName, filename string) (T, error)
- func LogPath(appName string) string
- func Migrate(ctx context.Context, db *sql.DB, cfg MigrateConfig) error
- func MigrateCreate(dir, name, migrationType string) error
- func MigrateDown(ctx context.Context, db *sql.DB, cfg MigrateConfig) error
- func MigrateDownTo(ctx context.Context, db *sql.DB, cfg MigrateConfig, version int64) error
- func MigrateReset(ctx context.Context, db *sql.DB, cfg MigrateConfig) error
- func MigrateStatus(ctx context.Context, db *sql.DB, cfg MigrateConfig) error
- func MigrateUp(ctx context.Context, db *sql.DB, cfg MigrateConfig) error
- func MigrateVersion(ctx context.Context, db *sql.DB, cfg MigrateConfig) (int64, error)
- func NewFileLogger(appName string) (*slog.Logger, func(), error)
- func NewHTTPClient(cfg HTTPConfig) *retryablehttp.Client
- func NewLogger(cfg LogConfig) *slog.Logger
- func NewRouter(cfg RouterConfig) chi.Router
- func NewStandardClient() *http.Client
- func NewStandardValidator() *validator.Validate
- func NewValidator(cfg ValidatorConfig) *validator.Validate
- func OpenPostgres(ctx context.Context, url string) (*pgxpool.Pool, error)
- func OpenPostgresWithConfig(ctx context.Context, cfg PostgresConfig) (*pgxpool.Pool, error)
- func OpenSQLite(path string) (*sql.DB, error)
- func OpenSQLiteContext(ctx context.Context, path string) (*sql.DB, error)
- func OpenSQLiteWithConfig(ctx context.Context, cfg SQLiteConfig) (*sql.DB, error)
- func PostgresFromEnv(ctx context.Context) (*pgxpool.Pool, error)
- func PostgresMigrate(ctx context.Context, db *sql.DB, dir string) error
- func Render(w http.ResponseWriter, r *http.Request, component templ.Component) error
- func RenderCtx(ctx context.Context, w http.ResponseWriter, component templ.Component) error
- func RenderWithStatus(w http.ResponseWriter, r *http.Request, status int, component templ.Component) error
- func SQLiteInMemory() (*sql.DB, error)
- func SQLiteMigrate(ctx context.Context, db *sql.DB, dir string) error
- func SQLiteTransaction(ctx context.Context, db *sql.DB, fn func(tx *sql.Tx) error) error
- func SaveState[T any](appName, filename string, data T) error
- func StatePath(appName, filename string) string
- func TemplHandler(component templ.Component) http.Handler
- func TemplHandlerFunc(fn func(r *http.Request) templ.Component) http.HandlerFunc
- func TemplHandlerFuncE(fn func(r *http.Request) (templ.Component, error)) http.HandlerFunc
- func ValidationErrors(err error) map[string]string
- func WithTransaction(ctx context.Context, pool *pgxpool.Pool, fn func(tx pgx.Tx) error) error
- type Cache
- func (c *Cache) Client() *redis.Client
- func (c *Cache) Close() error
- func (c *Cache) Delete(ctx context.Context, keys ...string) error
- func (c *Cache) Exists(ctx context.Context, key string) (bool, error)
- func (c *Cache) Expire(ctx context.Context, key string, ttl time.Duration) error
- func (c *Cache) Get(ctx context.Context, key string) (string, error)
- func (c *Cache) GetJSON(ctx context.Context, key string, dest interface{}) error
- func (c *Cache) Incr(ctx context.Context, key string) (int64, error)
- func (c *Cache) IncrBy(ctx context.Context, key string, value int64) (int64, error)
- func (c *Cache) Remember(ctx context.Context, key string, ttl time.Duration, ...) (string, error)
- func (c *Cache) RememberJSON(ctx context.Context, key string, ttl time.Duration, dest interface{}, ...) error
- func (c *Cache) Set(ctx context.Context, key string, value string, ttl time.Duration) error
- func (c *Cache) SetJSON(ctx context.Context, key string, value interface{}, ttl time.Duration) error
- func (c *Cache) SetNX(ctx context.Context, key string, value string, ttl time.Duration) (bool, error)
- func (c *Cache) TTL(ctx context.Context, key string) (time.Duration, error)
- type CacheConfig
- type HTTPConfig
- type LogConfig
- type MigrateConfig
- type PostgresConfig
- type RouterConfig
- type SQLiteConfig
- type ValidatorConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var StandardMiddleware = []func(http.Handler) http.Handler{ middleware.RequestID, middleware.RealIP, middleware.Logger, middleware.Recoverer, }
StandardMiddleware provides production-ready middleware stack:
- RequestID: Injects request ID for tracing
- RealIP: Extracts real client IP from proxies
- Logger: Structured request/response logging
- Recoverer: Panic recovery
Functions ¶
func LoadConfig ¶
LoadConfig loads configuration from the first available file path into type T.
Features:
- Supports multiple config paths (first found wins)
- Automatic environment variable binding
- DOT to UNDERSCORE env key mapping (e.g., db.host → DB_HOST)
Supported formats: JSON, YAML, TOML, HCL, envfile, Java properties
Example:
type Config struct {
DB struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
} `mapstructure:"db"`
}
cfg, err := gokart.LoadConfig[Config]("config.yaml", "config.json")
func LoadConfigWithDefaults ¶
LoadConfigWithDefaults loads configuration with default values pre-populated.
The defaults parameter provides fallback values that will be overridden by values from config files or environment variables.
Example:
defaults := Config{
DB: struct{Host string; Port int}{
Host: "localhost",
Port: 5432,
},
}
cfg, err := gokart.LoadConfigWithDefaults(defaults, "config.yaml")
func LoadState ¶
LoadState loads typed state from ~/.config/{appName}/{filename}.
Returns zero value and os.ErrNotExist if the file doesn't exist. This allows callers to distinguish between missing file and parse errors.
Example:
state, err := gokart.LoadState[AppState]("myapp", "state.json")
if errors.Is(err, os.ErrNotExist) {
// First run, use defaults
state = AppState{WindowSize: 800}
} else if err != nil {
return err
}
func LogPath ¶
LogPath returns the path where file logs are written. Deprecated: Use logger.Path directly.
func Migrate ¶
Migrate runs all pending migrations.
Example with file-based migrations:
db, _ := gokart.OpenPostgres(ctx, url)
err := gokart.Migrate(ctx, db.Config().ConnConfig.Database, gokart.MigrateConfig{
Dir: "migrations",
Dialect: "postgres",
})
Example with embedded migrations:
//go:embed migrations/*.sql
var migrations embed.FS
err := gokart.Migrate(ctx, db, gokart.MigrateConfig{
FS: migrations,
Dir: "migrations",
Dialect: "postgres",
})
func MigrateCreate ¶
MigrateCreate creates a new migration file.
Example:
err := gokart.MigrateCreate("migrations", "add_users_table", "sql")
func MigrateDown ¶
MigrateDown rolls back the last migration.
func MigrateDownTo ¶
MigrateDownTo rolls back to a specific version.
func MigrateReset ¶
MigrateReset rolls back all migrations.
func MigrateStatus ¶
MigrateStatus prints the status of all migrations.
func MigrateVersion ¶
MigrateVersion returns the current migration version.
func NewFileLogger ¶
NewFileLogger creates a logger that writes to a temp file. Deprecated: Use logger.NewFile directly.
func NewHTTPClient ¶
func NewHTTPClient(cfg HTTPConfig) *retryablehttp.Client
NewHTTPClient creates a retryable HTTP client with exponential backoff.
Default configuration:
- Timeout: 30s
- RetryMax: 3 attempts
- RetryWait: 1s base delay
The client automatically retries on network errors and 5xx responses.
Example:
client := gokart.NewHTTPClient(gokart.HTTPConfig{
Timeout: 10 * time.Second,
RetryMax: 5,
RetryWait: 2 * time.Second,
})
resp, err := client.Get("https://api.example.com/data")
Example ¶
package main
import (
"time"
"github.com/dotcommander/gokart"
)
func main() {
client := gokart.NewHTTPClient(gokart.HTTPConfig{
Timeout: 10 * time.Second,
RetryMax: 3,
RetryWait: 1 * time.Second,
})
_ = client // Use client.Get(), client.Post(), etc.
}
Output:
func NewLogger ¶
NewLogger creates a new structured logger with sensible defaults. Deprecated: Use logger.New directly.
Example ¶
package main
import (
"github.com/dotcommander/gokart"
)
func main() {
log := gokart.NewLogger(gokart.LogConfig{
Level: "info",
Format: "json",
})
log.Info("server started", "port", 8080)
}
Output:
func NewRouter ¶
func NewRouter(cfg RouterConfig) chi.Router
NewRouter creates a new chi router with configured middleware.
Example:
router := gokart.NewRouter(gokart.RouterConfig{
Middleware: gokart.StandardMiddleware,
Timeout: 30 * time.Second,
})
router.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
http.ListenAndServe(":8080", router)
Example ¶
package main
import (
"net/http"
"time"
"github.com/dotcommander/gokart"
)
func main() {
router := gokart.NewRouter(gokart.RouterConfig{
Middleware: gokart.StandardMiddleware,
Timeout: 30 * time.Second,
})
router.Get("/api/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// http.ListenAndServe(":8080", router)
}
Output:
func NewStandardClient ¶
NewStandardClient creates a standard http.Client with retry logic.
This is a convenience wrapper around NewHTTPClient that returns a standard library http.Client interface for drop-in compatibility.
Uses default configuration:
- Timeout: 30s
- RetryMax: 3 attempts
- RetryWait: 1s base delay
Example:
client := gokart.NewStandardClient()
resp, err := client.Get("https://api.example.com/data")
func NewStandardValidator ¶
NewStandardValidator creates a validator with default settings.
Convenience wrapper around NewValidator with zero configuration.
Example:
v := gokart.NewStandardValidator() err := v.Struct(myStruct)
func NewValidator ¶
func NewValidator(cfg ValidatorConfig) *validator.Validate
NewValidator creates a configured validator instance.
Default configuration:
- Uses JSON tag names for field identification
- Registers common custom validators (notblank)
Example:
v := gokart.NewValidator(gokart.ValidatorConfig{})
type User struct {
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=0,lte=130"`
}
if err := v.Struct(user); err != nil {
// handle validation errors
}
func OpenPostgres ¶
OpenPostgres opens a PostgreSQL connection pool with default settings. Deprecated: Use postgres.Open directly.
func OpenPostgresWithConfig ¶
OpenPostgresWithConfig opens a PostgreSQL connection pool with custom settings. Deprecated: Use postgres.OpenWithConfig directly.
func OpenSQLite ¶
OpenSQLite opens a SQLite database with default settings. Deprecated: Use sqlite.Open directly.
func OpenSQLiteContext ¶
OpenSQLiteContext opens a SQLite database with context. Deprecated: Use sqlite.OpenContext directly.
func OpenSQLiteWithConfig ¶
OpenSQLiteWithConfig opens a SQLite database with custom settings. Deprecated: Use sqlite.OpenWithConfig directly.
func PostgresFromEnv ¶
PostgresFromEnv opens a PostgreSQL pool using DATABASE_URL environment variable. Deprecated: Use postgres.FromEnv directly.
func PostgresMigrate ¶
PostgresMigrate is a convenience function for PostgreSQL migrations.
Example:
pool, _ := gokart.OpenPostgres(ctx, url) db := stdlib.OpenDBFromPool(pool) err := gokart.PostgresMigrate(ctx, db, "migrations")
func Render ¶
Render renders a templ component to an http.ResponseWriter.
Sets Content-Type to text/html and handles errors.
Example:
func handleHome(w http.ResponseWriter, r *http.Request) {
gokart.Render(w, r, views.HomePage("Welcome"))
}
func RenderCtx ¶
RenderCtx renders a templ component with a custom context.
Example:
ctx := context.WithValue(r.Context(), "user", currentUser) gokart.RenderCtx(ctx, w, views.Dashboard(data))
func RenderWithStatus ¶
func RenderWithStatus(w http.ResponseWriter, r *http.Request, status int, component templ.Component) error
RenderWithStatus renders a templ component with a custom status code.
Example:
func handleNotFound(w http.ResponseWriter, r *http.Request) {
gokart.RenderWithStatus(w, r, http.StatusNotFound, views.NotFoundPage())
}
func SQLiteInMemory ¶
SQLiteInMemory creates an in-memory SQLite database for testing. Deprecated: Use sqlite.InMemory directly.
func SQLiteMigrate ¶
SQLiteMigrate is a convenience function for SQLite migrations.
Example:
db, _ := gokart.OpenSQLite("app.db")
err := gokart.SQLiteMigrate(ctx, db, "migrations")
func SQLiteTransaction ¶
SQLiteTransaction executes a function within a SQLite transaction. Deprecated: Use sqlite.Transaction directly.
func SaveState ¶
SaveState saves typed state to ~/.config/{appName}/{filename}.
The file is written as indented JSON for human readability. Directory is created with 0755, files with 0644 permissions.
Example:
type AppState struct {
LastOpened string `json:"last_opened"`
WindowSize int `json:"window_size"`
}
err := gokart.SaveState("myapp", "state.json", AppState{
LastOpened: "/path/to/file",
WindowSize: 1024,
})
func StatePath ¶
StatePath returns the full path to a state file.
Returns empty string if the user config directory cannot be determined.
Example:
path := gokart.StatePath("myapp", "state.json")
// Returns: /Users/username/.config/myapp/state.json (on macOS)
func TemplHandler ¶
TemplHandler creates an http.Handler from a templ component.
Useful for static pages or when you don't need request data.
Example:
router.Get("/about", gokart.TemplHandler(views.AboutPage()))
func TemplHandlerFunc ¶
TemplHandlerFunc creates an http.HandlerFunc from a function that returns a component.
Useful when the component needs data from the request.
Example:
router.Get("/user/{id}", gokart.TemplHandlerFunc(func(r *http.Request) templ.Component {
id := chi.URLParam(r, "id")
user := getUser(id)
return views.UserPage(user)
}))
func TemplHandlerFuncE ¶
TemplHandlerFuncE creates an http.HandlerFunc from a function that can return an error.
Example:
router.Get("/dashboard", gokart.TemplHandlerFuncE(func(r *http.Request) (templ.Component, error) {
data, err := loadDashboardData(r.Context())
if err != nil {
return nil, err
}
return views.Dashboard(data), nil
}))
func ValidationErrors ¶
ValidationErrors extracts field-level errors from a validation error. Returns nil if err is not a validator.ValidationErrors.
Example:
if err := v.Struct(user); err != nil {
for field, msg := range gokart.ValidationErrors(err) {
fmt.Printf("%s: %s\n", field, msg)
}
}
Types ¶
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache wraps Redis client with convenience methods.
func OpenCache ¶
OpenCache opens a Redis connection with default settings.
Example:
cache, err := gokart.OpenCache(ctx, "localhost:6379")
if err != nil {
log.Fatal(err)
}
defer cache.Close()
func OpenCacheURL ¶
OpenCacheURL opens a Redis connection using a URL.
Example:
cache, err := gokart.OpenCacheURL(ctx, "redis://:password@localhost:6379/0")
func OpenCacheWithConfig ¶
func OpenCacheWithConfig(ctx context.Context, cfg CacheConfig) (*Cache, error)
OpenCacheWithConfig opens a Redis connection with custom settings.
Example:
cache, err := gokart.OpenCacheWithConfig(ctx, gokart.CacheConfig{
Addr: "localhost:6379",
Password: "secret",
KeyPrefix: "myapp:",
})
func (*Cache) Remember ¶
func (c *Cache) Remember(ctx context.Context, key string, ttl time.Duration, fn func() (interface{}, error)) (string, error)
Remember gets a value or sets it using the provided function.
Example:
user, err := cache.Remember(ctx, "user:123", time.Hour, func() (interface{}, error) {
return db.GetUser(ctx, 123)
})
func (*Cache) RememberJSON ¶
func (c *Cache) RememberJSON(ctx context.Context, key string, ttl time.Duration, dest interface{}, fn func() (interface{}, error)) error
RememberJSON gets a value or computes and caches it as JSON. Unlike Remember, this preserves type information for GetJSON retrieval.
Example:
var user User
err := cache.RememberJSON(ctx, "user:123", time.Hour, &user, func() (interface{}, error) {
return db.GetUser(ctx, 123)
})
func (*Cache) SetJSON ¶
func (c *Cache) SetJSON(ctx context.Context, key string, value interface{}, ttl time.Duration) error
SetJSON marshals and stores a value as JSON.
type CacheConfig ¶
type CacheConfig struct {
// URL is the Redis connection string.
// Format: redis://:password@host:port/db or redis://host:port
URL string
// Addr is the Redis server address (alternative to URL).
// Default: localhost:6379
Addr string
// Password for Redis authentication.
Password string
// DB is the Redis database number.
// Default: 0
DB int
// PoolSize is the maximum number of connections.
// Default: 10
PoolSize int
// MinIdleConns is the minimum number of idle connections.
// Default: 2
MinIdleConns int
// DialTimeout is the timeout for establishing new connections.
// Default: 5 seconds
DialTimeout time.Duration
// ReadTimeout is the timeout for socket reads.
// Default: 3 seconds
ReadTimeout time.Duration
// WriteTimeout is the timeout for socket writes.
// Default: 3 seconds
WriteTimeout time.Duration
// KeyPrefix is prepended to all keys.
KeyPrefix string
}
CacheConfig configures Redis connection.
func DefaultCacheConfig ¶
func DefaultCacheConfig() CacheConfig
DefaultCacheConfig returns production-ready defaults.
type HTTPConfig ¶
type HTTPConfig struct {
Timeout time.Duration // request timeout (default: 30s)
RetryMax int // max retry attempts (default: 3)
RetryWait time.Duration // wait between retries (default: 1s)
}
HTTPConfig configures HTTP client behavior.
type MigrateConfig ¶
type MigrateConfig struct {
// Dir is the directory containing migration files.
// Default: "migrations"
Dir string
// Table is the name of the migrations tracking table.
// Default: "goose_db_version"
Table string
// Dialect is the database dialect (postgres, sqlite3, mysql).
// Auto-detected if not specified.
Dialect string
// FS is an optional filesystem for embedded migrations.
FS fs.FS
// AllowMissing allows applying missing (out-of-order) migrations.
// Default: false
AllowMissing bool
// NoVersioning disables version tracking (for one-off scripts).
// Default: false
NoVersioning bool
}
MigrateConfig configures database migrations.
func DefaultMigrateConfig ¶
func DefaultMigrateConfig() MigrateConfig
DefaultMigrateConfig returns sensible defaults.
type PostgresConfig ¶
PostgresConfig is an alias for postgres.Config. Deprecated: Use postgres.Config directly.
func DefaultPostgresConfig ¶
func DefaultPostgresConfig(url string) PostgresConfig
DefaultPostgresConfig returns production-ready defaults. Deprecated: Use postgres.DefaultConfig directly.
type RouterConfig ¶
type RouterConfig struct {
Middleware []func(http.Handler) http.Handler
Timeout time.Duration // request timeout (default: none)
}
RouterConfig configures HTTP router behavior.
type SQLiteConfig ¶
SQLiteConfig is an alias for sqlite.Config. Deprecated: Use sqlite.Config directly.
func DefaultSQLiteConfig ¶
func DefaultSQLiteConfig(path string) SQLiteConfig
DefaultSQLiteConfig returns production-ready defaults. Deprecated: Use sqlite.DefaultConfig directly.
type ValidatorConfig ¶
type ValidatorConfig struct {
// UseJSONNames uses json tag names in error messages instead of struct field names.
// Default: true (more useful for API error responses)
UseJSONNames bool
}
ValidatorConfig configures validation behavior.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cache
module
|
|
|
Package cli provides CLI application utilities wrapping Cobra and Lipgloss.
|
Package cli provides CLI application utilities wrapping Cobra and Lipgloss. |
|
cmd
|
|
|
gokart
command
|
|
|
Package logger provides structured logging utilities wrapping log/slog.
|
Package logger provides structured logging utilities wrapping log/slog. |
|
migrate
module
|
|
|
Package postgres provides PostgreSQL utilities wrapping pgx/v5.
|
Package postgres provides PostgreSQL utilities wrapping pgx/v5. |
|
Package sqlite provides SQLite database utilities wrapping modernc.org/sqlite.
|
Package sqlite provides SQLite database utilities wrapping modernc.org/sqlite. |
|
web
module
|