Gofasta: Code Generation and Composable Packages for Go Backend Services
Technical Whitepaper
Version 4.0.0 — April 2026
Published by the Gofasta Authors — MIT License
Abstract
Go is an excellent language for building backend services, but starting a new production project still involves significant repetitive setup: wiring routers, configuring databases, setting up authentication, writing CRUD boilerplate, organizing project structure, and preparing deployment manifests. Each team reinvents these patterns independently, leading to inconsistency and wasted effort.
Gofasta is an open-source toolkit that eliminates this setup overhead. It consists of two components: a CLI tool (gofastadev/cli) that scaffolds projects and generates idiomatic Go code, and a library (gofastadev/gofasta) of production-ready packages covering common backend concerns. Generated code is plain Go — no custom syntax, no transpilation, no runtime magic. Developers own every line of output and can modify or replace any component independently.
Gofasta is also agent-native by default. Every CLI command emits structured JSON output, every error carries a stable machine-readable code and a remediation hint, every resource is inspectable as structured data, and every scaffolded project ships with the configuration files AI coding agents read at startup. The design goal is that an AI agent working on a Gofasta project should be measurably faster and more accurate than one working on a hand-rolled Go project — not as an afterthought but as a first-class constraint on every feature.
This paper describes the technical architecture, design principles, agent-ergonomics model, and feature set of Gofasta, and explains how it fits into the Go ecosystem.
Table of Contents
- Introduction — setup tax, what Gofasta is and is not, curated defaults
- Design Principles — including §2.7 Agent-Native by Default
- Architecture
- The CLI Tool
- The Gofasta Library
- Code Generation Strategy
- Dependency Injection
- Database Layer
- HTTP and API Layer
- Authentication and Authorization
- Security Defaults
- Background Processing
- Real-Time Communication
- Email and Notifications
- File Storage
- Internationalization
- Feature Flags
- Observability
- Resilience
- Testing
- Deployment
- Adoption and Migration Path
- Comparison with Existing Tools
- Roadmap
- Contributing
1. Introduction
1.1 The Setup Tax
Go’s standard library and ecosystem provide excellent building blocks for backend services. The language is fast, memory-efficient, compiles to a single binary, and handles concurrency natively. These properties make it a strong technical choice for backend services at any scale — from a solo developer’s first SaaS to a large organization’s internal platform.
However, assembling these building blocks into a production-ready application imposes a cost that is largely independent of the application’s business logic. Before writing the first line of domain code, a developer must:
- Choose and integrate a set of libraries. Go does not ship a canonical answer for HTTP routing, database access, authentication, caching, email, background jobs, or configuration management. Each concern requires evaluating competing libraries, reading their documentation, and writing integration code. For a typical backend, this involves 15–25 separate library selections.
- Design a project layout. Go does not prescribe a project structure. Teams must decide where to put models, handlers, services, repositories, DTOs, and configuration — and these decisions vary across projects within the same organization.
- Write structural wiring. Connecting routers, middleware, database connections, authentication, logging, and health checks requires hundreds of lines of setup code that looks nearly identical across projects. This code is not business logic — it is infrastructure glue.
- Produce CRUD boilerplate. For each resource, developers write a model, migration, repository, service, DTO, controller, and route registration file. The pattern is the same every time; only the field names change.
- Prepare deployment artifacts. Dockerfiles, systemd unit files, CI/CD workflows, and reverse proxy configurations are needed before the first deploy, and they follow predictable patterns.
This overhead is measurable. A production-ready Go backend — with structured logging, JWT authentication, role-based access control, database migrations, input validation, health checks, Swagger documentation, Docker configuration, and CI/CD workflows — typically requires 2,000–4,000 lines of non-business-logic code and 40–80 files before the first domain endpoint can be implemented. For an experienced Go developer, this represents days of work. For a developer new to Go, it can represent weeks.
This is not a deficiency of the language. Go’s explicitness — the property that every behavior is visible in source code, that there is no hidden control flow, no auto-discovery, no runtime magic — is precisely why engineers choose it. The setup cost is the price of a codebase where every line is readable and every dependency is traceable. That price is worth paying. The question is whether each team needs to pay it independently, or whether the structurally identical portions can be generated once and then owned by the developer.
1.2 What Gofasta Is
Gofasta is two things, plus an audience commitment:
-
A CLI tool (
github.com/gofastadev/cli) installed globally viago install github.com/gofastadev/cli/cmd/gofasta@latest. It creates new projects, generates resource code (models, services, controllers, migrations), runs migrations, manages seeds, regenerates dependency injection wiring, and provides introspection + verification commands that serve humans and AI coding agents equally well. It does not import the gofasta library; it only manipulates files on disk. -
A Go library (
github.com/gofastadev/gofasta) containing multiple packages underpkg/. These packages provide configuration loading, structured logging, error handling, HTTP utilities, middleware, authentication, caching, storage, email, notifications, WebSockets, scheduling, queues, resilience patterns, validation, internationalization, observability, feature flags, encryption, health checks, and test utilities. Each package is independently importable and has no side effects. -
A toolkit designed to be driven by AI coding agents as first-class users. Every scaffolded project ships with an
AGENTS.mdbriefing, a JSON-Schema-aware config helper, a structured-error protocol, and an opt-in installer (gofasta ai <agent>) that wires per-agent configuration for Claude Code, OpenAI Codex, Cursor, Aider, and Windsurf. The documentation site publishes/llms.txtand/llms-full.txtso agents reason about current features rather than stale training data. This is not a bolt-on layer — it is a design constraint that shapes every CLI command and every scaffold default. See §2.7 Agent-Native by Default and §4.5 Agent Ergonomics.
The CLI generates plain Go code — the same code a senior engineer would write by hand — and then gets out of the way. After generation, there is no gofasta runtime, no application wrapper, no hidden lifecycle. The project is standard Go: main.go calls net/http, handlers return errors, models are GORM structs, dependency injection is resolved at compile time by Google Wire. A developer can read every line top to bottom and know exactly what runs. An AI agent can introspect every line structurally (gofasta inspect, gofasta routes --json), verify every change holistically (gofasta verify), and detect drift before committing (gofasta status).
1.3 What Gofasta Is Not
- Not a custom language. All generated code is standard Go. There is no custom syntax, no transpiler, no preprocessor, and no special file extensions.
- Not a runtime wrapper. There is no global application object, no lifecycle hooks, and no inversion-of-control container at runtime. Dependency wiring happens at compile time via Google Wire. Gofasta is a toolkit — a CLI plus a library of independent packages — not a runtime layer your code sits inside.
- Not a lock-in. Every generated file is owned by the developer. The scaffold imports a curated set of
pkg/*packages as its defaults — configuration, logging, HTTP utilities, authentication, and so on — the same way a typical Go backend imports a curated set of community libraries. These are defaults, not requirements. Eachpkg/*package is independently importable, so a developer who prefers a different library for any single concern (a different config loader, a different cache, a different JWT implementation) can delete the default import,go gettheir preferred alternative, and swap it in without touching the rest of the project. “Not a lock-in” means any package can be replaced — not that no package is used. - Not an all-or-nothing commitment. A project can use
pkg/cachewithoutpkg/mailer, orpkg/resiliencewithoutpkg/queue. There is no umbrella import that drags everything in. The CLI also generates code once and does not manage or update generated code after the fact — once a project is scaffolded, the CLI is optional. - Not an AI runtime library. While Gofasta is agent-native at the development-tooling level (the CLI, the scaffold, and the docs are designed for AI agents to drive), it does not include runtime LLM SDKs, prompt-engineering abstractions, vector-database wrappers, or agent-orchestration frameworks. Applications that need to call LLMs at runtime import a provider SDK (
anthropic-sdk-go,openai-go, the Google Generative AI SDK) directly, the same way they’d import any other third-party library. Agent-native refers to the development experience of building Gofasta apps, not to the runtime capabilities of the apps themselves.
1.4 Curated Defaults, Not Lock-In
A scaffolded project imports packages from gofastadev/gofasta/pkg/* — configuration loading, structured logging, JWT authentication, caching, and so on. These are standard Go library imports, not framework bindings. Each package is independently importable, has no side effects, and does not reference any other pkg/* package unless functionally necessary (for example, pkg/health accepts a *gorm.DB and a cache.CacheService to check their connectivity).
The relationship between a project and pkg/cache is identical to the relationship between a project and redis/go-redis — it is a dependency the developer chose, and it can be removed with a single import deletion. No other file in the project needs to change.
The toolkit provides these packages as curated defaults: a tested, documented set of libraries that work well together and cover the concerns most backends share. A developer who prefers a different JWT library, a different cache client, or a different email provider can swap any individual package without touching the rest. The value is not lock-in. The value is that someone already made the selection, wrote the tests, and documented the integration — so each team does not have to.
2. Design Principles
2.1 Explicit Over Implicit
All behavior in a Gofasta project is visible in the source code. There is no convention-based discovery, no auto-registration, no struct-tag-driven routing, and no runtime reflection for dependency injection. If a route exists, there is a line of code registering it. If a dependency is injected, there is a Wire provider set declaring it. A developer reading the code can trace the full request path without needing to know any toolkit-specific conventions — everything that happens at runtime is a plain Go function call they can follow.
2.2 Generate, Then Own
The CLI generates idiomatic Go code that the developer owns completely. Generated files are committed to version control and can be modified freely. The CLI does not maintain hidden state or require re-running to keep generated code in sync. After generation, the code is yours — modify it, delete it, refactor it. Gofasta gets out of the way.
2.3 Standard Library Compatibility
The pkg/* packages build on Go’s standard interfaces wherever possible. HTTP routing uses chi, which implements http.Handler and works with standard net/http middleware (func(http.Handler) http.Handler). Configuration uses struct tags and environment variables. Logging uses slog from the standard library. Database access uses GORM, which builds on database/sql. None of these choices prevent using the standard library directly, and each is a swappable default — for example, the scaffold’s router can be replaced with gorilla/mux or stdlib http.ServeMux without touching any pkg/* code.
A Gofasta project compiles with go build, tests with go test, lints with golangci-lint, and debugs with Delve. It does not require a custom compiler, a patched Go runtime, or a proprietary build command. Any tool that works with standard Go works with a Gofasta project — including IDE support, CI/CD pipelines, and profiling tools. This is a deliberate design constraint, not a limitation.
2.4 Compile-Time Safety
Gofasta prefers compile-time checks over runtime checks. Dependency injection is resolved at compile time by Google Wire. Type mismatches, missing providers, and circular dependencies are caught by the Go compiler, not at application startup. Generated code uses concrete types and interfaces — not interface{} or reflection.
2.5 Composable Packages
The packages in pkg/ are independently importable. A project can use pkg/cache without pkg/mailer, or pkg/resilience without pkg/queue. There is no umbrella import that pulls in everything — no single gofasta.App type a project has to construct, no meta-package that transitively drags in every other package. Each pkg/* is self-contained, declares its own minimal dependencies, and can be replaced individually.
2.6 No Shortcuts
Gofasta does not optimize for the shortest possible demo. It optimizes for codebases that remain readable and maintainable after six months of active development by a team. Patterns like interface-based repositories, separate DTOs, and explicit route registration add a small amount of initial code but pay dividends in testability, clarity, and flexibility.
2.7 Agent-Native by Default
Gofasta treats AI coding agents (Claude Code, OpenAI Codex, Cursor, Aider, Windsurf, and successors) as first-class users of the CLI and the scaffold — not as an afterthought layered on top of an otherwise human-only tool. The practical consequences:
- Every CLI command that emits structured output honors a global
--jsonflag. The JSON shape is stable API. Agents parse it mechanically; they do not regex-scan English text. - Every CLI error carries a stable machine-readable code, a one-line message, a remediation hint, and a documentation URL. Agents pattern-match on the code (e.g.,
WIRE_MISSING_PROVIDER,HEALTH_CHECK_FAILED), read the hint for the exact fix, and link to the docs when they need deeper context. No error propagation relies on string parsing. - Every scaffolded project ships with
AGENTS.mdat the project root. This file — the emerging universal format adopted by every major AI coding agent — tells agents the directory layout, the command surface, the conventions to follow, the failure modes to avoid (most critically: do not editwire_gen.go), and a pre-commit self-check list. Agents read it automatically at startup. - Every scaffolded project ships with
cmd/schema/main.go— a small helper that emits the JSON Schema forconfig.yamlby reflecting over theAppConfigtype in the version ofpkg/configpinned in the project’sgo.mod. Invoked bygofasta config schema; callable directly asgo run ./cmd/schema. Agents validate proposed config edits before writing them, instead of guessing key names from stale training data. - The documentation site publishes
/llms.txtand/llms-full.txtfollowing the llmstxt.org spec. Agents asking “how does gofasta’spkg/featureflagwork?” fetch one file and read current, version-matched documentation instead of relying on training-set recall that may be months out of date. - The
gofasta ai <agent>command installs per-agent configuration (permission allowlists, pre-commit hooks, project slash commands, conventions files) without cluttering projects that don’t use that particular agent. Opt-in, idempotent, and trivially extensible to new agents as they emerge. - Higher-level commands like
gofasta verify,gofasta status,gofasta inspect <Resource>, andgofasta do <workflow>exist specifically to collapse what would otherwise be multi-round-trip agent workflows into single calls. An agent asked to “add an Invoice resource and confirm the project still compiles” runs two commands (gofasta do new-rest-endpoint Invoice ...+gofasta verify), not seven.
This is not a claim that Gofasta generates better code when an agent writes it — generated code is identical whether a human or an agent typed the command. The claim is about development throughput: an agent working in a Gofasta codebase has structured-output commands, stable error codes, resource introspection, drift detection, and named workflow chains available, all of which reduce the number of wrong turns and tool calls it takes to complete a task correctly. The whitepaper’s §4.5 Agent Ergonomics documents each capability in detail.
The design constraint follows from a simple observation: AI-assisted development is no longer a niche workflow, and Go toolchains that treat it as one will be slower to use for a large and growing fraction of developers. Gofasta builds for this explicitly, the same way Go’s standard library builds for go build + go test + go vet as the assumed tooling surface.
3. Architecture
3.1 Project Structure
A Gofasta project follows a layered architecture with clear separation of concerns:
myapp/
├── app/ # Application code
│ ├── main/main.go # Entry point
│ ├── models/ # GORM database models
│ ├── dtos/ # Request/response data transfer objects
│ ├── repositories/ # Data access layer
│ │ └── interfaces/ # Repository contracts
│ ├── services/ # Business logic layer
│ │ └── interfaces/ # Service contracts
│ ├── rest/
│ │ ├── controllers/ # HTTP handlers
│ │ └── routes/ # Route registration
│ ├── graphql/ # Only with --graphql
│ │ ├── schema/ # .gql schema files
│ │ └── resolvers/ # GraphQL resolvers
│ ├── validators/ # Input validation rules
│ ├── di/ # Dependency injection (Google Wire)
│ │ ├── container.go # Container struct
│ │ ├── wire.go # Wire build configuration
│ │ ├── wire_gen.go # Generated wiring (do not edit)
│ │ └── providers/ # Provider sets per resource
│ └── jobs/ # Background jobs and scheduled tasks
├── cmd/ # CLI commands (Cobra)
│ ├── root.go
│ ├── serve.go # HTTP server startup
│ └── seed.go # Database seeding
├── db/
│ ├── migrations/ # SQL migration pairs (.up.sql / .down.sql)
│ └── seeds/ # Seed data functions
├── configs/ # RBAC policies, feature flag definitions
├── deployments/ # Docker, CI/CD workflows, nginx, systemd
├── templates/emails/ # HTML email templates
├── locales/ # Translation files (i18n)
├── config.yaml # Application configuration
├── compose.yaml # Docker Compose
├── Dockerfile # Production image
├── Makefile # Development shortcuts
└── gqlgen.yml # GraphQL code generation config (only with --graphql)3.2 Request Flow
The request flow is linear and traceable:
HTTP Request
→ Router (chi)
→ Middleware (auth, CORS, logging, rate limiting)
→ Controller (parses request, calls service)
→ Service (business logic, calls repository)
→ Repository (data access via GORM)
→ DatabaseEach layer depends only on the layer below it via interfaces. Controllers never access the database directly. Services never parse HTTP requests. Repositories never contain business logic.
3.3 Optional GraphQL Support
Gofasta projects are REST-first by default. GraphQL support can be added at project creation with gofasta new myapp --graphql, which generates gqlgen schema files, resolvers, and mounts the /graphql endpoint alongside the REST API. Both API styles share the same services and repositories — they differ only in the entry point (controllers vs. resolvers). This avoids logic duplication and ensures consistency between APIs.
When GraphQL is enabled, the project includes:
app/graphql/schema/— GraphQL schema files (.gql)app/graphql/resolvers/— Resolver implementationsgqlgen.yml— gqlgen code generation configuration/graphqland/graphql-playgroundHTTP endpoints
4. The CLI Tool
4.1 Installation
# Option A: Go install (recommended for Go developers)
go install github.com/gofastadev/cli/cmd/gofasta@latest
# Option B: Pre-built binary (no Go toolchain needed)
curl -fsSL https://raw.githubusercontent.com/gofastadev/cli/main/dist/install.sh | shRequirement: Go 1.25.0 or later (Option A only). Option B downloads a self-contained binary.
4.2 Commands
The CLI exposes ~40 commands and subcommands, grouped below by purpose. Every command is invokable as gofasta <command> [flags] and supports --help for detailed usage.
Project lifecycle
| Command | Description |
|---|---|
gofasta new <name> | Bootstrap a new project (~78 files) with full structure, Go module, and starter User resource. Accepts a plain name (myapp) or a full module path (github.com/myorg/myapp). Add --graphql / --gql to include gqlgen schema and resolvers. |
gofasta init | Initialize a freshly cloned project: create .env from .env.example, run go mod tidy, generate Wire DI, generate GraphQL code (if gqlgen.yml exists), run database migrations, and verify the build. |
gofasta doctor | Check system prerequisites (Go, migrate, Docker, air, wire, gqlgen, swag) and project health (config.yaml, database connectivity). Useful for bug reports. |
gofasta upgrade | Self-update to the latest CLI release. Detects whether the binary was installed via go install or a pre-built binary and runs the right upgrade path. |
gofasta version | Print detailed version information including CLI version, Go runtime version, and OS/arch. |
Development workflow
| Command | Description |
|---|---|
gofasta dev | Bring the full local environment up with one command: preflight → start compose services (db, optional cache + queue profiles) → wait for healthchecks → run pending migrations → optionally seed → launch Air for hot reload → teardown on exit. Optional --dashboard starts a local HTML debug panel on :9090 (app health, services, routes with request/response types, metrics, recent requests, recent SQL, and a per-request trace waterfall with span-level call-stack snapshots plus a one-click replay button — all gated behind the devtools build tag so production binaries pay zero cost). Every step accepts --json for structured NDJSON output; every failure surfaces a stable DEV_* error code. Each stage can be opted out individually (--no-services, --no-db, --no-cache, --no-queue, --no-migrate, --no-teardown, --fresh, --dry-run). |
gofasta serve | Start the HTTP server (delegates to the project’s own serve command). |
gofasta console | Launch an interactive Go REPL in the project directory using yaegi . Requires yaegi to be installed separately. |
gofasta routes | Display all registered API routes from app/rest/routes/ in a formatted table (method, path, source file). Structured JSON available via --json. |
gofasta swagger | Generate OpenAPI/Swagger documentation from code annotations via swag init. |
gofasta wire | Regenerate Wire dependency injection code. |
gofasta verify | Run the full preflight gauntlet — gofmt, vet, golangci-lint, tests with the race detector, build, Wire drift, routes sanity. Structured per-check JSON output; non-zero exit on any failure. The single “am I done?” check for humans and AI agents alike. |
gofasta status | Report project drift — Wire-derived code out of sync, Swagger docs stale, pending migrations, uncommitted generated files, go.sum freshness. Complementary to verify — reports artifacts that need regeneration rather than quality gates. |
gofasta inspect <Resource> | AST-parse a resource’s model, DTOs, service interface, controller, and routes; emit a single structured report. Agents planning a modification get the full picture without opening six files. |
gofasta config schema | Emit a JSON Schema (Draft 7) describing config.yaml — feed to VS Code YAML / JetBrains editors for autocomplete, or to CI for validation. Shells out to a project-local helper so the schema always matches the gofasta version pinned in go.mod. |
gofasta do <workflow> | Run a named chain of gofasta commands — new-rest-endpoint (scaffold + migrate + swagger), rebuild (wire + swagger), fresh-start (init + migrate + seed), clean-slate (db reset + seed), health-check (verify + status). Pass --dry-run to preview the chain. |
gofasta ai <agent> | Install project-specific configuration for an AI coding agent — Claude Code, Cursor, OpenAI Codex, Aider, or Windsurf. Opt-in (only AGENTS.md ships by default); idempotent; tracked in .gofasta/ai.json. |
Database
| Command | Description |
|---|---|
gofasta migrate up | Apply all pending SQL migrations. |
gofasta migrate down | Roll back the last migration. |
gofasta seed [--fresh] | Run database seed functions. Use --fresh to drop all tables, re-migrate, then seed. |
gofasta db reset [--skip-seed] | Drop all tables, re-apply migrations, and seed. Use --skip-seed to skip seeding. |
Code generation
The gofasta generate command (shorthand g) scaffolds resources. Every generated file is auto-wired into the DI container, routes, and GraphQL schema where applicable.
| Command | Description |
|---|---|
gofasta g scaffold <Name> [field:type ...] | Generate a complete REST resource: model, migration, repository (+ interface), service (+ interface), DTOs, Wire provider, controller, routes. Add --graphql for GraphQL schema + resolver wiring. |
gofasta g model <Name> [field:type ...] | Generate a GORM model and SQL migration pair. |
gofasta g repository <Name> [field:type ...] | Generate model + migration + repository interface + repository implementation. Aliased gofasta g repo. |
gofasta g service <Name> [field:type ...] | Generate through the service layer: model, repo, service interface + implementation, DTOs, Wire provider. Add --graphql for a resolver too. Aliased gofasta g svc. |
gofasta g controller <Name> [field:type ...] | Generate everything up through the REST controller + routes, auto-wired end-to-end. Add --graphql for GraphQL support. Aliased gofasta g ctrl. |
gofasta g dto <Name> [field:type ...] | Generate request/response DTOs only (standalone). |
gofasta g migration <Name> [field:type ...] | Generate only the .up.sql / .down.sql migration pair. |
gofasta g route <Name> | Generate only the route registration file (assumes a controller already exists). |
gofasta g provider <Name> | Generate a Wire provider and auto-patch the container and wire.go. |
gofasta g resolver <Name> | Patch the GraphQL resolver to add a new service dependency. |
gofasta g job <name> [cron-expr] | Generate a cron-scheduled background job, register it in the scheduler, and add its schedule to config.yaml. Uses 6-field cron syntax (with seconds). |
gofasta g task <name> | Generate an async task handler for the pkg/queue (asynq-backed) queue system. |
gofasta g email-template <name> | Generate an HTML email template in templates/emails/. Aliased gofasta g email. |
Supported field types are string, text, int, float, bool, uuid, and time. SQL types are automatically adapted per database driver (PostgreSQL, MySQL, SQLite, SQL Server, ClickHouse) based on database.driver in config.yaml.
Deployment
All deploy commands read configuration from the deploy: section of config.yaml and support --host, --method (docker or binary), --port, --path, --arch, and --dry-run flags.
| Command | Description |
|---|---|
gofasta deploy | Deploy the application to a remote server via SSH. Defaults to the docker method (transfers a built Docker image); --method binary cross-compiles a Go binary and manages it via systemd. |
gofasta deploy setup | Prepare a fresh VPS for deployment: install system packages, Docker (for docker method) or service user (for binary method), create directory structure, install nginx reverse proxy config. |
gofasta deploy status | Check the status of the deployed application (current release, container/service state). |
gofasta deploy logs | Tail application logs from the remote server (streams docker compose logs -f or journalctl -u <app> -f). |
gofasta deploy rollback | Roll back to the previous release on the remote server. |
Shell integration
| Command | Description |
|---|---|
gofasta completion <shell> | Generate the shell completion script for bash, zsh, fish, or PowerShell. Output is shell code meant to be sourced. |
gofasta help [command] | Show help for any command. Equivalent to gofasta <command> --help. |
Global flags
| Flag | Description |
|---|---|
-h, --help | Show help for the current command. |
-v, --version | Print the CLI version (machine-parseable — no banner). |
--no-banner | Suppress the branded banner. Also honored via GOFASTA_NO_BANNER=1 environment variable. The banner is also suppressed automatically for gofasta completion (to keep shell scripts valid) and --version (to keep output parseable), and when NO_COLOR is set the banner renders without ANSI colors. |
--json | Emit machine-parseable JSON output (and suppress the banner). Intended for AI agents and CI automation. Every structured-output command honors it. |
4.3 Project Creation
gofasta new myappThis command:
- Creates the project directory with the full structure
- Runs
go mod init - Copies ~92 template files (REST-only by default)
- Installs dependencies (
go mod tidy) - Generates Wire dependency injection code
- Includes a starter
Userresource so the project compiles and runs immediately
The command accepts a project name or a full Go module path (e.g., github.com/myorg/myapp). If the argument contains /, it is used as the module path; otherwise the project name is used as both. The database driver defaults to PostgreSQL and is configured in config.yaml after project creation.
Flag: --graphql (or --gql) — adds GraphQL support alongside REST. When passed, the project also includes gqlgen configuration, schema files, resolvers, and the /graphql endpoint. GraphQL is additive — REST controllers and routes are always present regardless of this flag.
gofasta new myapp # REST-only
gofasta new myapp --graphql # REST + GraphQL4.4 Scaffold Generation
gofasta g scaffold Product name:string price:float description:text active:boolThis single command generates 11 new files and patches 4 existing files, producing a complete CRUD resource:
Generated files:
- Database model with UUID primary key, timestamps, and soft delete
- Up and down SQL migrations
- Repository interface and GORM implementation
- Service interface and implementation
- Request/response DTOs with validation tags
- REST controller with Swagger annotations
- Route registration
- Wire provider set
Patched files:
app/di/container.go— Adds service and controller fieldsapp/di/wire.go— Adds provider set to Wire buildapp/rest/routes/index.routes.go— Registers routescmd/serve.go— Wires controller into route configuration
Supported field types:
| Type | Go Type | Postgres | MySQL | SQLite | GraphQL |
|---|---|---|---|---|---|
string | string | VARCHAR(255) | VARCHAR(255) | TEXT | String |
text | string | TEXT | LONGTEXT | TEXT | String |
int | int | INTEGER | INT | INTEGER | Int |
float | float64 | DECIMAL(10,2) | DECIMAL(10,2) | REAL | Float |
bool | bool | BOOLEAN | TINYINT(1) | INTEGER | Boolean |
uuid | uuid.UUID | UUID | CHAR(36) | TEXT | ID |
time | time.Time | TIMESTAMP | DATETIME | DATETIME | DateTime |
SQL types are automatically adapted per database driver.
Every scaffold run auto-verifies the generated code compiles by invoking go build ./... as a final step (skippable with --no-verify). A starter <resource>.controller_test.go is emitted alongside the controller so the project is green on go test out of the box — and any template regression shows up immediately instead of silently producing broken code.
4.5 Agent Ergonomics
Every new project ships first-class integration with AI coding agents — Claude Code, OpenAI Codex, Cursor, Aider, Windsurf, and any MCP-aware successor. The support is layered: universal files ship by default, agent-specific configuration is opt-in, and every CLI command has a machine-parseable mode.
Default artifacts in every scaffolded project:
AGENTS.mdat the project root. The emerging universal format picked up by every modern coding agent. Ships with project overview, every command, conventions to follow, a “things NOT to do” list (most notably don’t editwire_gen.go), a Wire walkthrough covering the most common failure mode, and a pre-commit self-check list. Agents read it automatically at startup.cmd/schema/main.go— a small helper that reflects over theAppConfigtype in the project’s pinnedpkg/configversion and emits the resulting JSON Schema (Draft 7). Invoked bygofasta config schema; callable directly asgo run ./cmd/schemafor CI or IDE extensions. Because it runs in the project, the schema always matches the exact library version the project builds against — no version skew between the CLI and the installedpkg/config.
CLI-level agent features:
- Structured errors. Every
gofastaerror carries a stable machine-readable code, a one-line message, a remediation hint, and a link to the relevant docs page. In--jsonmode errors serialize as{code, message, hint, docs}— agents pattern-match on the code rather than regex-parsing English. - Universal
--jsonflag. Every structured-output command (routes,version,verify,status,inspect,config schema,ai status,do <workflow>,g scaffold, …) emits a single-line JSON document when the flag is set. The shapes are stable API. gofasta ai <agent>installer. Opt-in, idempotent installation of per-agent configuration (permission allowlists, pre-commit hooks, project slash commands, conventions files). Shipping every agent’s configuration in the scaffold would clutter projects for developers who don’t use agents; this installer is the middle path.
Documentation-side agent artifacts:
/llms.txtand/llms-full.txton the documentation site. The first is a structured markdown index of every docs page following the llmstxt.org spec; the second is the entire documentation concatenated into one file for agents that prefer single-shot context loading. Both regenerated on every deploy via a build-time Node script that walks the MDX tree — no hand maintenance.
These decisions together make gofasta projects materially smoother to develop with AI assistance: agents land with immediate context (AGENTS.md), navigate the docs efficiently (llms.txt), run commands and parse results mechanically (--json), and get their own tooling configured with a single command (gofasta ai <agent>).
After generation, run gofasta wire to regenerate the DI container and gofasta migrate up to create the table.
5. The Gofasta Library
The library (github.com/gofastadev/gofasta) provides packages under pkg/. Each package is independently importable — there is no central “gofasta” import that pulls in everything.
| Package | Purpose |
|---|---|
pkg/config | YAML configuration loading with environment variable overrides |
pkg/logger | Structured logging via Go’s slog with configurable format and level |
pkg/errors | Typed application errors mapped to HTTP status codes and GraphQL error presentation |
pkg/models | Base model with UUID, timestamps, soft delete, optimistic locking |
pkg/types | Common DTOs for pagination, sorting, API responses |
pkg/httputil | JSON binding, response helpers, error-returning handler adapter |
pkg/middleware | CORS, rate limiting, logging, recovery, security headers, request ID, content-type validation |
pkg/health | Liveness and readiness health check endpoints |
pkg/auth | JWT generation/validation, Casbin RBAC, authentication and authorization middleware |
pkg/encryption | AES-256-GCM encryption/decryption for data at rest |
pkg/session | Cookie and filesystem session management (wraps gorilla/sessions) |
pkg/cache | In-memory and Redis caching with a unified interface |
pkg/storage | Local filesystem and S3-compatible file storage |
pkg/seeds | Database seeding with ordered execution |
pkg/mailer | Email delivery via SMTP, SendGrid, or Brevo with HTML template rendering |
pkg/notify | Multi-channel notifications (email, SMS via Twilio, Slack webhooks, database) |
pkg/websocket | WebSocket hub with rooms, broadcast, and connection management |
pkg/scheduler | Cron-based job scheduling with second-precision expressions |
pkg/queue | Async task queue backed by Redis via hibiken/asynq |
pkg/resilience | Retry policies and circuit breakers via failsafe-go |
pkg/validators | Struct validation via go-playground/validator/v10 with custom rules |
pkg/i18n | Internationalization with YAML locale files via go-i18n |
pkg/observability | Prometheus metrics and OpenTelemetry distributed tracing |
pkg/featureflag | Feature flag evaluation via the OpenFeature Go SDK with a swappable provider (in-memory, Flagd, LaunchDarkly, go-feature-flag, or custom) |
pkg/utils | Pagination math, string helpers, generic slice/map operations |
pkg/testutil/testdb | PostgreSQL container setup via testcontainers-go for integration tests |
5.1 Interface-Driven Design
Every package that admits multiple implementations defines an interface. This enables swapping implementations without changing consuming code:
// pkg/cache defines the interface
type CacheService interface {
Get(ctx context.Context, key string, dest interface{}) error
Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error
Delete(ctx context.Context, key string) error
Flush(ctx context.Context) error
Ping(ctx context.Context) error
}
// Two implementations are provided
cache.NewMemoryCache() // In-memory with TTL
cache.NewRedisCache(cfg) // Redis-backedThe same pattern applies to StorageService (local filesystem / S3), EmailSender (SMTP / SendGrid / Brevo), and QueueService (asynq / Redis).
5.2 Configuration
All packages read configuration from a central config.yaml file with environment variable overrides:
app:
name: myapp
port: 8080
env: development
database:
driver: postgres
host: localhost
port: 5432
name: myapp_db
user: postgres
password: postgres
ssl_mode: disable
max_open_conns: 25
max_idle_conns: 5
conn_max_lifetime: 5m
jwt:
secret: change-me-in-production
expiration: 24h
refresh_expiration: 168h
issuer: myapp
cache:
driver: memory
ttl: 5m
mail:
driver: smtp
from_name: MyApp
from_address: noreply@myapp.com
queue:
driver: memory
workers: 5
retry_attempts: 3
retry_delay: 30s
storage:
driver: local
local:
path: ./uploads
observability:
metrics:
enabled: true
path: /metrics
tracing:
enabled: false
exporter: jaeger
rate_limit:
enabled: true
max: 100
window: 1m
log:
level: info
format: jsonEvery value can be overridden via environment variables using the prefix GOFASTA_ with dot-separated keys mapped to underscores (e.g., GOFASTA_DATABASE_HOST=db). This allows committing safe development defaults while managing production secrets through the environment.
6. Code Generation Strategy
6.1 Philosophy
Gofasta’s code generation follows a principle common among well-accepted Go tools like sqlc, Wire, oapi-codegen, and go-blueprint: generate readable Go code, then get out of the way.
The generated output is:
- Standard Go — no custom syntax, no build tags (except Wire’s
wireinjecttag), no preprocessing - Readable — generated code looks like code a developer would write by hand
- Committed — generated files are checked into version control
- Editable — developers can and should modify generated code to fit their needs
- CLI-independent — once a project is generated, the
gofastaCLI is not needed to build, run, migrate, or deploy it. A developer can uninstall the CLI and the project keeps working. The CLI is a generation tool, not a runtime dependency.
Generated projects do import the toolkit’s pkg/* packages as their default building blocks (for config loading, logging, JWT, caching, mailing, and so on), just as any Go backend imports a set of community libraries. Those defaults are opt-out: any single pkg/* import can be deleted and replaced with an alternative library or a local implementation without touching the rest of the project. The toolkit never installs a runtime wrapper around your code — your main.go calls standard net/http, your HTTP handlers return plain errors, your models are plain GORM structs.
6.2 What Gets Generated
The CLI uses Go’s text/template package to produce source files. Templates are embedded in the CLI binary. The generation process is deterministic: the same input always produces the same output.
| Generator | Input | Output |
|---|---|---|
new | Project name, module path, driver | ~78 files: full project structure |
scaffold | Resource name, field definitions | 11 new files + 4 patched files |
model | Resource name, field definitions | Model struct + migration pair |
migration | Migration name, optional fields | SQL migration pair (up/down) |
repository | Resource name, field definitions | Interface + GORM implementation |
service | Resource name, field definitions | Interface + implementation |
controller | Resource name, field definitions | REST controller with Swagger annotations |
dto | Resource name, field definitions | Create/Update/Response DTOs |
route | Resource name | Route registration function |
resolver | Resource name | GraphQL schema + resolver stubs |
provider | Resource name | Wire provider set |
job | Job name, optional cron expression | Background job with scheduler registration |
task | Task name | Async task handler for asynq |
email-template | Template name | Responsive HTML email template |
6.3 Name Conventions
The CLI automatically converts resource names across naming conventions:
| Input | PascalCase | snake_case | camelCase | Plural |
|---|---|---|---|---|
Product | Product | product | product | products |
OrderItem | OrderItem | order_item | orderItem | order_items |
This ensures consistent naming across Go types (PascalCase), database tables (snake_case), JSON fields (camelCase), and URL paths (plural snake_case).
7. Dependency Injection
7.1 Google Wire
Gofasta uses Google Wire for compile-time dependency injection. Wire generates concrete Go code that wires all dependencies — there is no runtime container, no reflection, and no service locator pattern.
7.2 How It Works
Each resource defines a provider set that declares how to construct its components:
// app/di/providers/product.go
var ProductSet = wire.NewSet(
repositories.NewProductRepository,
wire.Bind(new(repoInterfaces.ProductRepository), new(*repositories.ProductRepositoryImpl)),
services.NewProductService,
wire.Bind(new(svcInterfaces.ProductService), new(*services.ProductServiceImpl)),
controllers.NewProductController,
)An injector function in app/di/wire.go assembles all provider sets:
//go:build wireinject
func InitializeContainer(cfg *config.AppConfig, db *gorm.DB) (*Container, error) {
wire.Build(
providers.UserSet,
providers.AuthSet,
providers.ProductSet,
wire.Struct(new(Container), "*"),
)
return nil, nil
}Running gofasta wire (which invokes wire ./app/di/...) produces wire_gen.go — a concrete function that constructs the entire dependency graph. If any dependency is missing or circular, the Go compiler reports it as a build error.
7.3 Why Wire
- Compile-time resolution. Missing dependencies are caught at build time, not at startup.
- No reflection. The generated code is plain Go function calls.
- Readable output.
wire_gen.goshows exactly how every dependency is constructed. - No framework coupling. Wire is a standalone tool maintained by the Go team at Google.
8. Database Layer
8.1 GORM
Gofasta uses GORM as its database abstraction. GORM provides an active-record-style ORM with support for multiple database drivers.
8.2 Multi-Database Support
A single database.driver field in config.yaml controls which database the project uses. The CLI generates driver-specific SQL migrations, and the pkg/config package connects to the correct driver at startup. Switching databases requires changing one configuration value and regenerating migrations — no application code changes.
| Driver | Config value | Connection pooling | Notes |
|---|---|---|---|
| PostgreSQL | postgres | Yes | Default. Full feature support including CITEXT, UUID generation via gen_random_uuid(). |
| MySQL | mysql | Yes | UTF-8 (utf8mb4), timezone-aware connections. |
| SQLite | sqlite | No | File-based or :memory:. Suitable for development, testing, and embedded use. |
| SQL Server | sqlserver | Yes | Microsoft SQL Server. |
| ClickHouse | clickhouse | Yes | Column-oriented, for analytics workloads. |
Generated migrations adapt SQL types per driver automatically. For example, a uuid field produces UUID on PostgreSQL, CHAR(36) on MySQL, and TEXT on SQLite. A bool field produces BOOLEAN on PostgreSQL, TINYINT(1) on MySQL, and INTEGER on SQLite.
Because the postgres driver uses GORM’s standard PostgreSQL dialect, projects can point it at any PostgreSQL wire-compatible backend — including managed Postgres services (Neon, Supabase, Amazon RDS, Google Cloud SQL) and distributed SQL engines (CockroachDB, YugabyteDB) — by changing the DSN in config.yaml. Driver-specific features (CockroachDB’s gen_random_uuid() replacement, serialization levels, etc.) are the responsibility of the target database and are configured through its own tooling; gofasta does not ship a dedicated driver entry for each wire-compatible backend.
8.3 Base Model
All models embed models.BaseModelImpl from the pkg/models package, which provides:
- UUID primary key — auto-generated via GORM’s
BeforeCreatehook - Timestamps —
CreatedAtandUpdatedAt, managed automatically by GORM - Soft delete —
DeletedAtfield; deleted records are excluded from queries by default - Optimistic locking —
RecordVersionfield incremented on each update for concurrent write safety - Active flag —
IsActiveandIsDeletablefields for business-level record control
type Product struct {
models.BaseModelImpl
Name string `gorm:"type:varchar(255);not null" json:"name"`
Price float64 `gorm:"type:decimal(10,2);not null" json:"price"`
}8.4 Migrations
Migrations are plain SQL files in db/migrations/, organized as numbered pairs:
db/migrations/
├── 000001_create_users.up.sql
├── 000001_create_users.down.sql
├── 000002_create_products.up.sql
└── 000002_create_products.down.sqlThe up file applies the change; the down file reverses it. Migrations are run with gofasta migrate up and rolled back with gofasta migrate down. Migration status is tracked in the database.
A new project includes base migrations that create database-level functions for auto-updating updated_at timestamps, enforcing soft-delete protection on non-deletable records, and incrementing record versions — all as database triggers, not application code.
8.5 Repository Pattern
Data access is abstracted behind repository interfaces:
// app/repositories/interfaces/product_repository.go
type ProductRepository interface {
Create(ctx context.Context, product *models.Product) error
FindAll(ctx context.Context, page, perPage int) ([]models.Product, int64, error)
FindByID(ctx context.Context, id string) (*models.Product, error)
Update(ctx context.Context, product *models.Product) error
Delete(ctx context.Context, id string) error
}The implementation uses GORM directly. This separation allows mocking the repository in service tests without a database connection.
8.6 Seeds
Database seeds in db/seeds/ populate initial data (default roles, admin users, test data). Seeds are registered via pkg/seeds and executed in order:
gofasta seed # Run all seeds
gofasta seed --fresh # Drop tables, re-migrate, then seed9. HTTP and API Layer
9.1 Routing
Gofasta uses chi as its default HTTP router, which builds on Go’s standard net/http interfaces. The router is an opt-out default — it can be replaced with gorilla/mux, stdlib http.ServeMux, or any http.Handler-compatible router without touching the rest of the project. Routes are registered explicitly in Go code:
func ProductRoutes(r chi.Router, ctrl *controllers.ProductController) {
r.Get("/products", httputil.Handle(ctrl.List))
r.Post("/products", httputil.Handle(ctrl.Create))
r.Get("/products/{id}", httputil.Handle(ctrl.GetByID))
r.Put("/products/{id}", httputil.Handle(ctrl.Update))
r.Delete("/products/{id}", httputil.Handle(ctrl.Archive))
}Every route is visible in the source code. There is no auto-discovery based on file names or struct methods. The httputil.Handle() wrapper adapts error-returning controller methods to standard http.HandlerFunc.
9.2 Controllers
Controllers return errors instead of writing responses directly on failure. The httputil.Handle() wrapper converts returned errors to appropriate HTTP responses:
func (c *ProductController) Create(w http.ResponseWriter, r *http.Request) error {
var req dtos.CreateProductRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return apperrors.BadRequest("invalid request body")
}
result, err := c.service.Create(r.Context(), req)
if err != nil {
return err
}
return httputil.Created(w, result)
}Path parameters are extracted via chi.URLParam(r, "id"). Request context is available via r.Context().
9.3 Request Binding
The httputil package provides generic bind functions that parse and validate request bodies in a single call:
// Bind JSON body and validate struct tags
dto, err := httputil.BindJSON[dtos.CreateProductDto](r)
// Bind query parameters
query, err := httputil.BindQuery[dtos.ListProductsQuery](r)
// Bind form data
form, err := httputil.BindForm[dtos.UploadRequest](r)If validation fails, the bind functions return structured error responses automatically.
9.4 DTOs
Request and response types are separated from database models via DTOs:
type TCreateProductDto struct {
Name string `json:"name" validate:"required"`
Price float64 `json:"price" validate:"required"`
}
type TUpdateProductDto struct {
Name *string `json:"name,omitempty"`
Price *float64 `json:"price,omitempty"`
}
type ProductResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
RecordVersion int `json:"recordVersion"`
IsActive bool `json:"isActive"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}Update DTOs use pointer fields to distinguish between “not provided” (nil) and “set to zero value.” Validation uses go-playground/validator/v10 struct tags.
9.5 Response Helpers
The httputil package provides consistent JSON response formatting:
httputil.OK(w, data) // 200
httputil.Created(w, data) // 201
httputil.JSON(w, status, data) // Custom status
httputil.Handle(handlerFn) // Wraps error-returning handlers into http.HandlerFunc9.6 Middleware
The middleware package provides standard HTTP middleware using the func(http.Handler) http.Handler signature, composable via middleware.Chain():
| Middleware | Function |
|---|---|
middleware.RequestLogging(logger) | Structured request logging with method, path, status, duration, and request ID |
middleware.Recovery() | Panic recovery with error logging |
middleware.CORS(config) | Cross-origin resource sharing |
middleware.SecurityHeaders(config) | HSTS, CSP, X-Frame-Options, X-Content-Type-Options via unrolled/secure |
middleware.RateLimit(config) | Per-IP rate limiting with configurable store (memory or Redis) |
middleware.RequestID() | Generates and stores a unique request ID in context |
middleware.ContentTypeValidation() | Validates request Content-Type header |
middleware.Chain(handler, middlewares...) | Compose multiple middleware in order |
9.7 GraphQL (Opt-In)
GraphQL support is available when a project is created with gofasta new myapp --graphql. It is powered by gqlgen and is additive — REST controllers and routes remain fully functional. Schema files live in app/graphql/schema/, and resolvers are generated from the schema. Resolvers call the same services used by REST controllers, ensuring logic consistency:
func (r *queryResolver) Products(ctx context.Context, page *int, limit *int) (*dtos.PaginatedProductResponse, error) {
return r.productService.FindAll(*page, *limit)
}Available endpoints:
/graphql— GraphQL endpoint/graphql-playground— Interactive GraphQL explorer (development only)
9.8 Swagger
Controllers include Swagger annotation comments. Running gofasta swagger generates OpenAPI spec files (docs/swagger.json, docs/swagger.yaml) and a Swagger UI endpoint at /swagger/index.html.
10. Authentication and Authorization
10.1 JWT Authentication
The pkg/auth package provides JWT token management:
jwtService := auth.NewJWTService(cfg.JWT)
// Generate tokens
token, err := jwtService.GenerateToken(userID, role)
refreshToken, err := jwtService.GenerateRefreshToken(userID)
// Validate and refresh
claims, err := jwtService.ValidateToken(tokenString)
newToken, err := jwtService.RefreshToken(refreshToken)Configuration via config.yaml:
jwt:
secret: change-me-in-production
expiration: 24h
refresh_expiration: 168h
issuer: myappGenerated projects include pre-built auth endpoints: register (POST /api/v1/auth/register), login (POST /api/v1/auth/login), and token refresh (POST /api/v1/auth/refresh).
10.2 RBAC with Casbin
Role-based access control uses Casbin with policy files:
# configs/rbac_policy.csv
p, admin, /api/v1/*, *
p, user, /api/v1/products, GET
p, user, /api/v1/profile, (GET)|(PUT)
g, admin, moderator
g, moderator, userThe auth.RBACMiddleware() middleware checks permissions on each request. Policies can also be modified at runtime via the Casbin enforcer API.
10.3 Authentication Middleware
The pkg/auth package provides two middleware functions:
// Extract and validate JWT from Authorization: Bearer header
auth.JWTAuth(jwtService)
// Enforce RBAC permissions based on request path and method
auth.RBACMiddleware(rbacService, resource, action)Both middleware functions follow the standard func(http.Handler) http.Handler signature.
11. Security Defaults
A scaffolded Gofasta project includes security protections out of the box, configured as middleware in the HTTP pipeline. These are not hidden behaviors — each protection is a visible middleware call in the route configuration, and each can be modified or removed by editing the relevant source file.
11.1 What Ships by Default
| Protection | Implementation | What it does |
|---|---|---|
| Security headers | middleware.SecurityHeaders() | Sets HSTS, Content-Security-Policy, X-Frame-Options (DENY), X-Content-Type-Options (nosniff), Referrer-Policy, and Permissions-Policy via the unrolled/secure library. |
| CORS | middleware.CORS(config) | Configurable allowed origins, methods, and headers. Defaults to restrictive settings. |
| Rate limiting | middleware.RateLimit(config) | Token bucket algorithm, configurable per-IP limits and time window. Supports memory and Redis stores. |
| JWT authentication | auth.JWTAuth() | HS256-signed tokens with configurable expiry. Refresh token support. |
| Role-based access control | auth.RBACMiddleware() | Casbin-backed permission enforcement per route. |
| Password hashing | bcrypt | Passwords are never stored in plaintext. |
| Encryption at rest | pkg/encryption | AES-256-GCM for sensitive data fields. |
| Request ID | middleware.RequestID() | Unique ID per request for audit trails and log correlation. |
| Panic recovery | middleware.Recovery() | Catches panics, logs the stack trace, returns 500 without exposing internals. |
| Content-type validation | middleware.ContentTypeValidation() | Rejects requests with unexpected Content-Type headers. |
| Input validation | pkg/validators | Struct-level validation on all incoming DTOs via go-playground/validator. |
| Soft deletes | models.BaseModelImpl | Records are marked deleted, never physically removed. Database triggers prevent deletion of non-deletable records. |
| Optimistic locking | models.BaseModelImpl | RecordVersion field prevents silent overwrites from concurrent updates. |
11.2 No Hidden Magic
Every protection listed above corresponds to a line of code in the generated project. Security headers are applied because middleware.SecurityHeaders(cfg) is called in the middleware chain. Rate limiting works because middleware.RateLimit(cfg) is registered. A developer can read the middleware stack top to bottom and know exactly what runs on every request. There is no implicit security layer that activates behind the scenes.
12. Background Processing
12.1 Scheduled Jobs
The pkg/scheduler package runs cron-based recurring tasks:
s := scheduler.New(logger)
s.Register(myJob) // myJob implements scheduler.Job interface: Name() string, Run() error
s.Start()
defer s.Stop()The Job interface requires two methods: Name() (returns a human-readable identifier for logging) and Run() (executes the job logic). The scheduler uses 6-field cron syntax with second precision via robfig/cron.
The CLI generates job scaffolding with gofasta g job cleanup-tokens "0 0 * * * *", which creates the job file, registers it in the scheduler, and adds the cron expression to config.yaml.
12.2 Async Task Queues
The task queue is built on hibiken/asynq , a Redis-based distributed task queue:
queue := queue.NewAsynqQueue(cfg, logger)
// Register handler
queue.RegisterHandler("send_welcome_email", handleSendWelcomeEmail)
// Enqueue a task from application code
queue.Enqueue("send_welcome_email", payload)
// Start processing
queue.Start()
defer queue.Shutdown()Tasks are enqueued from application code and processed asynchronously by worker goroutines. Failed tasks are retried automatically by asynq with configurable retry policies and exponential backoff.
The CLI generates task scaffolding with gofasta g task send-welcome-email.
Backend: Redis (via asynq).
13. Real-Time Communication
The pkg/websocket package provides a WebSocket hub for real-time bidirectional communication:
hub := websocket.NewHub(logger)
go hub.Run()
// In an HTTP handler — upgrade the connection
router.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
websocket.ServeWS(hub, w, r)
})13.1 Rooms
Clients can be organized into rooms for scoped messaging:
hub.JoinRoom(client, "chat:lobby")
hub.LeaveRoom(client, "chat:lobby")13.2 Broadcasting
Messages can be sent to all connected clients or to a specific room:
// Broadcast to all clients
hub.Broadcast(websocket.Message{
Event: "notification",
Payload: payload,
})
// Broadcast to a specific room
hub.Broadcast(websocket.Message{
Room: "chat:lobby",
Event: "new_message",
Payload: payload,
})13.3 Connection Management
The hub tracks all connected clients with goroutine-safe operations:
count := hub.ClientCount()Clients support configurable ping periods and write deadlines via functional options:
websocket.ServeWS(hub, w, r,
websocket.WithPingPeriod(30 * time.Second),
websocket.WithWriteWait(10 * time.Second),
)14. Email and Notifications
14.1 Email
The pkg/mailer package provides email delivery through three providers behind a single interface:
type EmailSender interface {
Send(ctx context.Context, msg EmailMessage) error
}Providers:
SMTPSender— SMTP with optional STARTTLSSendGridSender— SendGrid API v3BrevoSender— Brevo (formerly Sendinblue) HTTP API
The active provider is selected by mail.driver in config.yaml. A factory function creates the correct implementation:
sender, err := mailer.NewEmailSender(cfg, renderer, logger)14.2 HTML Email Templates
The TemplateRenderer loads and caches Go HTML templates from the templates/emails/ directory:
renderer := mailer.NewTemplateRenderer("templates/emails", "MyApp")
html, err := renderer.Render("welcome", map[string]any{
"Name": "Alice",
"AppName": "MyApp",
})The CLI generates new email templates with gofasta g email-template welcome, which creates an HTML file extending the base layout with header and footer.
14.3 Multi-Channel Notifications
The pkg/notify package dispatches notifications across multiple channels:
notifier := notify.NewNotifier(logger,
notify.NewEmailChannel(emailSender),
notify.NewSMSChannel(twilioSID, twilioToken, fromNumber),
notify.NewSlackChannel(webhookURL),
notify.NewDatabaseChannel(db),
)
err := notifier.Send(ctx, notify.Recipient{
ID: "user-123",
Email: "alice@example.com",
Phone: "+1234567890",
Name: "Alice",
}, notify.Notification{
Subject: "Order Shipped",
Body: "Your order #456 has been shipped.",
Channels: []notify.Channel{notify.ChannelEmail, notify.ChannelSMS},
})Each channel implements the ChannelSender interface:
| Channel | Backend | What it sends |
|---|---|---|
EmailChannel | Delegates to pkg/mailer | Email via SMTP, SendGrid, or Brevo |
SMSChannel | Twilio REST API | SMS text messages |
SlackChannel | Incoming Webhook URL | Slack messages |
DatabaseChannel | GORM (any supported database) | Persisted notification records with read/unread tracking |
If no channels are specified in the notification, the notifier dispatches to all registered channels.
15. File Storage
The pkg/storage package provides file storage behind a unified interface:
type StorageService interface {
Upload(ctx context.Context, path string, reader io.Reader, size int64) error
Download(ctx context.Context, path string) (io.ReadCloser, error)
Delete(ctx context.Context, path string) error
URL(path string) string
}Backends:
| Backend | Config value | Implementation |
|---|---|---|
| Local filesystem | local | Stores files at a configured directory path. URL() returns the joined filesystem path. |
| S3-compatible | s3 | AWS S3, MinIO, DigitalOcean Spaces, or any S3-compatible provider via the minio-go client. URL() returns the HTTPS object URL. |
The active backend is selected by storage.driver in config.yaml:
storage:
driver: local # or "s3"
local:
path: ./uploads
s3:
endpoint: s3.amazonaws.com
bucket: myapp-uploads
region: us-east-1
access_key: ${AWS_ACCESS_KEY_ID}
secret_key: ${AWS_SECRET_ACCESS_KEY}
use_ssl: trueApplication code uses the interface, making the backend swappable without code changes:
store, err := storage.NewStorageService(cfg, logger)
err := store.Upload(ctx, "avatars/user-123.jpg", file, fileSize)
reader, err := store.Download(ctx, "avatars/user-123.jpg")
url := store.URL("avatars/user-123.jpg")
err := store.Delete(ctx, "avatars/user-123.jpg")16. Internationalization
The pkg/i18n package provides message translation using YAML locale files:
i18nService := i18n.NewI18nService("locales", "en")
// Translate a message
greeting := i18nService.T("fr", "welcome_message", map[string]interface{}{
"Name": "Alice",
})
// Extract language from HTTP request (Accept-Language header)
lang := i18nService.LangFromRequest(r)
msg := i18nService.T(lang, "validation.required", nil)Locale files live in the locales/ directory as YAML:
# locales/en.yaml
welcome_message: "Welcome, {{.Name}}!"
validation:
required: "This field is required."
email: "Please enter a valid email address."The function i18n.CreateDefaultLocaleFile("locales") generates a starter English locale file if none exists. A scaffolded project includes the locales/ directory with an en.yaml file.
17. Feature Flags
The pkg/featureflag package is a thin wrapper around the OpenFeature Go SDK — the CNCF-standard flag-evaluation contract. Application code calls one method (IsEnabled); the actual flag storage and rule engine live in a provider the application registers at startup. Providers are swappable: an in-memory provider for local development, Flagd or go-feature-flag for self-hosted rule engines, LaunchDarkly for commercial SaaS, or a custom implementation — the application’s flag-checking code never changes.
// Register any OpenFeature-compatible provider at startup.
// Example: in-memory provider for local dev.
flags := map[string]memprovider.InMemoryFlag{
"new_checkout_flow": {
Key: "new_checkout_flow",
State: memprovider.Enabled,
DefaultVariant: "off",
Variants: map[string]any{"on": true, "off": false},
},
}
flagService, _ := featureflag.NewInMemoryService(flags, logger)
defer flagService.Close()
// Evaluation is provider-independent.
if flagService.IsEnabled(ctx, "new_checkout_flow", userID, map[string]any{
"plan": "premium",
"country": "US",
}) {
// New checkout logic
}Swapping to a production rule engine is a one-line change: call openfeature.SetProvider(...) with the target provider before constructing the service. Every call site that evaluates flags continues to work unchanged. This keeps pkg/featureflag consistent with gofasta’s opt-out-default philosophy — the package ships a contract, not a locked-in backend.
18. Observability
18.1 Prometheus Metrics
The pkg/observability package exposes Prometheus metrics via HTTP middleware:
router.Use(observability.MetricsMiddleware)
router.Handle("/metrics", observability.MetricsHandler())Exported metrics:
| Metric | Type | Labels | Description |
|---|---|---|---|
http_requests_total | Counter | method, path, status | Total HTTP requests processed |
http_request_duration_seconds | Histogram | method, path | Request latency distribution |
http_requests_in_flight | Gauge | — | Number of requests currently being processed |
These metrics are automatically recorded by the MetricsMiddleware for every HTTP request. The /metrics endpoint returns Prometheus-formatted output compatible with any Prometheus scraper, Grafana dashboard, or alerting system.
18.2 Distributed Tracing
OpenTelemetry tracing is available via two functions:
// Initialize the tracer provider
shutdown := observability.InitTracer("myapp")
defer shutdown()
// Apply tracing middleware to the router
router.Use(observability.TracingMiddleware("myapp"))InitTracer sets up an OTLP exporter with an AlwaysSample strategy and registers trace context and baggage propagation. TracingMiddleware creates spans for each HTTP request with method, path, and status attributes.
When tracing is not configured, both functions are no-ops with zero overhead.
18.3 Structured Logging
The pkg/logger package creates a structured logger using Go’s standard slog package:
log := logger.NewLogger(cfg.Log)The returned *slog.Logger supports JSON or text output format and configurable log levels (debug, info, warn, error). Because it returns a standard slog.Logger, it is compatible with any library or middleware that accepts slog.
18.4 Health Checks
The pkg/health package provides a controller with three endpoints for liveness and readiness probes:
healthCtrl := health.NewController(db, cacheService)
router.HandleFunc("/health", httputil.Handle(healthCtrl.Check))
router.HandleFunc("/health/live", httputil.Handle(healthCtrl.Live))
router.HandleFunc("/health/ready", httputil.Handle(healthCtrl.Ready))| Endpoint | Purpose | Checks |
|---|---|---|
/health | Basic liveness | Process is running, reports uptime |
/health/live | Liveness probe | Process is alive |
/health/ready | Readiness probe | Database connectivity (with latency), cache connectivity (if configured) |
The readiness endpoint checks database and cache health with timing information, making it suitable for load balancer health checks, uptime monitors, and orchestrator-style probes.
19. Resilience
The pkg/resilience package provides composable fault tolerance patterns using failsafe-go generics:
19.1 Retry with Backoff
retryPolicy := resilience.NewRetryPolicy[*http.Response](3, 100*time.Millisecond)
response, err := resilience.Execute(func() (*http.Response, error) {
return http.Get("https://api.example.com/data")
}, retryPolicy)NewRetryPolicy[T] creates a retry policy with the specified maximum attempts and delay between retries. Failsafe-go applies exponential backoff automatically.
19.2 Circuit Breaker
cb := resilience.NewCircuitBreaker[*PaymentResult](5, 30*time.Second)
result, err := resilience.Execute(func() (*PaymentResult, error) {
return paymentClient.Charge(ctx, amount)
}, cb)NewCircuitBreaker[T] creates a circuit breaker that opens after the specified number of failures and remains open for the specified delay before transitioning to half-open. States: Closed (normal) → Open (fail-fast) → Half-Open (test recovery).
19.3 Composing Policies
Retry and circuit breaker policies can be composed in a single Execute call:
retryPolicy := resilience.NewRetryPolicy[*Result](3, 100*time.Millisecond)
cb := resilience.NewCircuitBreaker[*Result](5, 30*time.Second)
result, err := resilience.Execute(func() (*Result, error) {
return externalService.Call(ctx, req)
}, retryPolicy, cb)Failsafe-go applies the policies in order: the circuit breaker wraps the retry policy, so retries only happen when the circuit is closed or half-open.
20. Testing
20.1 Integration Test Database
The pkg/testutil/testdb package provides a real PostgreSQL database for integration tests using testcontainers-go:
func TestProductRepository(t *testing.T) {
db := testdb.SetupTestDB(t)
// db is a *gorm.DB connected to a real PostgreSQL 16 container
// Base migrations are already applied (UUID generation, timestamps, soft delete triggers)
// Container is automatically cleaned up when the test ends via t.Cleanup
}SetupTestDB(t) spins up a PostgreSQL 16 Alpine container, applies the base DDL migrations (CITEXT extension, updated_at triggers, soft-delete protection, record version increment), and returns a connected *gorm.DB. The container is automatically destroyed when the test function exits.
For running additional migrations:
db := testdb.SetupTestDB(t)
testdb.RunMigrations(db) // Apply project-specific migrations20.2 Unit Testing Services
Services depend on repository interfaces, making them easy to test with mock implementations:
func TestProductService_Create(t *testing.T) {
mock := &mocks.MockProductRepository{
CreateFn: func(ctx context.Context, product *models.Product) error {
product.ID = uuid.New()
return nil
},
}
svc := services.NewProductService(mock)
result, err := svc.Create(ctx, dtos.CreateProductRequest{Name: "Widget", Price: 9.99})
assert.NoError(t, err)
assert.Equal(t, "Widget", result.Name)
}The scaffold generates mock implementations for each resource’s repository and service interfaces in testutil/mocks/.
20.3 Testing Approach
Gofasta projects use testify for assertions and test suites. The recommended testing strategy:
- Unit tests for services: mock the repository interface, test business logic in isolation.
- Integration tests for repositories: use
testdb.SetupTestDB(t)with a real PostgreSQL container to verify actual database queries. - Unit tests for controllers: mock the service interface, test HTTP handler logic.
- End-to-end tests: start the full HTTP server and make real HTTP requests against it.
Run unit tests with go test -short ./... and integration tests with go test ./....
21. Deployment
21.1 Generated Deployment Files
Every Gofasta project includes deployment configurations out of the box, scoped to the deployment target gofasta natively supports — a Linux VPS running Docker or systemd, reached over SSH:
deployments/
├── ci/ # GitHub Actions workflow templates (test, release, deploy-vps)
├── docker/ # Production compose file + dev Dockerfile with hot reload
├── nginx/ # Reverse proxy configuration
└── systemd/ # Service unit and reference deploy scriptCloud-managed platforms (ECS, Cloud Run, App Service) are out of scope for the current release. Projects that require them are expected to import their own infrastructure-as-code manifests — pkg/* packages impose no runtime assumptions that prevent this.
21.2 Docker
The generated Dockerfile uses a multi-stage build:
# Build stage
FROM golang:1.25.0-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server ./app/main
# Production stage
FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /server
COPY --from=builder /app/config.yaml /config.yaml
EXPOSE 8080
CMD ["/server", "serve"]compose.yaml includes the application, PostgreSQL, Redis (cache profile), and asynqmon (queue profile) with health checks. The development loop is driven by a single command:
gofasta dev # db + cache + queue + Air on the host, auto-on profiles
gofasta dev --all-in-docker # everything inside Docker, including Air
gofasta dev --no-services # Air only, bring your own databasegofasta dev orchestrates the full pipeline — preflight, service-start with health-wait, migration apply, optional seed, Air hot-reload — and exposes interactive controls (r to restart from scratch, q to quit, h for help) plus a --dashboard flag that mounts a live request inspector with trace waterfalls, EXPLAIN-on-click, N+1 detection, and HAR export.
21.3 VPS Deployment
The gofasta deploy command deploys directly to a remote Linux server via SSH:
gofasta deploy setup # Prepare server (Docker, nginx, directories)
gofasta deploy # Deploy current version
gofasta deploy status # Check deployment status
gofasta deploy logs # Tail application logs
gofasta deploy rollback # Roll back to previous releaseTwo deployment methods are supported:
- Docker (default): builds a Docker image locally, transfers it via SSH (
docker save | ssh docker load— no registry required), runs via Docker Compose on the server. - Binary: cross-compiles a Go binary, transfers it via SCP, manages it via systemd.
Both methods use a Capistrano-style release-directory layout (/opt/<app>/releases/<timestamp>/ plus a current symlink atomically flipped after the health check passes), support zero-downtime rollback via gofasta deploy rollback, and optionally front the application with the generated nginx reverse-proxy config.
21.4 CI/CD
GitHub Actions workflow templates ship under deployments/ci/ as inert YAML — they do not run until the developer copies them into .github/workflows/. This opt-in pattern prevents a first push from triggering deploys against unset secrets. The templates provided:
- Test — runs on push/PR with a PostgreSQL service container; uploads coverage to Codecov.
- Release — on
v*tag push, builds and pushes a Docker image to GHCR using the defaultGITHUB_TOKEN. - Deploy (VPS) — on push to
mainor manual dispatch, runsgofasta deployagainst the configured VPS. RequiresDEPLOY_HOST,DEPLOY_SSH_KEY, andDEPLOY_PORTrepository secrets.
22. Adoption and Migration Path
22.1 Starting a New Project
The fastest path: run gofasta new myapp, then start adding resources with gofasta g scaffold. The project compiles and runs immediately with a starter User resource, database migrations, authentication, health checks, Swagger docs, and Docker configuration.
22.2 Adopting the Library in an Existing Project
The pkg/* packages can be imported individually into any existing Go project. There is no initialization ceremony and no required package ordering:
go get github.com/gofastadev/gofasta/pkg/cache
go get github.com/gofastadev/gofasta/pkg/resilience
go get github.com/gofastadev/gofasta/pkg/middlewareEach package is self-contained. Importing pkg/cache does not pull in pkg/mailer or any other unrelated package.
22.3 Replacing a Default Package
Every pkg/* import in a scaffolded project can be replaced independently:
- Delete the import of the package you want to replace (e.g.,
pkg/cache). go getyour preferred alternative (e.g.,github.com/eko/gocache).- Update the Wire provider to construct your alternative implementation.
- Run
gofasta wireto regenerate the DI container.
No other files in the project need to change. The service layer depends on interfaces, not concrete types, so swapping the underlying implementation is a single-provider change.
22.4 Ejecting Entirely
A scaffolded project has no runtime dependency on the gofasta CLI. The CLI is a development tool — the project builds and runs without it. To fully remove the pkg/* library dependencies:
- Replace each
pkg/*import with an alternative library or a local implementation. - Remove
github.com/gofastadev/gofastafromgo.mod. - The project is now a standalone Go application with zero gofasta dependencies.
This is possible because every pkg/* package implements a standard pattern (interface + constructor) with no global state, no init functions, and no implicit coupling between packages.
23. Comparison with Existing Tools
23.1 Positioning
Gofasta occupies a specific niche in the Go ecosystem: it sits between a bare HTTP router (which leaves every project-level decision to the developer) and a monolithic runtime wrapper (which dictates every project-level decision). It is a toolkit — a CLI that generates standard Go code plus a library of independently importable packages — and the developer is always in control of which pieces they use. The goal is to compress the repetitive parts of starting a backend project (wiring, CRUD boilerplate, deployment scaffolding) without introducing a runtime layer the developer’s code has to live inside.
Gofasta also occupies a second niche that no other Go toolkit currently targets as a first-class audience: AI coding agents. The design principles in §2.7 and the agent-ergonomics surface in §4.5 treat agents as peer developers of the project — every command accepts --json, every error carries a stable machine-parseable code, the scaffold ships an AGENTS.md briefing, and per-agent editor configuration (Claude Code, Cursor, OpenAI Codex, Aider, Windsurf) installs with one command (gofasta ai <agent>, e.g. gofasta ai claude). High-level commands (verify, status, inspect, do) collapse long multi-step procedures into single calls with structured output. This is what distinguishes Gofasta’s positioning from every comparable tool in the matrix below: the matrix lists capabilities an application needs; the agent-native design decides how an agent interacts with the toolchain to build and maintain that application. The rest of this section evaluates feature breadth against other tools; the agent-native distinction cuts across all of them.
23.2 Comparison Matrix
The following matrix compares Gofasta against the tools a Go backend developer is most likely to evaluate. The tools are grouped by category: HTTP routers, project scaffolders, backend development platforms, microservice toolkits, and design-first generators.
vs. Routers and Scaffolders
| Capability | mux/chi/Gin/Echo | go-blueprint | Buffalo | Gofasta |
|---|---|---|---|---|
| HTTP routing | Yes | Yes | Yes | Yes |
| Project scaffolding | No | Yes | Yes | Yes |
| Resource code generation | No | No | Yes | Yes |
| Compile-time DI | No | No | No | Yes (Wire) |
| Database migrations | No | No | Yes (Pop) | Yes (SQL files) |
| Multi-database support | N/A | Yes | Yes (Pop) | Yes (5 drivers) |
| GraphQL support | No | No | No | Yes (opt-in, gqlgen) |
| Background jobs | No | No | Yes | Yes |
| Email / notifications | No | No | No | Yes |
| WebSocket support | No | No | No | Yes |
| File storage | No | No | No | Yes (local, S3) |
| Observability | No | No | No | Yes (Prometheus, OpenTelemetry) |
| Security middleware | Partial | No | Partial | Yes (headers, CORS, rate limit, auth) |
| Deployment manifests | No | No | No | Yes (Docker, CI/CD, VPS) |
Standard go build / go test | Yes | Yes | Yes | Yes |
| Runtime wrapper required | Yes | No | Yes | No |
vs. Backend Platforms and Full Toolkits
| Capability | Encore | go-zero | GoFrame | Gofasta |
|---|---|---|---|---|
| HTTP routing | Yes (annotations) | Yes | Yes | Yes |
| Project scaffolding | Yes | Yes (goctl) | Yes (gf) | Yes |
| Resource code generation | No | Yes (from .api files) | Yes (from DB schemas) | Yes (from CLI args) |
| Compile-time DI | No | No | No | Yes (Wire) |
| Database migrations | Yes (auto-provisioned) | No | Yes | Yes (SQL files) |
| Multi-database support | No (PostgreSQL only) | Yes (MySQL, PostgreSQL, MongoDB) | Yes (6 drivers) | Yes (5 drivers) |
| GraphQL support | No | No | No | Yes (opt-in, gqlgen) |
| Background jobs | Yes (cron) | No | Yes | Yes |
| Pub/Sub | Yes (cloud-agnostic) | No | No | No (roadmap) |
| Email / notifications | No | No | No | Yes (SMTP, SendGrid, Brevo, SMS, Slack) |
| WebSocket support | No | No | Yes | Yes |
| File storage | Yes (S3/GCS) | No | No | Yes (local, S3) |
| Internationalization | No | No | Yes | Yes |
| Feature flags | No | No | No | Yes |
| Resilience patterns | No | Yes (circuit breaker, load shedding) | No | Yes (retry, circuit breaker) |
| Observability | Yes (auto-instrumented) | Yes (OpenTelemetry) | Yes | Yes (Prometheus, OpenTelemetry) |
| Service discovery | Yes (automatic) | Yes (etcd, Consul, K8s) | Yes (etcd, ZooKeeper) | No |
| Security middleware | Partial (auth handler) | Yes (JWT) | Yes | Yes (headers, CORS, rate limit, auth) |
| Health checks | No | No | No | Yes |
| Test utilities | Yes (auto test DB) | No | No | Yes (testcontainers) |
| Deployment manifests | No (cloud-managed) | No | No | Yes (Docker, CI/CD, VPS) |
| VPS deployment command | No | No | No | Yes |
| Auto infrastructure provisioning | Yes | No | No | No |
| Local dev dashboard | Yes | No | No | Yes |
Standard go build / go test | No (custom compiler) | Yes | Yes | Yes |
net/http middleware compatible | No | No (custom middleware) | Partial | Yes |
| Runtime wrapper required | Yes (custom runtime) | Yes | Yes | No |
| Custom syntax or annotations | Yes (//encore:api) | Yes (.api DSL) | No | No |
| API definition format | Annotations in Go | Proprietary .api files | Standard Go | Standard Go |
| Free and open source | Partial (cloud is paid) | Yes | Yes | Yes |
vs. Microservice Toolkits and Design-First Generators
| Capability | go-kit | Kratos | Goa | Gofasta |
|---|---|---|---|---|
| HTTP routing | Yes (transport layer) | Yes (HTTP + gRPC) | Yes (generated) | Yes |
| Project scaffolding | No | Yes (from protobuf) | Yes (from Go DSL) | Yes |
| Resource code generation | No | Yes (from .proto) | Yes (from DSL) | Yes (from CLI args) |
| Compile-time DI | No | Yes (Wire) | No | Yes (Wire) |
| API definition format | Go interfaces | Protocol Buffers | Go DSL | Standard Go |
| gRPC support | Yes | Yes (first-class) | Yes (generated) | No |
| Multi-database support | N/A | N/A | N/A | Yes (5 drivers) |
| Database migrations | No | No | No | Yes (SQL files) |
| Background jobs | No | No | No | Yes |
| Email / notifications | No | No | No | Yes |
| Resilience patterns | Yes (circuit breaker) | Yes | No | Yes (retry, circuit breaker) |
| Service discovery | Yes (etcd, Consul, DNS) | Yes (etcd, Consul, K8s) | No | No |
| Observability | Yes (OpenTracing) | Yes (OpenTelemetry) | No | Yes (Prometheus, OpenTelemetry) |
| OpenAPI generation | No | No | Yes (from DSL) | Yes (from code annotations) |
Standard go build / go test | Yes | Yes | Yes | Yes |
| Runtime wrapper required | No (pattern library) | Yes | No (generated code) | No |
| Deployment manifests | No | No | No | Yes (Docker, CI/CD, VPS) |
| Actively maintained | Maintenance mode | Yes | Yes | Yes |
23.3 Key Differentiators
vs. every other Go toolkit (on agent-native support): None of the tools in the matrix above treats AI coding agents as a first-class audience. Routers expose no structured command surface at all. Scaffolders (go-blueprint, Buffalo) emit projects with no embedded briefing for agents, no --json on their generators, and unstructured error strings that agents have to regex to understand. Platform toolkits (Encore, go-zero, GoFrame, Kratos) are built around proprietary runtimes, DSLs, or annotation systems that existing agents do not have stable priors for — an agent working inside these toolchains is guessing about non-Go syntax instead of applying its considerable knowledge of standard Go. Gofasta inverts this: it generates standard Go code (so agents can rely on their existing Go knowledge), ships a scaffolded AGENTS.md that briefs any agent on project conventions, installs editor-specific rule files for Claude Code / Cursor / Codex / Aider / Windsurf via gofasta ai <agent> (e.g. gofasta ai claude), adds a universal --json flag across every command, returns structured errors with stable codes (see §4.5), and exposes higher-level commands (verify, status, inspect, do) that collapse multi-step procedures into one call. The result: an agent opening a fresh Gofasta project has a machine-readable entry point (AGENTS.md + llms.txt), a machine-readable operational surface (every command), and machine-readable failure modes (error codes). This is a category distinction, not a feature comparison.
vs. HTTP routers (chi, gorilla/mux, Gin, Echo): These are HTTP routers. Gofasta uses chi (which builds on net/http) as its default router but adds project structure, code generation, database management, authentication, background processing, and utility packages. The router itself is a swappable default — a developer can replace it with gorilla/mux, stdlib http.ServeMux, or any other http.Handler-compatible router without touching pkg/* code. A developer using any router still needs to set all of the surrounding structure up manually.
vs. go-blueprint: go-blueprint generates project scaffolding with a choice of router and database driver. Gofasta goes further with resource-level code generation (gofasta g scaffold), a library of production packages, and generated deployment manifests. go-blueprint generates the starting point; Gofasta generates the starting point and the ongoing CRUD boilerplate.
vs. Buffalo: Buffalo bundled a full set of components — its own ORM (Pop), template engine (Plush), and asset pipeline — as a single runtime layer a project had to buy into all at once. Gofasta takes the opposite approach: it generates standalone Go code and lets the developer import only the pkg/* packages they actually need. There is no monolithic layer to maintain, and if any single package stops fitting a project’s needs it can be replaced in isolation without touching the rest of the code.
vs. Encore: Encore is a backend development platform that provides automatic infrastructure provisioning, zero-config distributed tracing, and cloud deployment via a custom Go compiler fork. It trades standard toolchain compatibility for reduced boilerplate — projects cannot use go build or go test directly, must use Encore’s proprietary //encore:api annotations for endpoint definition, and are limited to PostgreSQL as the only supported database. Encore’s infrastructure declarations use proprietary APIs (encore.dev/storage/sqldb, encore.dev/pubsub, encore.dev/storage/cache) that create a compile-time dependency on the Encore toolchain; migrating away requires rewriting all API endpoint definitions, replacing infrastructure declarations with direct client libraries, and setting up independent observability. Gofasta takes the opposite approach: it generates standard Go code that compiles with go build, tests with go test, and deploys with any CI/CD pipeline. Generated projects use chi with standard net/http middleware — the entire Go middleware ecosystem is compatible. Gofasta supports five database drivers, and every pkg/* import is a standard Go dependency that can be individually replaced without touching the rest of the project. The tradeoff is explicit: Encore automates more at the cost of toolchain control and vendor dependency; Gofasta automates the generation step and then returns full control to the developer.
vs. go-zero: go-zero is a microservices toolkit with a powerful CLI (goctl) that generates complete services from .api definition files or Protocol Buffers. It includes built-in resilience patterns (circuit breaking, adaptive load shedding), service discovery, and distributed tracing — capabilities proven at massive scale at Tal Education. However, go-zero requires a proprietary .api definition format that is not OpenAPI, generates code tightly coupled to the go-zero runtime, and uses custom middleware signatures incompatible with standard net/http middleware. Its project structure is opinionated and difficult to deviate from. Gofasta generates standard Go code from CLI arguments (no proprietary definition file), uses net/http-compatible middleware, and produces projects that compile without any toolkit-specific runtime. go-zero is purpose-built for high-scale microservice architectures with service discovery and inter-service communication; Gofasta targets the broader case of production-ready backend services — monoliths, modular monoliths, or services that may grow into microservices later — with a wider feature set (email, notifications, file storage, i18n, feature flags, deployment manifests) and no commitment to a specific architectural pattern.
vs. GoFrame: GoFrame is a comprehensive runtime toolkit originating from the Chinese Go community. It provides an HTTP server, ORM (gdb), configuration, logging, caching, i18n, and a CLI (gf) that can reverse-engineer database schemas into Go code. Its feature breadth is comparable to Gofasta’s, and it supports six database drivers. The key difference is architectural: GoFrame is a runtime toolkit — the HTTP server, ORM, config loader, and logger are all GoFrame-specific APIs that your application imports and runs inside. Replacing one component (swapping the ORM, or using a different config loader) means fighting the integration between modules. Gofasta generates standalone Go code that uses well-known community libraries (chi, GORM, slog) rather than toolkit-specific equivalents. Each pkg/* package can be replaced individually because it builds on standard Go interfaces — swapping pkg/cache does not require changing pkg/config or pkg/middleware. GoFrame’s English documentation is significantly weaker than its Chinese documentation, which limits adoption outside Chinese-speaking teams.
vs. Kratos: Kratos is a microservices toolkit from Bilibili that shares several design choices with Gofasta: both use Google Wire for compile-time dependency injection, and both enforce layered architecture with clean separation between transport, service, and data layers. The fundamental difference is the API definition workflow. Kratos requires Protocol Buffers for service definitions and generates both HTTP and gRPC transport from .proto files — this is powerful for teams that need dual-protocol support but adds significant ceremony for REST-only projects. Gofasta defines APIs in standard Go code with explicit route registration, which requires no schema language and no code generation step for the HTTP layer itself. Kratos provides first-class service discovery (etcd, Consul, Kubernetes) and is built for distributed microservice deployments; Gofasta provides first-class deployment automation (Docker, Kubernetes manifests, VPS deployment) and a wider set of application-level packages (email, notifications, file storage, i18n, feature flags) for teams building complete backend services rather than individual microservice components.
vs. Goa: Goa takes a design-first approach: developers describe their API using a Go DSL, and the goa CLI generates server handlers, client packages, and OpenAPI documentation from that single source of truth. This ensures documentation and code never drift apart — a genuine advantage for API-first organizations. Goa generates both HTTP and gRPC transport from one design, and its generated code is clean and idiomatic. However, Goa’s scope is limited to the API layer. It does not provide database access, migrations, authentication, caching, background jobs, email, file storage, or deployment manifests — the developer must assemble these independently. Gofasta covers the full backend stack, from database migrations and GORM repositories through authentication and background processing to deployment automation. Goa’s DSL also has a significant learning curve; Gofasta uses standard Go code for everything, so the developer’s existing Go knowledge applies directly.
vs. go-kit: go-kit is a pattern library for building microservices, providing a three-layer architecture (transport → endpoint → service) with pluggable middleware for logging, metrics, rate limiting, and circuit breaking. It is not a code generator or a scaffolding tool — it is a set of Go packages that embody microservice best practices. go-kit’s flexibility comes at the cost of extreme verbosity: even a simple service requires many files and significant boilerplate for transport encoding, endpoint wiring, and middleware composition. go-kit is in maintenance mode — the maintainer has stated it is “done” and no major features are planned. Gofasta eliminates the boilerplate that go-kit leaves to the developer while preserving the same architectural principles (explicit wiring, interface-based design, middleware composition). Where go-kit provides the patterns, Gofasta generates the code that implements them.
vs. Ent: Ent is a graph-based ORM and code generation tool from Meta that generates a complete type-safe data access layer from schema definitions in Go. It is not a direct competitor — it solves a different layer of the stack (data access rather than full backend tooling) — but teams evaluating Gofasta’s default choice of GORM will often consider Ent as an alternative. Ent’s strengths are compile-time query safety (no interface{} in query results), graph-based relationship traversal, first-class GraphQL integration via entgql, and a schema-level privacy layer for multi-tenant authorization. Gofasta uses GORM as its default ORM because GORM has broader database driver support (five drivers vs. Ent’s three), a larger community, and a lower learning curve for developers familiar with active-record patterns. Ent could be used in a Gofasta project by replacing the generated GORM repository implementations with Ent clients — the service and controller layers would remain unchanged because they depend on repository interfaces, not on GORM directly. This is the opt-out-defaults design in practice: the ORM is a swappable default, not a locked-in requirement.
24. Roadmap
Gofasta is under active development. The roadmap is organized by impact on production readiness for SaaS, enterprise, and platform workloads.
Near-term — Strengthening the Core
- Multi-tenancy (
pkg/tenant) — Row-level tenant isolation with middleware (extracts tenant from JWT/subdomain/header), GORM scopes (auto-filters every query bytenant_id), and tenant-aware cache key prefixing. This is the single highest-value addition for SaaS: every multi-customer product needs it, cross-tenant data leaks are catastrophic, and the plumbing is tedious to build correctly from scratch. - Audit logging (
pkg/audit) — GORM callback-based, append-only audit trail recording entity, field, old value, new value, actor, and timestamp. Required by regulated industries (fintech, healthcare, govtech) and expected by enterprise buyers. - Outgoing webhooks (
pkg/webhook) — Event registration, HMAC-signed HTTP delivery with exponential backoff retries, delivery logging, and dead-letter handling. Every SaaS eventually needs to notify external systems, and the reliability logic (idempotency, timeout handling, retry budgets) is genuinely hard to get right. - OAuth2 provider integration — Extend
pkg/authwith OAuth2 flows for Google, GitHub, and Microsoft. “Sign in with Google” is table stakes for any consumer-facing product; enterprise SSO (via OpenID Connect) is required for B2B sales. - Tenant-aware rate limiting — Extend
pkg/middlewarerate limiter with per-tenant keys and configurable tier limits (e.g., free plan: 100 req/min, pro plan: 1000 req/min). Prevents noisy-neighbor problems in multi-tenant deployments. - Pub/Sub (
pkg/pubsub) — Typed publish/subscribe with two backends: an in-process dispatcher for monoliths and a Redis Pub/Sub implementation for distributed deployments. Defines aPublisherandSubscriberinterface so developers can swap backends or replace the package entirely with a purpose-built library like Watermill. Covers the 80% of event-driven use cases (send welcome email after signup, update search index after edit, log analytics event after purchase) without introducing Kafka or NATS. - Generated observability instrumentation — Extend the scaffold generators to emit OpenTelemetry span creation at each layer boundary (controller, service, repository) in generated code. Since the instrumentation is generated Go code the developer owns, it can be customized, extended, or removed. This delivers zero-config distributed tracing for scaffolded resources without requiring a custom compiler or runtime — the tracing is visible in the source code, not injected at build time.
Medium-term — Developer Experience and Tooling
- Expand
pkg/resilience— Add bulkhead (concurrent request limits) and timeout patterns alongside existing retry and circuit breaker. - Expand
pkg/testutil— HTTP test client, response assertion helpers (AssertStatus,AssertJSON), fixture loading from JSON/YAML files, and database assertion helpers (AssertDBHas,AssertDBMissing). Additionally, scaffold generators should produce a_test.gofile per resource with the test database wired in via testcontainers, so every generated resource ships with a working integration test from day one. - Expand
pkg/observability— Additional Prometheus metrics:http_response_size_byteshistogram,db_query_duration_secondshistogram,cache_hits_total/cache_misses_totalcounters,queue_jobs_processed_totalcounter. gofasta g middlewaregenerator — Scaffold custom middleware with the correctfunc(http.Handler) http.Handlersignature, logging, and test file.- Interactive project creation wizard — Guide developers through database driver, auth strategy, and optional feature selection when running
gofasta new. - Plugin system — Allow community-contributed generators to be installed and invoked via the CLI.
- Service architecture diagram generation —
gofasta diagramcommand that parses route files, the DI container, and model relationships to generate a Mermaid or DOT architecture diagram. Output as SVG, Markdown, or embeddable in Swagger UI. Purely a CLI analysis tool with zero runtime cost. - Machine-readable route output — Extend
gofasta routeswith a--jsonflag for CI/CD integration, API gateway configuration, and tooling that consumes route definitions programmatically. - Secrets management —
gofasta secret set <KEY>command that encrypts secrets locally (.env.enc) for safe version control, with decryption at startup via a master key from the environment. Integration with cloud secret managers (AWS Secrets Manager, GCP Secret Manager) as optional deployment targets. The CLI manages the workflow; the project reads secrets through standard environment variables at runtime — no proprietary secrets API in application code. - PaaS adapter generators (
gofasta deploy init <tool>) — Generate the configuration file each popular self-hosted VPS PaaS expects so teams already running one can adopt gofasta without rebuilding their deploy pipeline:app.json+CHECKSfor Dokku,captain-definitionfor CapRover, a Coolify-flavored Compose file with domain labels, a Dokploy-flavored Compose file with Swarm deploy keys, andconfig/deploy.yml+.kamal/secrets.examplefor Kamal. Each adapter writes files todeployments/<tool>/(or the path the tool requires at repo root) and a localREADME.mdlisting the exact follow-up commands to run with that PaaS’s native tooling. Gofasta does not wrap these platforms’ CLIs or call their APIs — the generated files are standard, ownable config the developer commits and deploys with the PaaS itself. This makesgofasta deployexpandable to the broader open-source VPS ecosystem without turning gofasta into a PaaS competitor; the direct-SSHgofasta deploypath remains the default, zero-dependency option.
Long-term — Scale and Enterprise
- Admin dashboard generation — Generate a web-based admin panel for managing resources, viewing audit logs, and monitoring system health.
- OpenAPI-first code generation — Generate Go handlers, DTOs, and route registration from an existing OpenAPI specification (spec-first, complementing the current code-first Swagger generation).
- Multi-service project support — Monorepo layout with shared packages, per-service scaffolding, and cross-service type generation.
- IDE extensions — VS Code and GoLand extensions for running generators, viewing routes, and navigating the project structure from the editor.
- MongoDB support — First-class document-database driver as a peer to the existing SQL drivers. Because MongoDB is not a SQL dialect, this is a larger shift than adding another GORM driver:
pkg/configgains a parallelSetupMongo()alongsideSetupDB(), the scaffold generators emit BSON-tagged structs and collection/index setup in place of GORM models and SQL migrations, andgofasta migratebecomes an index-and-collection setup runner for Mongo projects. The intended client is the officialgo.mongodb.org/mongo-driver/v2(no ODM) so generated code stays plain, explicit, and swappable. Targeted at teams whose domain is a natural fit for document storage (flexible schemas, nested documents, event/log stores) without forcing them off gofasta’s scaffolding and generator ergonomics. - Kubernetes manifests — Generated
deployment.yaml,service.yaml, andingress.yamlscoped to the project’s configured database and cache backends, as an optional complement to the existing VPS deploy flow for teams running Kubernetes clusters. - Cloud deployment manifests —
gofasta deploy awsandgofasta deploy gcpcommands that generate Terraform or CDK definitions for ECS/Fargate, Cloud Run, or equivalent managed container services. Following the “generate then own” principle, these commands would produce infrastructure-as-code files the developer commits and manages — not a managed cloud platform.
Explicitly out of scope
- Custom compiler or runtime fork — Gofasta will never require a patched Go compiler, a custom build command, or a proprietary runtime. Projects compile with
go build, test withgo test, and work with every standard Go tool. This is a permanent design constraint. - Managed cloud platform — Gofasta is a toolkit, not a hosting service. Any future deployment automation will generate configuration files the developer owns and deploys to their own infrastructure. There is no Gofasta-hosted cloud, no per-seat pricing, and no telemetry phone-home.
- CQRS / Event Sourcing — These are architectural patterns, not toolkit concerns. They are deeply domain-specific, and a generic implementation is either too abstract to be useful or too opinionated to be swappable. Projects that need CQRS should use Watermill directly. A toolkit that provides CQRS pushes users toward a specific architecture, which conflicts with gofasta’s identity as composable, opt-out-default packages.
- Message broker abstraction — Abstracting over Kafka, NATS, and RabbitMQ is Watermill’s job. Gofasta’s
pkg/pubsubwill provide in-process and Redis-backed pub/sub; distributed messaging beyond Redis is left to purpose-built libraries. - SAML implementation — SAML is an extremely complex protocol. Enterprise SSO is better delegated to specialized providers (WorkOS, Auth0, Clerk). Gofasta will provide integration guides rather than a direct SAML implementation.
- Billing SDK wrapper — Billing is deeply product-specific. Wrapping Stripe’s SDK adds indirection without value. This will be addressed as a documentation guide showing the integration pattern, not as a package.
The roadmap is driven by community feedback. Feature requests and contributions are welcome via GitHub Issues and Pull Requests.
25. Contributing
Gofasta is open source under the MIT License .
Repositories:
- Library:
github.com/gofastadev/gofasta— imported by your projects - CLI tool:
github.com/gofastadev/cli— installed viago install github.com/gofastadev/cli/cmd/gofasta@latest - Documentation website:
github.com/gofastadev/website
How to contribute:
- Open an issue to discuss the change before starting work
- Fork the repository and create a feature branch
- Write tests for new functionality
- Submit a pull request with a clear description
Community:
- GitHub Issues for bug reports and feature requests
- GitHub Discussions for questions and ideas
References
- Go Developer Survey 2024 Results — The Go Blog, https://go.dev/blog/survey2024-h2-results
- Google Wire — Compile-time Dependency Injection for Go, https://github.com/google/wire
- GORM — The fantastic ORM library for Go, https://gorm.io
- go-chi/chi — Lightweight, idiomatic HTTP router for Go, https://github.com/go-chi/chi
- gqlgen — Go library for building GraphQL servers, https://gqlgen.com
- go-playground/validator — Struct and field validation for Go, https://github.com/go-playground/validator
- hibiken/asynq — Distributed task queue for Go, https://github.com/hibiken/asynq
- golang-migrate — Database migrations for Go, https://github.com/golang-migrate/migrate
- Casbin — Authorization library, https://casbin.org
- OpenTelemetry Go — Observability framework, https://opentelemetry.io/docs/languages/go/
- Air — Live reload for Go apps, https://github.com/air-verse/air
- Cobra — CLI library for Go, https://github.com/spf13/cobra
- swaggo/swag — Swagger documentation generator for Go, https://github.com/swaggo/swag
- failsafe-go — Fault tolerance for Go, https://github.com/failsafe-go/failsafe-go
- OpenFeature — Vendor-neutral feature flagging specification (CNCF), https://openfeature.dev
- testcontainers-go — Docker containers for integration testing, https://github.com/testcontainers/testcontainers-go
- unrolled/secure — HTTP security headers for Go, https://github.com/unrolled/secure
This document describes Gofasta as of version 4.0.0. For the latest documentation, visit gofasta.dev .
Copyright 2025–2026 Gofasta Authors. Released under the MIT License.