Documentation previewThese docs are actively being built. Some pages may change as the framework and examples are finalized.
Skip to content

Generated Go applications you own

The composable stack for building with Go

One cohesive application model. Explicit dependency wiring. Local-first drivers. Production-ready primitives across the application stack.

Unreleased documentation@latest installs the latest tagged release

App-owned compositionGenerated code keeps application boundaries and extension points explicit.
Explicit RuntimesRun together locally, then split into production processes.
Swap drivers, not business logicMove between local and production infrastructure behind the same APIs.
The GoForj application forgeOne GoForj App composes frontend choices, libraries, infrastructure drivers, Atlas coding-agent support, and explicit Runtimes into Go source you own.RUNTIME GOFORJ CORE goforjgoforjgoforjgoforjgoforjBACKENDFRONTENDCORE LIBRARIESQUEUEEVENTSATLASSTORAGEDATABASECACHE

Start

A real application in two commands

forj new renders a complete Go project - the components you choose, nothing more. forj dev brings it alive. Built for Go developers shipping services, workers, CLIs, and full products.

A focused CLIAn API serviceWorkers and schedulesA full product with auth and Vue, React, or templ
  • Components are choices. Database, Cache, Events, File Storage, Background Jobs, auth, frontend, observability - picked at forj new, changed later as the App grows.
  • The structure is already there. Routes, wiring, lifecycle, configuration, and tests have a place before you write a line.
  • It runs before you configure anything. Local drivers back every primitive, so day one needs no cloud account and no docker-compose archaeology.
Agent-aware from the first renderforj new can install Atlas support for Codex, Claude Code, GitHub Copilot, and Gemini CLI, including project guidance, skills, and MCP context.

Infrastructure

Swap drivers, not business logic

Services depend on stable contracts. Configuration selects among the drivers compiled into the App, so a supported backend change leaves business logic alone.

Your service · the same file in every environment

go
// internal/photos/service.go
type Service struct {
	disk storage.Storage
}

func NewService(disk storage.Storage) *Service {
	return &Service{disk: disk}
}

func (s *Service) Store(ctx context.Context, in UploadInput) (Photo, error) {
	path := photoPath(in)
	if err := s.disk.WithContext(ctx).Put(path, in.Body); err != nil {
		return Photo{}, fmt.Errorf("store photo: %w", err)
	}
	return Photo{Path: path}, nil
}

Your environment · the only thing that changes

STORAGE_PHOTOS_DRIVER=local
DB_DRIVER=sqlite
CACHE_DRIVER=memory
QUEUE_DRIVER=workerpool
EVENTS_DRIVER=inproc
MAIL_DRIVER=log
0lines of Go changed
$ forj buildDriver support is compiled in, selection happens at runtime, and misconfiguration fails fast instead of failing quietly.

Every primitive works this way. Cache, storage, queue, events, database, mail - each runs on in-process or local drivers in a standalone binary, then swaps to real infrastructure in production. No code changes.

Generators

Generated code you own

Make commands create the file and the wiring: providers, routes, schedules, subscriptions. No annotations, no reflection container, no hidden registration.

  • Organized by package, not by file type. A feature's HTTP, CLI, queue, scheduler, and event entry points live beside the service that owns the work.
  • Reversible. --remove deletes the file and undoes the wiring the generator manages. --dry-run shows you first.
  • Readable output. Generated wiring is ordinary Go you can read, debug, and step through. If it would be embarrassing to look at, it does not ship.
one feature · four entry points
$ forj make:controller photos
$ forj make:job photos:thumbnail --queue media
$ forj make:schedule photos:digest --every 24h
$ forj make:subscriber photos:photo-uploaded

internal/photos/
├── controller.go                 # HTTP entry point
├── thumbnail_job.go              # queue entry point
├── digest_schedule.go            # scheduler entry point
├── photo_uploaded_subscriber.go  # event entry point
└── service.go                    # your workflow code

Operations

Run it your way. See everything it does

One binary hosts everything locally, or splits into explicit processes when production needs to scale. Build one artifact, then run the entry point your environment needs.

Your entire App is one file

Whatever shape you choose, forj build compiles it into one static binary: under 60 MB with everything enabled, nothing extra to install beside it.

Ships inside

