Skip to Content
DocumentationWhite Paper

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

  1. Introduction — setup tax, what Gofasta is and is not, curated defaults
  2. Design Principles — including §2.7 Agent-Native by Default
  3. Architecture
  4. The CLI Tool
  5. The Gofasta Library
  6. Code Generation Strategy
  7. Dependency Injection
  8. Database Layer
  9. HTTP and API Layer
  10. Authentication and Authorization
  11. Security Defaults
  12. Background Processing
  13. Real-Time Communication
  14. Email and Notifications
  15. File Storage
  16. Internationalization
  17. Feature Flags
  18. Observability
  19. Resilience
  20. Testing
  21. Deployment
  22. Adoption and Migration Path
  23. Comparison with Existing Tools
  24. Roadmap
  25. 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:

  1. A CLI tool (github.com/gofastadev/cli) installed globally via go 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.

  2. A Go library (github.com/gofastadev/gofasta) containing multiple packages under pkg/. 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.

  3. A toolkit designed to be driven by AI coding agents as first-class users. Every scaffolded project ships with an AGENTS.md briefing, 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.txt and /llms-full.txt so 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. Each pkg/* 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 get their 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/cache without pkg/mailer, or pkg/resilience without pkg/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 --json flag. 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.md at 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 edit wire_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 for config.yaml by reflecting over the AppConfig type in the version of pkg/config pinned in the project’s go.mod. Invoked by gofasta config schema; callable directly as go 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.txt and /llms-full.txt following the llmstxt.org  spec. Agents asking “how does gofasta’s pkg/featureflag work?” 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>, and gofasta 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) → Database

Each 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 implementations
  • gqlgen.yml — gqlgen code generation configuration
  • /graphql and /graphql-playground HTTP 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 | sh

Requirement: 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

CommandDescription
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 initInitialize 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 doctorCheck system prerequisites (Go, migrate, Docker, air, wire, gqlgen, swag) and project health (config.yaml, database connectivity). Useful for bug reports.
gofasta upgradeSelf-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 versionPrint detailed version information including CLI version, Go runtime version, and OS/arch.

Development workflow

CommandDescription
gofasta devBring 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 serveStart the HTTP server (delegates to the project’s own serve command).
gofasta consoleLaunch an interactive Go REPL in the project directory using yaegi . Requires yaegi to be installed separately.
gofasta routesDisplay all registered API routes from app/rest/routes/ in a formatted table (method, path, source file). Structured JSON available via --json.
gofasta swaggerGenerate OpenAPI/Swagger documentation from code annotations via swag init.
gofasta wireRegenerate Wire dependency injection code.
gofasta verifyRun 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 statusReport 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 schemaEmit 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

CommandDescription
gofasta migrate upApply all pending SQL migrations.
gofasta migrate downRoll 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.

CommandDescription
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.

CommandDescription
gofasta deployDeploy 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 setupPrepare 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 statusCheck the status of the deployed application (current release, container/service state).
gofasta deploy logsTail application logs from the remote server (streams docker compose logs -f or journalctl -u <app> -f).
gofasta deploy rollbackRoll back to the previous release on the remote server.

Shell integration

CommandDescription
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

FlagDescription
-h, --helpShow help for the current command.
-v, --versionPrint the CLI version (machine-parseable — no banner).
--no-bannerSuppress 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.
--jsonEmit 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 myapp

This command:

  1. Creates the project directory with the full structure
  2. Runs go mod init
  3. Copies ~92 template files (REST-only by default)
  4. Installs dependencies (go mod tidy)
  5. Generates Wire dependency injection code
  6. Includes a starter User resource 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 + GraphQL

4.4 Scaffold Generation

gofasta g scaffold Product name:string price:float description:text active:bool

This 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 fields
  • app/di/wire.go — Adds provider set to Wire build
  • app/rest/routes/index.routes.go — Registers routes
  • cmd/serve.go — Wires controller into route configuration

Supported field types:

TypeGo TypePostgresMySQLSQLiteGraphQL
stringstringVARCHAR(255)VARCHAR(255)TEXTString
textstringTEXTLONGTEXTTEXTString
intintINTEGERINTINTEGERInt
floatfloat64DECIMAL(10,2)DECIMAL(10,2)REALFloat
boolboolBOOLEANTINYINT(1)INTEGERBoolean
uuiduuid.UUIDUUIDCHAR(36)TEXTID
timetime.TimeTIMESTAMPDATETIMEDATETIMEDateTime

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.md at 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 edit wire_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 the AppConfig type in the project’s pinned pkg/config version and emits the resulting JSON Schema (Draft 7). Invoked by gofasta config schema; callable directly as go run ./cmd/schema for 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 installed pkg/config.

CLI-level agent features:

  • Structured errors. Every gofasta error carries a stable machine-readable code, a one-line message, a remediation hint, and a link to the relevant docs page. In --json mode errors serialize as {code, message, hint, docs} — agents pattern-match on the code rather than regex-parsing English.
  • Universal --json flag. 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.txt and /llms-full.txt on 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.

PackagePurpose
pkg/configYAML configuration loading with environment variable overrides
pkg/loggerStructured logging via Go’s slog with configurable format and level
pkg/errorsTyped application errors mapped to HTTP status codes and GraphQL error presentation
pkg/modelsBase model with UUID, timestamps, soft delete, optimistic locking
pkg/typesCommon DTOs for pagination, sorting, API responses
pkg/httputilJSON binding, response helpers, error-returning handler adapter
pkg/middlewareCORS, rate limiting, logging, recovery, security headers, request ID, content-type validation
pkg/healthLiveness and readiness health check endpoints
pkg/authJWT generation/validation, Casbin RBAC, authentication and authorization middleware
pkg/encryptionAES-256-GCM encryption/decryption for data at rest
pkg/sessionCookie and filesystem session management (wraps gorilla/sessions)
pkg/cacheIn-memory and Redis caching with a unified interface
pkg/storageLocal filesystem and S3-compatible file storage
pkg/seedsDatabase seeding with ordered execution
pkg/mailerEmail delivery via SMTP, SendGrid, or Brevo with HTML template rendering
pkg/notifyMulti-channel notifications (email, SMS via Twilio, Slack webhooks, database)
pkg/websocketWebSocket hub with rooms, broadcast, and connection management
pkg/schedulerCron-based job scheduling with second-precision expressions
pkg/queueAsync task queue backed by Redis via hibiken/asynq
pkg/resilienceRetry policies and circuit breakers via failsafe-go
pkg/validatorsStruct validation via go-playground/validator/v10 with custom rules
pkg/i18nInternationalization with YAML locale files via go-i18n
pkg/observabilityPrometheus metrics and OpenTelemetry distributed tracing
pkg/featureflagFeature flag evaluation via the OpenFeature Go SDK with a swappable provider (in-memory, Flagd, LaunchDarkly, go-feature-flag, or custom)
pkg/utilsPagination math, string helpers, generic slice/map operations
pkg/testutil/testdbPostgreSQL 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-backed

The 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: json

Every 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 wireinject tag), 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 gofasta CLI 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.

GeneratorInputOutput
newProject name, module path, driver~78 files: full project structure
scaffoldResource name, field definitions11 new files + 4 patched files
modelResource name, field definitionsModel struct + migration pair
migrationMigration name, optional fieldsSQL migration pair (up/down)
repositoryResource name, field definitionsInterface + GORM implementation
serviceResource name, field definitionsInterface + implementation
controllerResource name, field definitionsREST controller with Swagger annotations
dtoResource name, field definitionsCreate/Update/Response DTOs
routeResource nameRoute registration function
resolverResource nameGraphQL schema + resolver stubs
providerResource nameWire provider set
jobJob name, optional cron expressionBackground job with scheduler registration
taskTask nameAsync task handler for asynq
email-templateTemplate nameResponsive HTML email template

6.3 Name Conventions

The CLI automatically converts resource names across naming conventions:

InputPascalCasesnake_casecamelCasePlural
ProductProductproductproductproducts
OrderItemOrderItemorder_itemorderItemorder_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.go shows 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.

DriverConfig valueConnection poolingNotes
PostgreSQLpostgresYesDefault. Full feature support including CITEXT, UUID generation via gen_random_uuid().
MySQLmysqlYesUTF-8 (utf8mb4), timezone-aware connections.
SQLitesqliteNoFile-based or :memory:. Suitable for development, testing, and embedded use.
SQL ServersqlserverYesMicrosoft SQL Server.
ClickHouseclickhouseYesColumn-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 BeforeCreate hook
  • TimestampsCreatedAt and UpdatedAt, managed automatically by GORM
  • Soft deleteDeletedAt field; deleted records are excluded from queries by default
  • Optimistic lockingRecordVersion field incremented on each update for concurrent write safety
  • Active flagIsActive and IsDeletable fields 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.sql

The 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 seed

9. 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.HandlerFunc

9.6 Middleware

The middleware package provides standard HTTP middleware using the func(http.Handler) http.Handler signature, composable via middleware.Chain():

MiddlewareFunction
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: myapp

Generated 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, user

The 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

ProtectionImplementationWhat it does
Security headersmiddleware.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.
CORSmiddleware.CORS(config)Configurable allowed origins, methods, and headers. Defaults to restrictive settings.
Rate limitingmiddleware.RateLimit(config)Token bucket algorithm, configurable per-IP limits and time window. Supports memory and Redis stores.
JWT authenticationauth.JWTAuth()HS256-signed tokens with configurable expiry. Refresh token support.
Role-based access controlauth.RBACMiddleware()Casbin-backed permission enforcement per route.
Password hashingbcryptPasswords are never stored in plaintext.
Encryption at restpkg/encryptionAES-256-GCM for sensitive data fields.
Request IDmiddleware.RequestID()Unique ID per request for audit trails and log correlation.
Panic recoverymiddleware.Recovery()Catches panics, logs the stack trace, returns 500 without exposing internals.
Content-type validationmiddleware.ContentTypeValidation()Rejects requests with unexpected Content-Type headers.
Input validationpkg/validatorsStruct-level validation on all incoming DTOs via go-playground/validator.
Soft deletesmodels.BaseModelImplRecords are marked deleted, never physically removed. Database triggers prevent deletion of non-deletable records.
Optimistic lockingmodels.BaseModelImplRecordVersion 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 STARTTLS
  • SendGridSender — SendGrid API v3
  • BrevoSender — 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:

ChannelBackendWhat it sends
EmailChannelDelegates to pkg/mailerEmail via SMTP, SendGrid, or Brevo
SMSChannelTwilio REST APISMS text messages
SlackChannelIncoming Webhook URLSlack messages
DatabaseChannelGORM (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:

BackendConfig valueImplementation
Local filesystemlocalStores files at a configured directory path. URL() returns the joined filesystem path.
S3-compatibles3AWS 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: true

Application 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:

MetricTypeLabelsDescription
http_requests_totalCountermethod, path, statusTotal HTTP requests processed
http_request_duration_secondsHistogrammethod, pathRequest latency distribution
http_requests_in_flightGaugeNumber 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))
EndpointPurposeChecks
/healthBasic livenessProcess is running, reports uptime
/health/liveLiveness probeProcess is alive
/health/readyReadiness probeDatabase 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 migrations

20.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 script

Cloud-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 database

gofasta 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 release

Two 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 default GITHUB_TOKEN.
  • Deploy (VPS) — on push to main or manual dispatch, runs gofasta deploy against the configured VPS. Requires DEPLOY_HOST, DEPLOY_SSH_KEY, and DEPLOY_PORT repository 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/middleware

Each 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:

  1. Delete the import of the package you want to replace (e.g., pkg/cache).
  2. go get your preferred alternative (e.g., github.com/eko/gocache).
  3. Update the Wire provider to construct your alternative implementation.
  4. Run gofasta wire to 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:

  1. Replace each pkg/* import with an alternative library or a local implementation.
  2. Remove github.com/gofastadev/gofasta from go.mod.
  3. 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

Capabilitymux/chi/Gin/Echogo-blueprintBuffaloGofasta
HTTP routingYesYesYesYes
Project scaffoldingNoYesYesYes
Resource code generationNoNoYesYes
Compile-time DINoNoNoYes (Wire)
Database migrationsNoNoYes (Pop)Yes (SQL files)
Multi-database supportN/AYesYes (Pop)Yes (5 drivers)
GraphQL supportNoNoNoYes (opt-in, gqlgen)
Background jobsNoNoYesYes
Email / notificationsNoNoNoYes
WebSocket supportNoNoNoYes
File storageNoNoNoYes (local, S3)
ObservabilityNoNoNoYes (Prometheus, OpenTelemetry)
Security middlewarePartialNoPartialYes (headers, CORS, rate limit, auth)
Deployment manifestsNoNoNoYes (Docker, CI/CD, VPS)
Standard go build / go testYesYesYesYes
Runtime wrapper requiredYesNoYesNo

vs. Backend Platforms and Full Toolkits

CapabilityEncorego-zeroGoFrameGofasta
HTTP routingYes (annotations)YesYesYes
Project scaffoldingYesYes (goctl)Yes (gf)Yes
Resource code generationNoYes (from .api files)Yes (from DB schemas)Yes (from CLI args)
Compile-time DINoNoNoYes (Wire)
Database migrationsYes (auto-provisioned)NoYesYes (SQL files)
Multi-database supportNo (PostgreSQL only)Yes (MySQL, PostgreSQL, MongoDB)Yes (6 drivers)Yes (5 drivers)
GraphQL supportNoNoNoYes (opt-in, gqlgen)
Background jobsYes (cron)NoYesYes
Pub/SubYes (cloud-agnostic)NoNoNo (roadmap)
Email / notificationsNoNoNoYes (SMTP, SendGrid, Brevo, SMS, Slack)
WebSocket supportNoNoYesYes
File storageYes (S3/GCS)NoNoYes (local, S3)
InternationalizationNoNoYesYes
Feature flagsNoNoNoYes
Resilience patternsNoYes (circuit breaker, load shedding)NoYes (retry, circuit breaker)
ObservabilityYes (auto-instrumented)Yes (OpenTelemetry)YesYes (Prometheus, OpenTelemetry)
Service discoveryYes (automatic)Yes (etcd, Consul, K8s)Yes (etcd, ZooKeeper)No
Security middlewarePartial (auth handler)Yes (JWT)YesYes (headers, CORS, rate limit, auth)
Health checksNoNoNoYes
Test utilitiesYes (auto test DB)NoNoYes (testcontainers)
Deployment manifestsNo (cloud-managed)NoNoYes (Docker, CI/CD, VPS)
VPS deployment commandNoNoNoYes
Auto infrastructure provisioningYesNoNoNo
Local dev dashboardYesNoNoYes
Standard go build / go testNo (custom compiler)YesYesYes
net/http middleware compatibleNoNo (custom middleware)PartialYes
Runtime wrapper requiredYes (custom runtime)YesYesNo
Custom syntax or annotationsYes (//encore:api)Yes (.api DSL)NoNo
API definition formatAnnotations in GoProprietary .api filesStandard GoStandard Go
Free and open sourcePartial (cloud is paid)YesYesYes

vs. Microservice Toolkits and Design-First Generators

Capabilitygo-kitKratosGoaGofasta
HTTP routingYes (transport layer)Yes (HTTP + gRPC)Yes (generated)Yes
Project scaffoldingNoYes (from protobuf)Yes (from Go DSL)Yes
Resource code generationNoYes (from .proto)Yes (from DSL)Yes (from CLI args)
Compile-time DINoYes (Wire)NoYes (Wire)
API definition formatGo interfacesProtocol BuffersGo DSLStandard Go
gRPC supportYesYes (first-class)Yes (generated)No
Multi-database supportN/AN/AN/AYes (5 drivers)
Database migrationsNoNoNoYes (SQL files)
Background jobsNoNoNoYes
Email / notificationsNoNoNoYes
Resilience patternsYes (circuit breaker)YesNoYes (retry, circuit breaker)
Service discoveryYes (etcd, Consul, DNS)Yes (etcd, Consul, K8s)NoNo
ObservabilityYes (OpenTracing)Yes (OpenTelemetry)NoYes (Prometheus, OpenTelemetry)
OpenAPI generationNoNoYes (from DSL)Yes (from code annotations)
Standard go build / go testYesYesYesYes
Runtime wrapper requiredNo (pattern library)YesNo (generated code)No
Deployment manifestsNoNoNoYes (Docker, CI/CD, VPS)
Actively maintainedMaintenance modeYesYesYes

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 by tenant_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/auth with 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/middleware rate 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 a Publisher and Subscriber interface 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.go file 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_bytes histogram, db_query_duration_seconds histogram, cache_hits_total / cache_misses_total counters, queue_jobs_processed_total counter.
  • gofasta g middleware generator — Scaffold custom middleware with the correct func(http.Handler) http.Handler signature, 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 generationgofasta diagram command 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 routes with a --json flag for CI/CD integration, API gateway configuration, and tooling that consumes route definitions programmatically.
  • Secrets managementgofasta 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 + CHECKS for Dokku, captain-definition for CapRover, a Coolify-flavored Compose file with domain labels, a Dokploy-flavored Compose file with Swarm deploy keys, and config/deploy.yml + .kamal/secrets.example for Kamal. Each adapter writes files to deployments/<tool>/ (or the path the tool requires at repo root) and a local README.md listing 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 makes gofasta deploy expandable to the broader open-source VPS ecosystem without turning gofasta into a PaaS competitor; the direct-SSH gofasta deploy path 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/config gains a parallel SetupMongo() alongside SetupDB(), the scaffold generators emit BSON-tagged structs and collection/index setup in place of GORM models and SQL migrations, and gofasta migrate becomes an index-and-collection setup runner for Mongo projects. The intended client is the official go.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, and ingress.yaml scoped 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 manifestsgofasta deploy aws and gofasta deploy gcp commands 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 with go 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/pubsub will 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 via go install github.com/gofastadev/cli/cmd/gofasta@latest
  • Documentation website: github.com/gofastadev/website

How to contribute:

  1. Open an issue to discuss the change before starting work
  2. Fork the repository and create a feature branch
  3. Write tests for new functionality
  4. Submit a pull request with a clear description

Community:

  • GitHub Issues for bug reports and feature requests
  • GitHub Discussions for questions and ideas

References

  1. Go Developer Survey 2024 Results — The Go Blog, https://go.dev/blog/survey2024-h2-results 
  2. Google Wire — Compile-time Dependency Injection for Go, https://github.com/google/wire 
  3. GORM — The fantastic ORM library for Go, https://gorm.io 
  4. go-chi/chi — Lightweight, idiomatic HTTP router for Go, https://github.com/go-chi/chi 
  5. gqlgen — Go library for building GraphQL servers, https://gqlgen.com 
  6. go-playground/validator — Struct and field validation for Go, https://github.com/go-playground/validator 
  7. hibiken/asynq — Distributed task queue for Go, https://github.com/hibiken/asynq 
  8. golang-migrate — Database migrations for Go, https://github.com/golang-migrate/migrate 
  9. Casbin — Authorization library, https://casbin.org 
  10. OpenTelemetry Go — Observability framework, https://opentelemetry.io/docs/languages/go/ 
  11. Air — Live reload for Go apps, https://github.com/air-verse/air 
  12. Cobra — CLI library for Go, https://github.com/spf13/cobra 
  13. swaggo/swag — Swagger documentation generator for Go, https://github.com/swaggo/swag 
  14. failsafe-go — Fault tolerance for Go, https://github.com/failsafe-go/failsafe-go 
  15. OpenFeature — Vendor-neutral feature flagging specification (CNCF), https://openfeature.dev 
  16. testcontainers-go — Docker containers for integration testing, https://github.com/testcontainers/testcontainers-go 
  17. 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.

Last updated on