One cohesive application model. Explicit dependency wiring. Local-first drivers. Production-ready primitives across the application stack.
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 or 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
The same rule applies across the app. Cache, storage, queues, events, databases, and mail run on in-process or local drivers, then move to production infrastructure without changing service code.
The stack
The routes, workers, schedules, storage, cache, configuration, and operational tools teams rebuild in every service - already connected and ready to use.
Thin controllers, route groups, and middleware over the web abstraction. Health, readiness, and an OpenAPI reference 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.
Named database connections, migrations for each selected driver, and a built-in database 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 you choose to include, forj build compiles it into one static binary, with 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 … 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
$ ./bin/app
one process: http + jobs + schedulerDistributed
$ ./bin/app api
$ ./bin/app worker --queue media
$ ./bin/app schedulerforj route:list lists every registered HTTP route, so startup logs do not have to serve as route documentation.
/-/health and /-/ready are included, 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 stay in one app. When a Project needs another deployment or scaling boundary, one command adds another runnable app in the same repo: shared code, separate wiring, and separate binaries.
internal/. No RPC ceremony, no duplicated plumbing.admin without touching the rest.$ forj make:app admin
$ forj admin make:controller users
$ forj admin route:list
$ forj dev # manages apps listed in dev.apps
photodrop/
├── cmd/app/ # default app
├── cmd/admin/ # additional app binary
├── app/admin/ # routes, commands, wiring
└── internal/ # shared behavior, one module
$ forj api
$ forj admin 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, keeping the tutorial aligned with the framework.
Fit
A framework should say who it serves and who it does not. Here is the honest version.
A GoForj 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
Explicit process management and dependency wiring, local-first drivers, and an optional Vue starter kit with auth, settings, and dashboard screens ready to extend.
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.