
Golang Uber FX
FreeFramework for building long-running Go services with dependency injection.
Free · Opens the source repo
What Golang Uber FX does
Golang Uber FX is an application framework designed for building long-running services in Go, leveraging Uber's fx library for dependency injection (DI). It provides a structured way to manage application lifecycle, module composition, and dependency wiring, making it particularly useful for developers working on complex service architectures. Using FX, developers can define how components of their application interact and manage their lifecycle through hooks, ensuring a smooth startup and shutdown process.
The framework integrates a reflection-based DI container, built on top of uber-go/dig, which allows for easy registration of constructors and invocation of services. With features like lifecycle hooks, developers can efficiently handle application startup and shutdown sequences, ensuring that resources are properly allocated and released. This is especially critical for HTTP servers, workers, and other long-running processes where graceful shutdown and boot order are essential.
FX also introduces a module system that helps in organizing code into reusable components, allowing for better separation of concerns and easier maintenance. By grouping related services and their dependencies, developers can create a more manageable codebase. Additionally, the signal-aware run loop provided by FX allows applications to respond to system signals like SIGINT and SIGTERM, ensuring that they can shut down gracefully in response to external requests.
This skill is aimed at Go architects and developers who are building scalable and maintainable services using the uber-go/fx framework. It is particularly beneficial for those who need to manage complex dependency graphs and lifecycle events in their applications, making it a valuable addition to any Go developer's toolkit.
When to use it
Use this skill when developing long-running services in Go that require structured dependency injection and lifecycle management.
When not to use it
Avoid this skill for simple CLI applications or libraries that do not require a full DI framework or lifecycle management.
What you can build with it
Building a Web Server
Use Golang Uber FX to manage dependencies and lifecycle events for a web server, ensuring graceful startup and shutdown.
Creating Microservices
Leverage FX to wire together multiple microservices, managing their dependencies and lifecycle in a modular fashion.
Developing Background Workers
Implement background workers with FX to handle tasks that require reliable initialization and shutdown, using lifecycle hooks.
How to install Golang Uber FX
View source1. Install with the skills CLI
npx skills add samber/cc-skills-golang/golang-uber-fx --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by samberPersona: 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.
Using uber-go/fx for Application Wiring in Go
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.
go get go.uber.org/fx
fx vs. dig
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.
The Application
import "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.
Provide and Invoke
fx.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.
Lifecycle Hooks
Inject fx.Lifecycle and append hooks. Constructors should return quickly; long-running work belongs in OnStart.
func 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):
lc.Append(fx.StartStopHook(srv.Start, srv.Stop)) // matched pair
Parameter and Result Objects
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.
type 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
fx.Annotate wraps a constructor to add tags or interface bindings without a fx.Out struct. Prefer it for ergonomic name/group/As bindings:
fx.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"`),
),
)
Value Groups
Many constructors, one consumer slice — typical for routes, health checks, metrics collectors:
type 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
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.
var 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.
Best Practices
- Keep
main()thin — providers, modules, and a singleRun(). Push real work into modules so each can be tested in isolation. - Use lifecycle hooks instead of
init()or goroutines launched from constructors — Start/Stop ordering depends on graph topology, butinit()goroutines do not, which leads to races and leaks. - OnStart must return promptly — long work goes in a goroutine inside the hook. A blocking OnStart hangs the rest of the boot.
- Respect
ctx.Done()in hooks — a hook that ignores cancellation is reported as a timeout failure but its goroutine continues, leaking resources. - Group by module, not by layer — a module owns the providers, lifecycle, and decorators for one concern (HTTP, DB, metrics).
- Use
fx.Annotatefor tags rather than wrapping a constructor in anfx.Outstruct — keeps the constructor reusable outside fx. - Replace
fx.Providewithfx.Supplyfor pre-built values (config, command-line flags). Shorter, signals intent. - Validate the graph in CI by booting under
fx.New(...).Err()— catches missing providers and cycles before deploy.
Common Mistakes
| 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. |
Testing
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.
Further Reading
- advanced.md — Supply/Replace/Decorate, optional deps, custom event logging, manual lifecycle, full Quick Reference
- recipes.md — full HTTP service with database/metrics, background workers with graceful drain, multiple impls of the same interface, manual lifecycle for CLI embedding
- testing.md — fxtest patterns,
fx.Replace,fx.Populate, isolated lifecycle tests, CI graph validation
Cross-References
- → See
samber/cc-skills-golang@golang-uber-digskill for the underlying container,dig.In/dig.Out, and DI without lifecycle - → See
samber/cc-skills-golang@golang-dependency-injectionskill for DI concepts and library comparison - → See
samber/cc-skills-golang@golang-samber-doskill for a generics-based alternative without reflection - → See
samber/cc-skills-golang@golang-google-wireskill for compile-time DI (no runtime container) - → See
samber/cc-skills-golang@golang-structs-interfacesskill for interface design patterns - → See
samber/cc-skills-golang@golang-contextskill for context propagation in OnStart/OnStop hooks - → See
samber/cc-skills-golang@golang-testingskill for general testing patterns
If you encounter a bug or unexpected behavior in uber-go/fx, open an issue at https://github.com/uber-go/fx/issues.
Frequently asked questions about Golang Uber FX
Similar skills
Python PyPI Package Builder
Streamline the process of creating and publishing Python packages.
Minecraft Plugin Development
Streamline your Minecraft server plugin creation.
MCP Server Builder
Easily build .NET MCP servers with the latest standards.
CommunityToolkit.Mvvm Messenger
Decoupled communication for ViewModels in .NET applications.
MVVM Toolkit DI
Streamline ViewModel integration with Dependency Injection in .NET.
MCP Apps Builder
Essential guidelines for MCP server development.
