Generated Go applications you own
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
Start
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.
forj new, changed later as the App grows.forj new can install Atlas support for Codex, Claude Code, GitHub Copilot, and Gemini CLI, including project guidance, skills, and MCP context.$ forj newProject » photodropStarter kit » VueComponents » CLI, Docker, Mail, Auth, OAuth, Web API, Web UI, Metrics, Observability, Grafana, Database (MySQL), Scheduler, Jobs✔ Project render complete (created: 736, skipped: 0)$ cd photodrop && forj dev✔ 4/4 go build Built app in 2s· Running pre-dev setup[+] up 12/12✔ migrations complete (7)✔ Dev ready → App: http://localhost:3000 → Lighthouse: http://localhost:3000/lighthouse → Swagger: http://localhost:3000/swagger → Mailpit (inbox): http://localhost:8025 → Grafana (admin / admin): http://localhost:1300123:51:32.257 HTTP Starting HTTP server → addr=0.0.0.0:300023:51:32.256 Jobs Queue worker started → driver=redis · workers=3023:51:32.256 Scheduler Scheduler started$ curl localhost:3000/-/health{"status":"ok"}Infrastructure
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
// 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
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.
The stack
The foundation teams rebuild in every service - already built, already coherent. One runtime, one configuration story, one operational surface.
Thin controllers, route groups, and middleware over the web abstraction. Health, readiness, and Swagger included.
First-class CLI entry points with injected dependencies, not shell scripts around your binary.
Named, durable background work with typed payloads, retries, timeouts, and worker processes.
Typed facts with local-first fan-out. In-process today, NATS or Kafka when you need it.
Declarative recurring work with stable names, overlap control, and operator visibility.
Generated connections, named resources, per-driver migrations, and a built-in db shell.
Named accessors with explicit TTLs, locks, counters, and rate limits behind one contract.
Named disks for files and blobs. Local in development, object storage in production.
Fluent message composition with pluggable delivery: SMTP, Resend, Postmark, SES, and more.
Server-authoritative sessions, HttpOnly cookies, refresh rotation, reset and verification flows.
Prometheus-compatible metrics with bounded labels, plus execution records for every runtime.
A first-party operator view over routes, inspects, schedules, queues, cache, and storage.
Generators
Make commands create the file and the wiring: providers, routes, schedules, subscriptions. No annotations, no reflection container, no hidden registration.
--remove deletes the file and undoes the wiring the generator manages. --dry-run shows you first.$ 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 codeOperations
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.
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
$ 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:3000Standalone
$ forj app # → ./bin/app
one process: http + jobs + schedulerDistributed
$ forj api # → ./bin/app api
$ forj worker --queue media
$ forj schedulerforj route:list is the source of truth for the HTTP surface, not a scroll through startup logs.
/-/health and /-/ready ship generated, with token-gated structured diagnostics.
Prometheus-compatible series with bounded labels: route patterns, queue names, job names, schedule names.
Execution records for every request, job, schedule run, and command, browsable in a first-party operator UI.
Scale
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.
internal/. No RPC ceremony, no duplicated plumbing.marketplace without touching the rest.$ 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 workerTested foundation
A driver should not only compile - it should prove its behavior against the backend it claims to support.
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
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.
Fit
A framework should say who it serves and who it does not. Here is the honest version.
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.
For your next application
Runtime orchestration, explicit wiring, local-first drivers - and an optional Vue starter kit with auth, settings, and dashboard screens already shaped.
Quickstart →For your existing services
Queue, events, cache, storage, web, mail, scheduler, and more - standalone Go packages with their own APIs, drivers, and test suites.
Browse libraries →$ go install github.com/goforj/goforj/cmd/forj@latest
$ forj newUnreleased documentation@latest installs the latest tagged release.