HTTP serverQueue workersSchedulerCLI commandsMigrationsDriversHealth and metricsLighthouse UI
bin/app · everything
$ forj new  # components · all of them
$ forj build
$ ls -lh bin/app
-rwxr-xr-x  1 you  staff  57M  bin/app

$ ./bin/app
23:51:32.256 Scheduler  Scheduler started
23:51:32.256 Jobs       Queue worker started → workers=30
23:51:32.257 HTTP       Listening → addr=0.0.0.0:3000

Standalone

$ forj app  # → ./bin/app
one process: http + jobs + scheduler

Distributed

$ forj api  # → ./bin/app api
$ forj worker --queue media
$ forj scheduler

Route lists

forj route:list is the source of truth for the HTTP surface, not a scroll through startup logs.

Health and readiness

/-/health and /-/ready ship generated, with token-gated structured diagnostics.

Metrics

Prometheus-compatible series with bounded labels: route patterns, queue names, job names, schedule names.

Inspects and Lighthouse

Execution records for every request, job, schedule run, and command, browsable in a first-party operator UI.

Scale

Start with one App. Grow into many

Most products live their whole life as a single App - and that is the golden path. When a Project outgrows it, one command adds another runnable app in the same repo: shared code, separate wiring, separate binaries, separate scaling.

  • Apps are boundaries, not microservices. Named apps share one repo, one Go module, and everything under internal/. No RPC ceremony, no duplicated plumbing.
  • Each app deploys on its own terms. Its own binary, ports, wiring, and runtime identity in logs, metrics, and Lighthouse - scale marketplace without touching the rest.
  • Nothing changes until you need it. A single-App Project never pays for this. Multi-app is a fan-out path for larger systems, teams, and monorepos - not a new architecture to learn on day one.
one project · many apps
$ forj make:app marketplace
$ forj marketplace make:controller checkout
$ forj marketplace route:list
$ forj dev  # manages Apps listed in dev.apps

photodrop/
├── cmd/app/         # default app
├── cmd/marketplace/ # named app binary
├── app/marketplace/ # routes, commands, wiring
└── internal/        # shared behavior, one module

$ forj api             # → ./bin/app api
$ forj marketplace worker # → ./bin/marketplace worker

Tested foundation

Primitives that prove themselves

A driver should not only compile - it should prove its behavior against the backend it claims to support.

2,500+test functions across the first-party libraries
870+integration test runs against real backends in containers
49interchangeable drivers across queue, events, cache, storage, database, and mail
18standalone libraries, each useful without the framework

Driver suites run against Redis, Postgres, MySQL, NATS, Kafka, MinIO, SQS, and more through testcontainers and emulators. These numbers are generated from the repositories, not written by hand: see how they are counted →

Verified scenarios

Learn it by building it

Seven scenarios grow one small App from a single route to a fully observable system. Each ships only after it executes against the current templates - the tutorial cannot drift from the framework.

  1. 1JSON API route
  2. 2Cached profile
  3. 3File upload
  4. 4users.created event
  5. 5reports:generate job
  6. 6reports:daily schedule
  7. 7Runtime observability

Fit

Is GoForj for you?

A framework should say who it serves and who it does not. Here is the honest version.

Reach for GoForj when

  • You are building services, APIs, workers, schedulers, CLIs, or full products in Go.
  • You want the foundation every service repeats, wiring, queues, cache, auth, observability, built and tested before day one.
  • You want infrastructure to be a configuration decision instead of an architecture rewrite.

Reach for something else when

  • You want a thin router and nothing more. A minimal mux and hand-picked libraries will be lighter.
  • Your team rules out code generation. GoForj's model is rendered code you own, and that is not negotiable.
  • You are building a library, not an application. Use the standalone libraries instead.

If you outgrow it, you keep everything

A rendered App is ordinary Go: explicit wiring, readable files, standard modules. Stop running forj tomorrow and your application still builds, tests, and deploys. The framework earns its place in your workflow, not in your lock-in.

I love building in Go. I love how direct it feels, how simple it is to ship, and how long production services can stay understandable. But I got tired of rebuilding the same application foundation every time: commands, queues, schedules, cache, storage, mail, metrics, wiring, local dev. GoForj is the stack I wanted for complete Go applications: cohesive, explicit, compiled, and still recognizably Go.

Chris MilesCreator of GoForj