gokart

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 7 Imported by: 0

README

GoKart Logo

GoKart

Opinionated Go service toolkit. Thin wrappers around battle-tested packages that hand you the real types back — no lock-in, no hidden runtime.

Note: Not affiliated with Praetorian's GoKart (static security scanner). This GoKart is a service/CLI toolkit.

Install

go install github.com/dotcommander/gokart/cmd/gokart@latest

60 Seconds to a Working Project

$ gokart new myapi --postgres

  Created myapi/
  ✓ go mod init
  ✓ go get dependencies
  ✓ CLAUDE.md written

$ tree myapi/
myapi/
├── cmd/
│   └── main.go
├── internal/
│   ├── app/
│   │   └── context.go
│   └── commands/
│       ├── root.go
│       └── greet.go
├── go.mod
├── .gokart-manifest.json
├── CLAUDE.md
└── README.md

$ cd myapi && go run ./cmd
myapi 0.0.0

Add integrations later without re-scaffolding:

gokart add sqlite
gokart add ai
gokart add postgres --dry-run   # preview before applying

What You Get Back

GoKart's factory functions return the underlying types directly. You call pgx, chi, and database/sql as if you wrote the setup yourself — because you effectively did.

// postgres — returns *pgxpool.Pool, use pgx directly
pool, err := postgres.Open(ctx, os.Getenv("DATABASE_URL"))
rows, _ := pool.Query(ctx, "SELECT id, name FROM users WHERE active = $1", true)

// sqlite — returns *sql.DB, use database/sql directly
db, err := sqlite.Open("app.db")
db.QueryContext(ctx, "SELECT id FROM sessions WHERE expires_at > ?", time.Now())

// web — returns chi.Router, use chi directly
router := web.NewRouter(web.RouterConfig{Middleware: web.StandardMiddleware})
router.Get("/health", func(w http.ResponseWriter, r *http.Request) { ... })
router.Use(middleware.RealIP)

// cache — returns *redis.Client, use go-redis directly
client, err := cache.Open(ctx, "localhost:6379")
client.Set(ctx, "key", value, 5*time.Minute)

// config — typed, generics, auto env binding (DB_HOST → db.host)
cfg, err := gokart.LoadConfig[AppConfig]("config.yaml")

No wrapper types. No .Unwrap(). If GoKart's defaults don't fit, reach past them.

Packages

Package You get Wraps
gokart typed config, state persistence, logger aliases viper, slog
gokart/cli *cli.App, styled output, tables, spinners, editor bridge cobra, lipgloss
gokart/web chi.Router, graceful server, response helpers, templ, CSRF, pagination, health checks, rate limiting, auth middleware chi/v5, a-h/templ, validator/v10
gokart/postgres *pgxpool.Pool, transaction helper pgx/v5
gokart/sqlite *sql.DB, WAL mode, transaction helper modernc.org/sqlite
gokart/migrate schema migrations, embedded FS support goose/v3
gokart/cache *redis.Client, Remember pattern, distributed locks, data structures (hash, sorted set, set, list) go-redis/v9
gokart/fs atomic writes, config dir, read-or-create stdlib only
gokart/ai *openai.Client factory openai-go v3
gokart/logger JSON/text slog, file logger for TUI tools log/slog

Import only what you need — each is a separate Go module.

The Scaffolder

gokart new generates a structured CLI project wired to your chosen integrations:

gokart new mycli                    # Structured, global config (~/.config/mycli/)
gokart new mycli --local            # Structured, no global config
gokart new mycli --flat             # Single main.go
gokart new mycli --sqlite           # With SQLite wiring
gokart new mycli --postgres         # With PostgreSQL wiring
gokart new mycli --ai               # With OpenAI client
gokart new mycli --redis            # With Redis cache
gokart new mycli --postgres --ai    # Combined

gokart add surgically adds integrations to an existing project. It re-renders only the affected files (internal/app/context.go, internal/commands/root.go), runs go get, and updates the manifest.

gokart add sqlite
gokart add ai --force       # overwrite modified files
gokart add redis
gokart add postgres --dry-run

Philosophy

  • Modular — import only what you need, no forced dependencies.
  • Thin wrappers — factory functions, sensible defaults, real types returned.
  • Fight for inclusion — if stdlib or the underlying package already solves it, GoKart stays out of the way.
  • Web looks big, isn'tgokart/web lists many features but it's 17 small files (~1300 lines total), each ≤144 lines, each returning standard types. It's a toolkit of HTTP helpers sharing an import path, not a framework.

Examples

See examples/ for complete working projects:

  • http-service/ — Minimal HTTP API with chi router
  • cli-app/ — CLI with commands, tables, and spinners

Compatibility

Minimum Go version: 1.22+ (1.26+ recommended)

API stability: Library packages (gokart, gokart/cli, etc.) follow semver. Generator templates may evolve between minor versions — generated code is yours to modify.

License

MIT

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

Root module (github.com/dotcommander/gokart):

  • Config: viper wrapper for config files + env vars
  • State: JSON state persistence for CLI tools
  • Server: HTTP server with graceful shutdown

Submodules:

  • gokart/logger: slog wrapper with JSON/text formatting
  • gokart/cli: CLI framework wrapping cobra + lipgloss
  • gokart/web: HTTP router, client, templ helpers, validator
  • gokart/postgres: pgx/v5 connection pool
  • gokart/sqlite: modernc.org/sqlite (zero CGO) with WAL mode
  • gokart/cache: go-redis/v9 with convenience methods
  • gokart/migrate: goose/v3 schema migrations
  • gokart/ai: OpenAI client factory functions

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 := logger.New(logger.Config{Level: "info", Format: "json"})

// Config
cfg, err := gokart.LoadConfig[AppConfig]("config.yaml")

// Router (gokart/web)
router := web.NewRouter(web.RouterConfig{Middleware: web.StandardMiddleware})

// PostgreSQL (gokart/postgres)
pool, err := postgres.Open(ctx, "postgres://localhost/mydb")

// Cache (gokart/cache)
c, err := cache.Open(ctx, "localhost:6379")

// Migrations (gokart/migrate)
migrate.Postgres(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

Constants

This section is empty.

Variables

This section is empty.

Functions

func LoadConfig

func LoadConfig[T any](paths ...string) (T, error)

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

func LoadConfigWithDefaults[T any](defaults T, paths ...string) (T, error)

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

func LoadState[T any](appName, filename string) (T, error)

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 SaveState

func SaveState[T any](appName, filename string, data T) error

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

func StatePath(appName, filename string) string

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)

Types

This section is empty.

Directories

Path Synopsis
cache module
cli module
cmd
gokart command
internal
logger module
migrate module
postgres module
sqlite module
web module

Jump to

Keyboard shortcuts

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