Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Golang application framework using uber-go/fx — fx.New, fx.Provide, fx.Invoke, fx.Module, fx.Lifecycle hooks, fx.Annotate (name/group/As), fx.Decorate, fx.Supply, fx.Replace, fx.WithLogger, and signal-aware Run(). Apply when using or adopting uber-go/fx, when the codebase imports `go.uber.org/fx`, or when wiring services with fx.New. For raw DI without lifecycle, see `samber/cc-skills-golang@golang-uber-dig` skill.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 326% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 115% | 0% |
| case-17 | ✗→✓ | ▲ Improved | -7% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 91% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 148% | 0% |
Persona: You are a Go architect building a long-running service with fx. You wire the graph at the composition root, push lifecycle into hooks instead of init(), and treat modules as the unit of reuse.
Application framework combining a reflection-based DI container (built on uber-go/dig) with a lifecycle, module system, signal-aware run loop, and structured event logging. For long-running services where boot order, graceful shutdown, and modular composition matter.
Official Resources:
This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill (godig) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc-skills-golang@golang-gopls skill (gopls). Context7 remains a fallback for docs not indexed on pkg.go.dev.
bashgo get go.uber.org/fx
fx is built on top of dig and shares the same reflection-based container engine. The DI primitives (Provide, Invoke, In/Out structs, named values, value groups) are identical — fx.In/fx.Out are re-exports of dig.In/dig.Out.
What fx adds on top:
| Concern | dig | fx | | --- | --- | --- | | DI container | ✅ dig.New() | ✅ (embedded) | | Lifecycle hooks | ❌ | ✅ fx.Lifecycle OnStart/OnStop | | Module system | ❌ | ✅ fx.Module with scoped decorators | | Signal-aware run loop | ❌ | ✅ app.Run() blocks on SIGINT/SIGTERM | | Structured event logging | ❌ | ✅ fx.WithLogger / fxevent | | Startup/shutdown timeout | ❌ | ✅ fx.StartTimeout / fx.StopTimeout |
Choose fx for long-running services (HTTP servers, workers, daemons) — lifecycle and signal handling are mandatory there, and modules make large service graphs manageable.
Choose raw dig when you need wiring without a framework: CLI tools, libraries that expose a container to callers, test harnesses, or embedding DI into an existing app that manages its own lifecycle. See samber/cc-skills-golang@golang-uber-dig skill.
goimport "go.uber.org/fx" app := fx.New( fx.Provide(NewLogger, NewDatabase, NewServer), fx.Invoke(RegisterRoutes), ) app.Run() // blocks until SIGINT/SIGTERM, then runs OnStop hooks
Boot stages: fx.New validates types (constructors do not run); app.Start(ctx) runs each fx.Invoke and fires OnStart hooks in topological order; main blocks on app.Done(); app.Stop(ctx) fires OnStop hooks in reverse order. Default timeout is 15 seconds — override with fx.StartTimeout / fx.StopTimeout.
gofx.New( fx.Provide(NewLogger, NewDatabase, NewServer), // lazy fx.Invoke(RegisterRoutes, StartMetricsExporter), // always run during Start )
fx.Provide registers constructors; fx.Invoke is the trigger — without an Invoke (directly or transitively) referencing a type, its constructor never runs.
Inject fx.Lifecycle and append hooks. Constructors should return quickly; long-running work belongs in OnStart.
gofunc NewHTTPServer(lc fx.Lifecycle, log *zap.Logger, cfg *Config) *http.Server { srv := &http.Server{Addr: cfg.Addr} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { ln, err := net.Listen("tcp", srv.Addr) if err != nil { return err } go srv.Serve(ln) // blocking work in a goroutine return nil }, OnStop: func(ctx context.Context) error { return srv.Shutdown(ctx) }, }) return srv }
Both callbacks receive a context bounded by StartTimeout/StopTimeout — respect cancellation. OnStart must return quickly — spawn a goroutine for blocking work; otherwise startup hangs and dependent hooks never fire.
fx.StartHook / fx.StopHook / fx.StartStopHook adapt simpler signatures (no context, no error, or both):
golc.Append(fx.StartStopHook(srv.Start, srv.Stop)) // matched pair
fx re-exports dig's dig.In / dig.Out as fx.In / fx.Out. Use them when a constructor has 4+ dependencies, or when you need name/group/optional tags.
gotype ServerParams struct { fx.In Logger *zap.Logger DB *sql.DB Cache *redis.Client `optional:"true"` Routes []http.Handler `group:"routes"` } func NewServer(p ServerParams) *Server { /* ... */ }
fx.Annotate wraps a constructor to add tags or interface bindings without a fx.Out struct. Prefer it for ergonomic name/group/As bindings:
gofx.Provide( fx.Annotate(NewPrimaryDB, fx.ResultTags(`name:"primary"`)), fx.Annotate(NewPostgresDB, fx.As(new(Database))), // expose interface fx.Annotate(NewUserHandler, fx.As(new(http.Handler)), fx.ResultTags(`group:"routes"`), ), )
Many constructors, one consumer slice — typical for routes, health checks, metrics collectors:
gotype RouteResult struct { fx.Out Handler http.Handler `group:"routes"` } type ServerParams struct { fx.In Routes []http.Handler `group:"routes"` }
Append ,flatten (group:"routes,flatten") to unwrap a slice instead of nesting it. Order is not guaranteed — provide an explicit ordered slice when sequence matters.
fx.Module groups providers, invokes, and decorators under a name. Modules scope decorators to themselves and their children — a logger renamed in fx.Module("db", ...) only appears renamed for code inside that module.
govar DatabaseModule = fx.Module("database", fx.Provide(NewConnection, NewUserRepository), fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("db") }), ) func main() { fx.New( fx.Provide(NewConfig, NewLogger), DatabaseModule, HTTPModule, ).Run() }
Treat each module as a small library that can be lifted into another app — its public surface is the types it Provides.
For fx.Supply/fx.Replace/fx.Decorate, optional deps, custom logging, manual lifecycle, and Quick Reference, see advanced.md.
main() thin — providers, modules, and a single Run(). Push real work into modules so each can be tested in isolation.init() or goroutines launched from constructors — Start/Stop ordering depends on graph topology, but init() goroutines do not, which leads to races and leaks.ctx.Done() in hooks — a hook that ignores cancellation is reported as a timeout failure but its goroutine continues, leaking resources.fx.Annotate for tags rather than wrapping a constructor in an fx.Out struct — keeps the constructor reusable outside fx.fx.Provide with fx.Supply for pre-built values (config, command-line flags). Shorter, signals intent.fx.New(...).Err() — catches missing providers and cycles before deploy.| Mistake | Fix | | --- | --- | | Long-running work directly in OnStart | Spawn a goroutine inside OnStart; the hook itself must return quickly so dependent hooks can run. | | fx.Provide something that should be fx.Supply | Pre-built values (config, secrets) belong in fx.Supply — clearer and avoids a no-op constructor. | | Module decorator leaking to siblings | Decorate inside fx.Module(...) — decorators flow only to descendants. A top-level fx.Decorate is global. | | Group order assumed | Groups are unordered. If order matters, provide an ordered slice from one constructor. | | Constructors with side effects | Side effects belong in OnStart — constructors should be cheap and pure-ish, since they may run concurrently and lazily. | | Forgotten fx.Invoke | Without an Invoke (or downstream consumer), constructors never run. Add at least one Invoke per app. |
Use go.uber.org/fx/fxtest to integrate fx with *testing.T (failures call t.Fatal, RequireStop registers as t.Cleanup). fx.Populate(&target) pulls values out of the graph; fx.Replace swaps real dependencies for fakes. Full patterns in testing.md.
fx.Replace, fx.Populate, isolated lifecycle tests, CI graph validationsamber/cc-skills-golang@golang-uber-dig skill for the underlying container, dig.In/dig.Out, and DI without lifecyclesamber/cc-skills-golang@golang-dependency-injection skill for DI concepts and library comparisonsamber/cc-skills-golang@golang-samber-do skill for a generics-based alternative without reflectionsamber/cc-skills-golang@golang-google-wire skill for compile-time DI (no runtime container)samber/cc-skills-golang@golang-structs-interfaces skill for interface design patternssamber/cc-skills-golang@golang-context skill for context propagation in OnStart/OnStop hookssamber/cc-skills-golang@golang-testing skill for general testing patternsIf you encounter a bug or unexpected behavior in uber-go/fx, open an issue at <https://github.com/uber-go/fx/issues>.
Other measured skills in the registry, with their headline benchmark lift.