$npx -y skills add samber/cc-skills-golang --skill golang-uber-fxGolang 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
| 1 | **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. |
| 2 | |
| 3 | # Using uber-go/fx for Application Wiring in Go |
| 4 | |
| 5 | 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. |
| 6 | |
| 7 | **Official Resources:** |
| 8 | |
| 9 | - [pkg.go.dev/go.uber.org/fx](https://pkg.go.dev/go.uber.org/fx) |
| 10 | - [uber-go.github.io/fx](https://uber-go.github.io/fx/) |
| 11 | - [github.com/uber-go/fx](https://github.com/uber-go/fx) |
| 12 | |
| 13 | 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. |
| 14 | |
| 15 | ```bash |
| 16 | go get go.uber.org/fx |
| 17 | ``` |
| 18 | |
| 19 | ## fx vs. dig |
| 20 | |
| 21 | 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`. |
| 22 | |
| 23 | What fx adds on top: |
| 24 | |
| 25 | | Concern | dig | fx | |
| 26 | | --- | --- | --- | |
| 27 | | DI container | ✅ `dig.New()` | ✅ (embedded) | |
| 28 | | Lifecycle hooks | ❌ | ✅ `fx.Lifecycle` OnStart/OnStop | |
| 29 | | Module system | ❌ | ✅ `fx.Module` with scoped decorators | |
| 30 | | Signal-aware run loop | ❌ | ✅ `app.Run()` blocks on SIGINT/SIGTERM | |
| 31 | | Structured event logging | ❌ | ✅ `fx.WithLogger` / `fxevent` | |
| 32 | | Startup/shutdown timeout | ❌ | ✅ `fx.StartTimeout` / `fx.StopTimeout` | |
| 33 | |
| 34 | **Choose fx** for long-running services (HTTP servers, workers, daemons) — lifecycle and signal handling are mandatory there, and modules make large service graphs manageable. |
| 35 | |
| 36 | **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. |
| 37 | |
| 38 | ## The Application |
| 39 | |
| 40 | ```go |
| 41 | import "go.uber.org/fx" |
| 42 | |
| 43 | app := fx.New( |
| 44 | fx.Provide(NewLogger, NewDatabase, NewServer), |
| 45 | fx.Invoke(RegisterRoutes), |
| 46 | ) |
| 47 | app.Run() // blocks until SIGINT/SIGTERM, then runs OnStop hooks |
| 48 | ``` |
| 49 | |
| 50 | 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`. |
| 51 | |
| 52 | ## Provide and Invoke |
| 53 | |
| 54 | ```go |
| 55 | fx.New( |
| 56 | fx.Provide(NewLogger, NewDatabase, NewServer), // lazy |
| 57 | fx.Invoke(RegisterRoutes, StartMetricsExporter), // always run during Start |
| 58 | ) |
| 59 | ``` |
| 60 | |
| 61 | `fx.Provide` registers constructors; `fx.Invoke` is the trigger — without an Invoke (directly or transitively) referencing a type, its constructor never runs. |
| 62 | |
| 63 | ## Lifecycle Hooks |
| 64 | |
| 65 | Inject `fx.Lifecycle` and append hooks. Constructors should return quickly; long-running work belongs in `OnStart`. |
| 66 | |
| 67 | ```go |
| 68 | func NewHTTPServer(lc fx.Lifecycle, log *zap.Logger, cfg *Config) *http.Server { |
| 69 | srv := &http.Server{Addr: cfg.Addr} |
| 70 | |
| 71 | lc.Append(fx.Hook{ |
| 72 | OnStart: func(ctx context.Context) error { |
| 73 | ln, err := net.Listen("tcp", srv.Addr) |
| 74 | if err != nil { return err } |
| 75 | go srv.Serve(ln) // blocking work in a goroutine |
| 76 | return nil |
| 77 | }, |
| 78 | OnStop: func(ctx context.Context) error { |
| 79 | return srv.Shutdown(ctx) |
| 80 | }, |
| 81 | }) |
| 82 | return srv |
| 83 | } |
| 84 | ``` |
| 85 | |
| 86 | Both call |