
Golang Google Wire
- 31.6k installs
- 2.8k repo stars
- Updated July 27, 2026
- samber/cc-skills-golang
google/wire is a code-generation based Go DI toolkit that resolves the graph at compile time.
About
google/wire is a code-generation based dependency injection toolkit for Go. It resolves the dependency graph at compile time and emits plain Go constructor calls, eliminating runtime reflection overhead. Use it when you want compile-time safety and predictability, with generated wire_gen.go files as committed source.
- Compile-time DI via code generation - no runtime container
- Errors caught at wire./... time instead of runtime
- Explicit interface bindings prevent graph ambiguity
Golang Google Wire by the numbers
- 31,572 all-time installs (skills.sh)
- +423 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #33 of 4,386 Backend & APIs skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
golang-google-wire capabilities & compatibility
- Capabilities
- compile time wiring · code generation · interface binding
- Platforms
- macOS · Windows · Linux
- Runs
- Runs locally
- Pricing
- Free
npx skills add https://github.com/samber/cc-skills-golang --skill golang-google-wireAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 31.6k |
|---|---|
| repo stars | ★ 2.8k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | samber/cc-skills-golang ↗ |
Why does google wire cause redeclared compile errors?
Implement compile-time dependency injection in Go with code generation and zero runtime overhead.
Who is it for?
Projects prioritizing compile-time safety over runtime flexibility; embedded systems with tight constraints
Skip if: Applications needing dynamic wiring or lazy initialization
When should I use this skill?
You need compile-time DI with zero runtime overhead and explicit interface bindings
What you get
Wire injector files with wireinject build tags, provider sets, and clean wire_gen.go codegen builds.
- Wire injector files with build tags
- Provider set definitions and wire_gen.go
Files
Persona: You are a Go architect using wire for compile-time DI. You let the compiler catch missing dependencies, treat wire_gen.go as committed source, and re-run wire ./... after every graph change.
Dependencies:
- wire:
go install github.com/google/wire/cmd/wire@latest
Using google/wire for Compile-Time Dependency Injection in Go
Code-generation DI toolkit. Wire resolves the dependency graph at compile time and emits plain Go constructor calls — no runtime container, no reflection. Errors appear when you run wire ./..., not at first request.
Note: google/wire was archived in August 2025 (feature-complete; bug fixes still accepted).
Official Resources: pkg.go.dev · github.com/google/wire · User Guide · Best Practices
This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.
go get -tool github.com/google/wire/cmd/wire@latest
go get github.com/google/wirewire vs. Runtime DI
| Concern | wire | dig / fx / samber/do |
|---|---|---|
| Resolution | Compile time (codegen) | Runtime (reflection) |
| Error detection | wire ./... fails | First Invoke/startup |
| Runtime container | None — plain Go calls | Present |
| Lifecycle hooks | Not built in | fx: OnStart/OnStop |
| Generated files | wire_gen.go (committed) | None |
For lifecycle, lazy loading, and a full matrix see samber/cc-skills-golang@golang-dependency-injection.
Providers
A provider is any Go function — inputs are dependencies, outputs are provided types. Three return forms:
func NewConfig() *Config { return &Config{Addr: ":8080"} }
func NewDB(cfg *Config) (*sql.DB, error) { return sql.Open("postgres", cfg.DSN) }
func NewRedis(cfg *Config) (*redis.Client, func(), error) { // cleanup chained in reverse order
c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
return c, func() { c.Close() }, nil
}Provider Sets
wire.NewSet groups providers for reuse. Sets can reference other sets.
// infra/wire.go
var InfraSet = wire.NewSet(
NewConfig,
NewDB,
NewRedis,
)
// service/wire.go
var ServiceSet = wire.NewSet(
NewUserRepo,
NewUserService,
wire.Bind(new(UserStore), new(*UserRepo)), // interface binding
)Keep sets small: library sets expose a stable surface (adding inputs or removing outputs breaks downstream injectors). One set per package is a useful default.
Injectors and //go:build wireinject
The injector file declares the initialization function. Wire generates its body into wire_gen.go and replaces the stub.
//go:build wireinject
package main
import "github.com/google/wire"
// Wire generates the body of this function.
func InitApp() (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil // replaced by codegen
}The //go:build wireinject tag prevents the stub from being compiled into the binary — only wire_gen.go (which has no such tag) makes it through go build. Without this tag, both files define the same function, causing a compile error.
Alternative syntax when a dummy return is inconvenient:
func InitApp() (*App, func(), error) {
panic(wire.Build(InfraSet, ServiceSet, NewApp))
}Interface Bindings
Wire forbids implicit interface satisfaction — you must declare bindings explicitly so the graph is unambiguous when multiple types implement the same interface.
var Set = wire.NewSet(
NewPostgresUserRepo,
wire.Bind(new(UserStore), new(*PostgresUserRepo)), // tell wire: *PostgresUserRepo satisfies UserStore
)Explicit bindings prevent graph breakage when a new type implementing the same interface is added elsewhere.
Struct Providers and Values
wire.Struct fills struct fields from the graph without a manual constructor. Tag fields wire:"-" to exclude them.
wire.Struct(new(Server), "Logger", "DB") // inject named fields
wire.Struct(new(Server), "*") // inject all non-excluded fields
wire.Value(Foo{X: 42}) // constant expression (no fn calls / channels)
wire.InterfaceValue(new(io.Reader), os.Stdin) // interface-typed literal
wire.FieldsOf(new(Config), "DSN", "Addr") // promote struct fields as graph nodesSee advanced.md for the wire:"-" exclusion tag and wire.FieldsOf details.
Disambiguating Duplicate Types
Wire forbids two providers for the same type. Wrap the underlying type in distinct named types so each has exactly one provider:
type PrimaryDSN string
type ReplicaDSN stringFull Application Example
// wire.go — injector, excluded from binary via build tag
//go:build wireinject
package main
func InitApp() (*App, func(), error) {
wire.Build(config.ConfigSet, infra.InfraSet, service.ServiceSet, NewApp)
return nil, nil, nil
}
// main.go
func main() {
app, cleanup, err := InitApp()
if err != nil { log.Fatal(err) }
defer cleanup()
app.Run()
}Wire generates wire_gen.go (plain Go, committed, DO NOT EDIT). For a full example with per-package sets, cleanup-heavy graphs, and generated output, see recipes.md.
Codegen Workflow
wire ./... # regenerate all injectors in the module
wire check ./... # validate graph without regenerating (fast CI check)Run wire ./... after every constructor signature change. Add //go:generate go run github.com/google/wire/cmd/wire to injector files so go generate ./... also works. Commit wire_gen.go — it must stay in sync for CI builds.
Best Practices
1. Never edit wire_gen.go — it is overwritten on every wire ./... run. Treat it as a build artifact that happens to be committed; source of truth is the provider and injector files. 2. Always add //go:build wireinject to injector files — omitting it causes duplicate-symbol compile errors because both the stub and the generated file define the same function. 3. Use named types to distinguish values of the same underlying type — wire enforces one provider per type; named types like type DSN string let you have PrimaryDSN and ReplicaDSN coexist. 4. Keep library provider sets minimal and backward-compatible — adding new required inputs breaks downstream injectors; removing outputs does too. Introduce only newly-created types in the same release. 5. Return (T, func(), error) from cleanup providers and let wire chain them — wire generates the correct reverse-order cleanup and handles partial failures (if construction fails midway, only already-built cleanups run). 6. Keep injector files focused — one function per file, one package import at a time. Fat injectors with dozens of wire.Build arguments are hard to reason about; delegate to per-package sets.
Common Mistakes
| Mistake | Fix |
|---|---|
Editing wire_gen.go manually | Never edit it. Change providers or injectors and re-run wire ./.... |
Missing //go:build wireinject | Add the tag as the very first line of every injector file. |
Two providers returning *sql.DB | Wrap with a named struct type: type PrimaryDB struct { *sql.DB } — Wire does not distinguish pointer type aliases. |
Injecting an interface without wire.Bind | Add wire.Bind(new(MyInterface), new(*MyImpl)) to the provider set. |
Forgetting to re-run wire ./... after changes | Run wire before go build; add it to go generate or a Makefile target. |
Calling cleanup() without guarding for nil | Wire returns nil cleanup on construction error; guard with if cleanup != nil { defer cleanup() }. |
Testing
Wire generates plain Go constructors, so unit tests use manual injection — no container to clone or reset. For testing patterns (test injectors swapping real providers for fakes, CI stale-check for wire_gen.go), see testing.md.
Further Reading
- advanced.md — cleanup chains, multiple injectors, set nesting, error catalogue, codegen flags, quick reference
- recipes.md — HTTP server, multi-injector build, cleanup-heavy graph, CLI embedding
- testing.md — test injectors, fake bindings, CI stale check
Cross-References
- → See
samber/cc-skills-golang@golang-dependency-injectionskill for DI concepts and library comparison - → See
samber/cc-skills-golang@golang-uber-digskill for runtime reflection-based DI without lifecycle - → See
samber/cc-skills-golang@golang-uber-fxskill for runtime DI with lifecycle hooks, modules, and signal-aware Run() - → See
samber/cc-skills-golang@golang-samber-doskill for generics-based DI without reflection - → See
samber/cc-skills-golang@golang-structs-interfacesskill for interface design patterns - → See
samber/cc-skills-golang@golang-testingskill for general testing patterns
If you encounter a bug or unexpected behavior in google/wire, open an issue at <https://github.com/google/wire/issues>.
[
{
"id": 1,
"name": "build-constraint-on-injector",
"description": "Tests that the model adds //go:build wireinject to injector files to prevent duplicate-symbol compile errors",
"prompt": "I'm setting up google/wire in my Go project. Here's my injector file:\n\n```go\npackage main\n\nimport \"github.com/google/wire\"\n\nfunc InitApp() (*App, error) {\n wire.Build(InfraSet, ServiceSet, NewApp)\n return nil, nil\n}\n```\n\nWire generates wire_gen.go successfully, but when I run `go build`, I get a 'redeclared in this block' compile error for InitApp. What's wrong and how do I fix it?",
"trap": "Without the skill, the model may suggest renaming the function, reorganizing packages, or not identify that the missing //go:build wireinject tag is the cause — both the stub and wire_gen.go define InitApp, causing the duplicate.",
"assertions": [
{"id": "1.1", "text": "Identifies the missing //go:build wireinject build tag as the root cause"},
{"id": "1.2", "text": "Shows //go:build wireinject as the first line of the injector file"},
{"id": "1.3", "text": "Explains that the tag prevents the stub from being compiled into the binary (only wire_gen.go compiles)"},
{"id": "1.4", "text": "Does NOT suggest renaming the function or reorganizing packages as the fix"},
{"id": "1.5", "text": "Does NOT suggest deleting wire_gen.go as the fix"}
]
},
{
"id": 2,
"name": "interface-binding-required",
"description": "Tests that wire.Bind is required for interface-to-concrete mappings and cannot be inferred",
"prompt": "I have this Go code using google/wire:\n\n```go\n// repo.go\ntype UserStore interface {\n GetUser(id int64) (*User, error)\n}\n\ntype PostgresUserRepo struct{ db *sql.DB }\nfunc (r *PostgresUserRepo) GetUser(id int64) (*User, error) { ... }\nfunc NewUserRepo(db *sql.DB) *PostgresUserRepo { return &PostgresUserRepo{db: db} }\n\n// service.go\nfunc NewUserService(store UserStore) *UserService { return &UserService{store: store} }\n\n// wire_providers.go\nvar AppSet = wire.NewSet(NewDB, NewUserRepo, NewUserService)\n```\n\nWhen I run `wire ./...` I get: `no provider found for UserStore`. NewUserRepo returns *PostgresUserRepo which clearly implements UserStore. Why doesn't wire figure this out?",
"trap": "Without the skill, the model might suggest wire should automatically resolve the interface, or suggest wrapping NewUserRepo to return UserStore directly, missing the explicit wire.Bind requirement.",
"assertions": [
{"id": "2.1", "text": "Explains that wire never auto-resolves interface satisfaction — bindings must be explicit"},
{"id": "2.2", "text": "Shows wire.Bind(new(UserStore), new(*PostgresUserRepo)) added to the provider set"},
{"id": "2.3", "text": "Places wire.Bind inside the same wire.NewSet (or adds it to a set in wire.Build)"},
{"id": "2.4", "text": "Explains WHY wire requires explicit bindings (predictability — avoids surprise rebinding when new implementations are added)"},
{"id": "2.5", "text": "Does NOT suggest changing NewUserRepo to return UserStore directly as the primary fix"}
]
},
{
"id": 3,
"name": "duplicate-type-named-wrapper",
"description": "Tests the named-type pattern to disambiguate multiple values of the same underlying type",
"prompt": "I'm building a Go service with google/wire. I need to inject two database connection strings — one for the primary database and one for a read replica. I tried this:\n\n```go\nfunc NewPrimaryDSN() string { return os.Getenv(\"PRIMARY_DSN\") }\nfunc NewReplicaDSN() string { return os.Getenv(\"REPLICA_DSN\") }\n\nvar DBSet = wire.NewSet(NewPrimaryDSN, NewReplicaDSN, NewPrimaryDB, NewReplicaDB)\n```\n\nWire complains about multiple bindings for string. How should I structure this?",
"trap": "Without the skill, the model might suggest using wire.Value or provider arguments, or use a config struct — missing the idiomatic named-type wrapper pattern that wire's own docs recommend.",
"assertions": [
{"id": "3.1", "text": "Introduces distinct named types (e.g., type PrimaryDSN string and type ReplicaDSN string)"},
{"id": "3.2", "text": "Updates NewPrimaryDSN to return PrimaryDSN and NewReplicaDSN to return ReplicaDSN"},
{"id": "3.3", "text": "Updates NewPrimaryDB and NewReplicaDB signatures to accept the named types"},
{"id": "3.4", "text": "Explains that wire enforces one provider per type, so distinct named types are the correct solution"},
{"id": "3.5", "text": "Does NOT suggest using a single Config struct with both DSNs as the primary fix (that avoids the problem rather than solving it with named types)"}
]
},
{
"id": 4,
"name": "cleanup-signature",
"description": "Tests the (T, func(), error) cleanup provider pattern instead of manual defer in main",
"prompt": "I'm using google/wire to wire my Go service. I need my *sql.DB connection pool to be closed when the app shuts down. Currently I'm doing this in main:\n\n```go\nfunc main() {\n app, err := InitApp()\n if err != nil { log.Fatal(err) }\n defer db.Close() // but I don't have access to db here!\n app.Run()\n}\n```\n\nI realize I need the DB closed on shutdown, but InitApp() only returns *App. How should I wire cleanup with google/wire?",
"trap": "Without the skill, the model might suggest passing db out of InitApp as a second return value, or storing it as a global, missing the (T, func(), error) cleanup provider pattern.",
"assertions": [
{"id": "4.1", "text": "Changes NewDB to return (*sql.DB, func(), error) where the cleanup function calls db.Close()"},
{"id": "4.2", "text": "Changes the injector function to return (*App, func(), error) to propagate the cleanup chain"},
{"id": "4.3", "text": "Shows main calling defer cleanup() after the nil-check"},
{"id": "4.4", "text": "Explains that wire chains cleanup functions and calls them in reverse construction order"},
{"id": "4.5", "text": "Does NOT suggest passing db as an extra return value from InitApp alongside *App"}
]
},
{
"id": 5,
"name": "no-edit-wire-gen",
"description": "Tests that the model never edits wire_gen.go and instructs re-running wire ./... instead",
"prompt": "I added a new *Logger parameter to my NewServer constructor in my google/wire project:\n\n```go\nfunc NewServer(db *sql.DB, log *zap.Logger) *Server { ... }\n```\n\nNow `go build` fails with 'too few arguments in call to NewServer'. The error is inside wire_gen.go on line 47. Should I edit wire_gen.go to add the logger argument there, or is there another way?",
"trap": "Without the skill, a model may suggest editing wire_gen.go directly to 'fix' the build error quickly, which would be overwritten on the next wire run.",
"assertions": [
{"id": "5.1", "text": "Explicitly says NOT to edit wire_gen.go (it is always overwritten)"},
{"id": "5.2", "text": "Instructs running wire ./... to regenerate wire_gen.go"},
{"id": "5.3", "text": "Explains that *zap.Logger must be provided in the graph (either via a provider or wire.Value)"},
{"id": "5.4", "text": "Shows how to add NewLogger (or wire.Value) to the appropriate wire.NewSet so the dependency is satisfied"},
{"id": "5.5", "text": "Does NOT present editing wire_gen.go as an option"}
]
},
{
"id": 6,
"name": "provider-set-organization",
"description": "Tests per-package provider set organization instead of one giant set in main",
"prompt": "My Go service using google/wire is growing. I currently have everything in one place:\n\n```go\n// wire.go\n//go:build wireinject\n\nfunc InitApp() (*App, func(), error) {\n wire.Build(\n NewConfig, NewDB, NewCache, NewLogger,\n NewUserRepo, NewOrderRepo, NewProductRepo,\n wire.Bind(new(UserStore), new(*PostgresUserRepo)),\n wire.Bind(new(OrderStore), new(*PostgresOrderRepo)),\n wire.Bind(new(ProductStore), new(*PostgresProductRepo)),\n NewUserService, NewOrderService, NewProductService,\n NewHTTPServer, NewRouter,\n NewApp,\n )\n return nil, nil, nil\n}\n```\n\nThis is getting unwieldy. How should I organize this with google/wire?",
"trap": "Without the skill, the model may just split the providers into helper variables in the same package, missing the idiomatic per-package wire.NewSet pattern.",
"assertions": [
{"id": "6.1", "text": "Introduces per-package wire.NewSet variables (e.g., InfraSet, RepoSet, ServiceSet, TransportSet)"},
{"id": "6.2", "text": "Each set lives in its own package's wire.go file (not all in main)"},
{"id": "6.3", "text": "The injector wire.Build references the set variables rather than individual providers"},
{"id": "6.4", "text": "wire.Bind declarations move into the relevant package's set (not into wire.Build directly)"},
{"id": "6.5", "text": "Explains the benefit: per-package sets are independently composable and keep the injector readable"}
]
},
{
"id": 7,
"name": "injector-parameter-vs-value-provider",
"description": "Tests using wire.Value or injector parameters for pre-built values instead of wrapper constructors",
"prompt": "In my Go app using google/wire, I parse a *Config struct from command-line flags in main() before calling InitApp. I tried writing a no-op provider:\n\n```go\nvar parsedCfg *Config\n\nfunc ProvideConfig() *Config { return parsedCfg }\n\nvar AppSet = wire.NewSet(ProvideConfig, ...)\n```\n\nThis works but feels wrong — I'm using a global variable. Is there a cleaner way to pass a pre-built *Config into the wire graph?",
"trap": "Without the skill, the model might suggest keeping the global variable pattern or using init(), missing both wire.Value and the injector-parameter patterns.",
"assertions": [
{"id": "7.1", "text": "Shows the injector-parameter approach: func InitApp(cfg *Config) (*App, func(), error) with wire.Build"},
{"id": "7.2", "text": "OR shows wire.Value(cfg) inside wire.Build — both are valid answers"},
{"id": "7.3", "text": "Explains that injector parameters are treated as pre-built providers by wire"},
{"id": "7.4", "text": "Does NOT use a global variable as the recommended solution"},
{"id": "7.5", "text": "Does NOT suggest using init() to set the value"}
]
},
{
"id": 8,
"name": "fields-of-struct",
"description": "Tests wire.FieldsOf to expose struct fields as individual graph nodes",
"prompt": "I have a single Config struct in my Go app with google/wire:\n\n```go\ntype Config struct {\n DatabaseDSN string\n CacheAddress string\n APIKey string\n}\n\nfunc NewConfig() *Config { return loadFromEnv() }\n```\n\nNewDB needs a DatabaseDSN string, NewCache needs a CacheAddress string, NewExternalClient needs an APIKey string — but all three are plain strings. How do I make these available to the wire graph without creating three separate provider functions?",
"trap": "Without the skill, the model will suggest three named-type wrappers or three extraction functions, missing wire.FieldsOf which promotes struct fields directly.",
"assertions": [
{"id": "8.1", "text": "Uses wire.FieldsOf(new(Config), \"DatabaseDSN\", \"CacheAddress\", \"APIKey\") or a subset"},
{"id": "8.2", "text": "Places wire.FieldsOf inside the provider set or wire.Build"},
{"id": "8.3", "text": "Updates NewDB, NewCache, NewExternalClient to accept the string fields as parameters (or uses named types alongside FieldsOf)"},
{"id": "8.4", "text": "Explains that wire.FieldsOf promotes struct fields as individual graph nodes without manual extraction functions"},
{"id": "8.5", "text": "Does NOT suggest writing three separate func GetDatabaseDSN(c *Config) string extractor functions as the primary recommendation"}
]
},
{
"id": 9,
"name": "test-injector-pattern",
"description": "Tests the test-injector pattern with wire.Bind for fake dependencies instead of runtime mocking hacks",
"prompt": "My Go service is wired with google/wire. I have a Mailer interface implemented by SMTPMailer in production. I want integration tests that use a FakeMailer instead — recording sent emails — without modifying the production provider sets. The test must wire the full graph (not just NewUserService in isolation). How should I approach this?",
"trap": "Without the skill, the model may suggest monkey-patching, a global variable for the mailer, or a runtime DI container for tests — missing the test-injector pattern with a test-only wire.NewSet and wire.Bind.",
"assertions": [
{"id": "9.1", "text": "Creates a test-only provider set (e.g., TestMailerSet) with NewFakeMailer and wire.Bind(new(Mailer), new(*FakeMailer))"},
{"id": "9.2", "text": "Creates a test injector function in a _test.go file with //go:build wireinject"},
{"id": "9.3", "text": "The test injector's wire.Build composes the production sets with the test-only set"},
{"id": "9.4", "text": "Does NOT suggest global variables, monkey-patching, or a runtime DI container for tests"},
{"id": "9.5", "text": "Mentions that wire ./... (or go generate) must be run to produce the test-injector generated code"}
]
},
{
"id": 10,
"name": "wire-vs-fx-for-daemon",
"description": "Tests that the model recommends uber-go/fx over wire for long-running services that need lifecycle management",
"prompt": "I'm starting a new Go HTTP server project and evaluating DI options. A colleague suggested google/wire because 'it's simpler and type-safe at compile time.' The server needs graceful shutdown (drain in-flight requests), OnStart/OnStop hooks for the database pool and metrics exporter, and should handle SIGINT/SIGTERM. Should I use wire?",
"trap": "Without the skill, the model may agree that wire is suitable because it's simple and compile-time safe, not recognizing that lifecycle, signal handling, and hook ordering are exactly what fx provides and wire explicitly lacks.",
"assertions": [
{"id": "10.1", "text": "Identifies that wire has no built-in lifecycle management (no OnStart/OnStop hooks)"},
{"id": "10.2", "text": "Identifies that wire has no built-in signal handling (SIGINT/SIGTERM)"},
{"id": "10.3", "text": "Recommends uber-go/fx (or at minimum flags it as the better fit) for a long-running HTTP daemon with lifecycle needs"},
{"id": "10.4", "text": "Does NOT recommend wire as sufficient for a service requiring graceful shutdown and lifecycle hooks"},
{"id": "10.5", "text": "Mentions that with wire the developer must implement shutdown and signal handling manually"}
]
}
]
Advanced — google/wire
Detail topics referenced from SKILL.md. Each section is self-contained.
Cleanup Chains
When a provider returns (T, func(), error), Wire adds the cleanup to a chain. The generated injector runs cleanups in reverse construction order: the last-built dependant is cleaned up first, ensuring dependants are torn down before their dependencies.
// Provider with cleanup
func NewDB(cfg *Config) (*sql.DB, func(), error) {
db, err := sql.Open("postgres", string(cfg.DSN))
if err != nil { return nil, nil, err }
return db, func() { db.Close() }, nil
}
func NewCache(cfg *Config) (*redis.Client, func(), error) {
c := redis.NewClient(&redis.Options{Addr: cfg.CacheAddr})
return c, func() { c.Close() }, nil
}Wire generates something like:
func InitApp() (*App, func(), error) {
cfg := NewConfig()
db, dbCleanup, err := NewDB(cfg)
if err != nil { return nil, nil, err }
cache, cacheCleanup, err := NewCache(cfg)
if err != nil {
dbCleanup() // already-built cleanups run on partial failure
return nil, nil, err
}
app := NewApp(db, cache)
return app, func() {
cacheCleanup() // reverse order
dbCleanup()
}, nil
}Caller pattern — guard against nil cleanup on construction failure:
app, cleanup, err := InitApp()
if err != nil { log.Fatal(err) }
defer cleanup()Wire always returns a non-nil cleanup function when construction succeeds. If construction fails midway, the returned cleanup is nil — guard before calling.
Multiple Injectors in One Package
A package can contain multiple injector functions. Each must live in a file with //go:build wireinject. All generated functions land in wire_gen.go in the same package.
//go:build wireinject
package main
// Production injector
func InitProdApp() (*App, func(), error) {
wire.Build(ProdSet, NewApp)
return nil, nil, nil
}
// Development injector with debug providers
func InitDevApp() (*App, func(), error) {
wire.Build(DevSet, NewApp)
return nil, nil, nil
}Select at runtime with a flag, or at build time with separate //go:build prod / //go:build !prod constraints on the injector files.
wire.NewSet Nesting Strategies
Sets can contain other sets, building a hierarchy that mirrors your package structure.
// pkg/config/wire.go
var ConfigSet = wire.NewSet(NewConfig)
// pkg/infra/wire.go
var InfraSet = wire.NewSet(
config.ConfigSet, // embed upstream set
NewDB,
NewCache,
)
// pkg/service/wire.go
var ServiceSet = wire.NewSet(
NewUserService,
wire.Bind(new(UserStore), new(*UserRepo)),
)
// wire.go (injector)
wire.Build(infra.InfraSet, service.ServiceSet, NewApp)Library set stability rules (from upstream best practices):
- Safe: replace one provider with another that has the same or fewer inputs, in the same release.
- Safe: introduce a brand-new output type not previously provided.
- Breaking: add a new required input to a provider — downstream injectors cannot satisfy it.
- Breaking: remove a provided output type — downstream injectors that depend on it fail.
- Breaking: add a type that the injector already provides — Wire reports a duplicate.
wire:"-" Exclusion Tag
Exclude a struct field from wire.Struct injection by tagging it:
type Server struct {
Logger *zap.Logger
DB *sql.DB
mu sync.Mutex `wire:"-"` // unexported — auto-excluded
Timeout time.Duration `wire:"-"` // exported but opt-out
}
wire.Struct(new(Server), "*") // injects Logger and DB; skips mu and TimeoutUnexported fields are always skipped regardless of the tag.
Common Codegen Errors
| Error message | Root cause | Fix |
|---|---|---|
no provider found for TYPE | A dependency is not provided by any set in wire.Build | Add the missing provider or set |
multiple bindings for TYPE | Two providers return the same type | Use named types or remove the duplicate |
argument N has no provider for TYPE | An interface is requested but no wire.Bind maps to it | Add wire.Bind(new(Iface), new(*Impl)) to a set |
cycle detected | A → B → A circular dependency | Break the cycle by introducing an interface or factory |
wire.Build used outside of injector function | wire.Build called from a non-injector function | Only call wire.Build inside functions with the build tag |
| duplicate symbol / redeclared in this block | Injector file is missing //go:build wireinject | Add the build tag as the first line |
Codegen Flags
# Specify output file prefix (default: wire_gen)
wire -output_file_prefix=init gen ./cmd/server
# Apply build tags during generation
wire -tags=integration gen ./...
# Prepend a header file (e.g., license comment) to generated output
wire -header_file=hack/boilerplate.go.txt gen ./...panic(wire.Build(...)) Alternate Syntax
Wire accepts either a dummy return or a panic call as the injector body. The panic form avoids writing zero-value returns for complex types:
// Preferred when return types are complex or error-prone to zero-initialize
func InitApp(ctx context.Context) (*App, func(), error) {
panic(wire.Build(AppSet))
}Wire detects both forms and replaces the body during codegen. The panic is never reached in the compiled binary — only the generated wire_gen.go version is compiled.
Accepting External Values as Injector Arguments
When a value is constructed before wire runs (e.g., parsed flags, an http.Client from a test), pass it as a parameter to the injector rather than providing it from within the graph:
//go:build wireinject
func InitApp(cfg *Config) (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil
}
// main.go
cfg := parseFlags()
app, cleanup, err := InitApp(cfg)Wire treats injector parameters as pre-built providers — they satisfy dependencies without needing a wire.NewSet entry.
Quick Reference
| Symbol | Purpose |
|---|---|
wire.NewSet(providers...) | Group providers into a reusable set |
wire.Build(sets...) | Declare injector body (codegen replaces it) |
wire.Bind(new(Iface), new(*Concrete)) | Bind interface to concrete type |
wire.Struct(new(T), "Field", ...) | Inject struct fields from the graph |
wire.Struct(new(T), "*") | Inject all non-excluded fields |
wire.Value(expr) | Bind a constant expression (no fn calls/channels) |
wire.InterfaceValue(new(I), value) | Bind a value to an interface type |
wire.FieldsOf(new(T), "Field", ...) | Promote struct fields as individual graph nodes |
//go:build wireinject | Build tag: exclude injector stub from binary |
wire_gen.go | Generated output — commit, never edit |
wire ./... | Regenerate all injectors in the module |
wire check ./... | Validate graph without regenerating |
Recipes — google/wire
End-to-end examples. Each recipe is self-contained.
HTTP Server with Postgres and Redis
A typical service: parsed config → DB (with cleanup) → Redis (with cleanup) → repo → service → HTTP server.
myapp/
├── config/
│ ├── config.go
│ └── wire.go
├── infra/
│ ├── db.go
│ ├── cache.go
│ └── wire.go
├── repo/
│ ├── user.go
│ └── wire.go
├── service/
│ ├── user.go
│ └── wire.go
├── transport/
│ ├── handler.go
│ └── wire.go
├── wire.go // injector — //go:build wireinject
├── wire_gen.go // generated — commit this
└── main.go// config/config.go
type Config struct {
Addr string
DSN string
CacheAddr string
}
func NewConfig() *Config {
return &Config{
Addr: env("ADDR", ":8080"),
DSN: mustEnv("DATABASE_URL"),
CacheAddr: env("REDIS_ADDR", "localhost:6379"),
}
}
// config/wire.go
var ConfigSet = wire.NewSet(NewConfig)// infra/db.go
func NewDB(cfg *config.Config) (*sql.DB, func(), error) {
db, err := sql.Open("postgres", cfg.DSN)
if err != nil { return nil, nil, err }
if err := db.Ping(); err != nil { db.Close(); return nil, nil, err }
return db, func() { db.Close() }, nil
}
// infra/cache.go
func NewRedis(cfg *config.Config) (*redis.Client, func(), error) {
c := redis.NewClient(&redis.Options{Addr: cfg.CacheAddr})
if err := c.Ping(context.Background()).Err(); err != nil {
return nil, nil, err
}
return c, func() { c.Close() }, nil
}
// infra/wire.go
var InfraSet = wire.NewSet(NewDB, NewRedis)// repo/user.go
type UserStore interface {
GetUser(ctx context.Context, id int64) (*User, error)
}
type PostgresUserRepo struct{ db *sql.DB }
func NewUserRepo(db *sql.DB) *PostgresUserRepo { return &PostgresUserRepo{db: db} }
// repo/wire.go
var RepoSet = wire.NewSet(
NewUserRepo,
wire.Bind(new(UserStore), new(*PostgresUserRepo)),
)// service/user.go
type UserService struct {
store repo.UserStore
cache *redis.Client
}
func NewUserService(store repo.UserStore, cache *redis.Client) *UserService {
return &UserService{store: store, cache: cache}
}
// service/wire.go
var ServiceSet = wire.NewSet(NewUserService)// wire.go
//go:build wireinject
package main
func InitApp() (*transport.Handler, func(), error) {
wire.Build(
config.ConfigSet,
infra.InfraSet,
repo.RepoSet,
service.ServiceSet,
transport.NewHandler,
)
return nil, nil, nil
}// main.go
func main() {
handler, cleanup, err := InitApp()
if err != nil { log.Fatal(err) }
defer cleanup()
srv := &http.Server{Addr: ":8080", Handler: handler}
log.Fatal(srv.ListenAndServe())
}Multiple Build Variants (Prod vs Dev)
Use separate injector files with //go:build constraints to select different provider sets at build time.
// wire_prod.go
//go:build wireinject && !dev
package main
func InitApp() (*App, func(), error) {
wire.Build(ProdSet, NewApp)
return nil, nil, nil
}
// wire_dev.go
//go:build wireinject && dev
package main
func InitApp() (*App, func(), error) {
wire.Build(DevSet, NewApp) // DevSet swaps real DB for in-memory SQLite
return nil, nil, nil
}Use -output_file_prefix to write separate output files — both commands would otherwise overwrite the same wire_gen.go:
wire -tags dev -output_file_prefix=wire_gen_dev gen .
wire -output_file_prefix=wire_gen_prod gen .Add build constraints to the generated files so only one compiles per build:
// wire_gen_prod.go — add at the top (after wire writes it)
//go:build !dev
// wire_gen_dev.go — add at the top
//go:build devCommit both generated files. At build time, only the matching file is compiled.
Cleanup-Heavy Graph
When several providers need shutdown coordination, wire's reverse-order cleanup is essential.
// Providers return (T, func(), error)
func NewDBPool(cfg *Config) (*pgxpool.Pool, func(), error) {
pool, err := pgxpool.New(context.Background(), cfg.DSN)
if err != nil { return nil, nil, err }
return pool, func() { pool.Close() }, nil
}
func NewOTelExporter(cfg *Config) (*otlptrace.Exporter, func(), error) {
exp, err := otlptracegrpc.New(context.Background(), ...)
if err != nil { return nil, nil, err }
return exp, func() { exp.Shutdown(context.Background()) }, nil
}
func NewTracerProvider(exp *otlptrace.Exporter) (*trace.TracerProvider, func(), error) {
tp := trace.NewTracerProvider(trace.WithBatcher(exp))
return tp, func() { tp.Shutdown(context.Background()) }, nil
}Wire generates shutdown in reverse: TracerProvider → OTelExporter → DBPool. Each cleanup runs before its dependencies shut down — guaranteeing in-flight spans are flushed before the exporter closes.
Embedding Wire in a CLI
Wire produces a struct, not an app framework. You control the lifecycle:
// wire.go
//go:build wireinject
package cmd
func InitServer(cfg *Config) (*http.Server, func(), error) {
wire.Build(InfraSet, ServiceSet, NewHTTPServer)
return nil, nil, nil
}
// cmd/serve.go
func runServe(cfg *Config) error {
srv, cleanup, err := InitServer(cfg)
if err != nil { return err }
defer cleanup()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return srv.Shutdown(ctx)
}Unlike fx.Run(), wire does not manage the lifecycle loop. Implement signal handling and graceful shutdown explicitly. This is a feature for CLI tools that spin up short-lived services and need precise control over the shutdown sequence.
Passing External Values to Wire
Values built before wire runs (parsed config, test doubles) become injector parameters:
//go:build wireinject
// cfg is resolved outside the graph — treated as a provided *Config
func InitApp(cfg *Config) (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil
}
// main.go
cfg, err := config.Load()
if err != nil { log.Fatal(err) }
app, cleanup, err := InitApp(cfg)The parameter cfg *Config satisfies any downstream provider that requests *Config — no wire.Value or extra set entry needed.
Testing — google/wire
Wire generates plain Go constructor calls, so tests work directly on the constructor layer — no container API to learn.
Unit Tests: Plain Constructor Injection
The generated code has no wire dependency. Test constructors directly:
func TestUserService_GetUser(t *testing.T) {
mockStore := &MockUserStore{users: map[int64]*User{1: &User{ID: 1, Name: "Alice"}}}
cache := newTestRedis(t)
svc := service.NewUserService(mockStore, cache)
u, err := svc.GetUser(context.Background(), 1)
require.NoError(t, err)
assert.Equal(t, "Alice", u.Name)
}Pass mocks directly as constructor arguments. No wire, no container, no file to generate. This is the idiomatic approach for unit tests.
Test Injectors: Swapping Providers
For integration or component tests where you want the full wired graph but with selected dependencies replaced, create a test-only injector in a _test.go file.
// app_test.go
//go:build wireinject
package main
import (
"testing"
"github.com/google/wire"
)
// TestSet replaces real infra with in-memory fakes
var TestSet = wire.NewSet(
NewTestConfig,
NewInMemoryUserStore,
wire.Bind(new(repo.UserStore), new(*InMemoryUserStore)),
NewTestRedis,
)
func InitTestApp(t *testing.T) (*App, func(), error) {
wire.Build(TestSet, service.ServiceSet, NewApp)
return nil, nil, nil
}// app_integration_test.go
//go:build !wireinject // compiles when the wireinject tag is NOT set
package main
func TestApp_GetUser(t *testing.T) {
app, cleanup, err := InitTestApp(t)
require.NoError(t, err)
defer cleanup()
// test against the fully-wired app with fake dependencies
u, err := app.GetUser(context.Background(), 1)
require.NoError(t, err)
assert.NotNil(t, u)
}Run wire ./... to generate wire_gen.go — the test injector is included because the _test.go file is compiled as part of the package during go test.
Key pattern from upstream best practices: Prefer creating a test-only provider set over passing mocks as injector arguments (though both work). The set approach keeps the test injector composable.
Passing Mocks as Injector Arguments
An alternative to a test set: pass the mock directly as an injector parameter. Wire treats it as a pre-built provider.
//go:build wireinject
func InitTestApp(store repo.UserStore) (*App, func(), error) {
wire.Build(config.ConfigSet, service.ServiceSet, NewApp)
return nil, nil, nil
}
// Test
func TestApp(t *testing.T) {
mock := &MockUserStore{}
app, cleanup, err := InitTestApp(mock)
require.NoError(t, err)
defer cleanup()
// ...
}Use this form when you only need to replace one or two dependencies and a full TestSet is overkill.
CI: Detecting Stale wire_gen.go
If wire_gen.go is not regenerated after a provider change, CI builds pass but the graph is wrong. Enforce freshness in CI:
# Option 1: re-run wire and check for diffs
wire ./...
git diff --exit-code -- '**/wire_gen.go'# .github/workflows/ci.yml
- name: Check wire_gen.go is up-to-date
run: |
go install github.com/google/wire/cmd/wire@v0.7.0
wire ./...
git diff --exit-code -- '**/wire_gen.go'# Option 2: use wire check (verifies graph without regenerating)
wire check ./...wire check exits non-zero if the graph is inconsistent but does not update wire_gen.go. Use it for a fast graph-validity check without modifying files.
Testing Interface Bindings
wire.Bind can be used in test sets to bind a fake to the same interface:
// Fake implements the same interface as the real provider
type FakeMailer struct{ sent []string }
func (f *FakeMailer) Send(to, body string) error { f.sent = append(f.sent, to); return nil }
var TestMailerSet = wire.NewSet(
NewFakeMailer,
wire.Bind(new(notification.Mailer), new(*FakeMailer)),
)
var TestSet = wire.NewSet(
TestMailerSet,
realServiceSet, // everything else is real
)This keeps the test injector narrow — only the Mailer is faked; the rest of the graph is real.
Table-Driven Tests Without Wire
Wire is an initialization tool. Once the object graph is built, table-driven tests on individual services need no wire involvement:
func TestUserService(t *testing.T) {
cases := []struct {
name string
id int64
users map[int64]*User
want string
err bool
}{
{"found", 1, map[int64]*User{1: &User{Name: "Alice"}}, "Alice", false},
{"not found", 99, nil, "", true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
svc := service.NewUserService(&MockUserStore{users: tc.users}, nil)
u, err := svc.GetUser(context.Background(), tc.id)
if tc.err { require.Error(t, err); return }
assert.Equal(t, tc.want, u.Name)
})
}
}Wire has no role here — the injector was only needed to build the object graph in main (or in an integration test). Unit tests construct dependencies directly.
Related skills
FAQ
Why does golang-google-wire require //go:build wireinject?
golang-google-wire requires //go:build wireinject on injector files so InitApp in the injector is excluded from normal go build, preventing duplicate-symbol errors when wire_gen.go defines the same function after code generation.
What Wire patterns does golang-google-wire cover?
golang-google-wire covers google/wire injector files, wire.Build provider sets like InfraSet and ServiceSet, wireinject build tags, and resolving go build redeclared errors after wire_gen.go is generated.
Is Golang Google Wire safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.