Service Providers
Service providers are the central place to configure and bootstrap your application. Every major subsystem (database, mail, queue) should be wired through a provider.
The Provider interface
A provider must implement two methods defined in the nimbus package:
type Provider interface {
Register(app *nimbus.App) error
Boot(app *nimbus.App) error
}
- Register — runs first for all providers. Bind services into
app.Container. Do not resolve other services here. - Boot — runs after every provider has registered. Safe to resolve dependencies and perform setup that relies on other services.
Creating a custom provider
Create a struct that satisfies nimbus.Provider. In Register, bind your service. In Boot, run any initialisation that depends on the container.
package providers
import (
"github.com/CodeSyncr/nimbus"
"github.com/CodeSyncr/nimbus/database"
"github.com/CodeSyncr/nimbus/lucid"
)
type DatabaseProvider struct{}
func (p *DatabaseProvider) Register(app *nimbus.App) error {
app.Container.Singleton("db", func() (*lucid.DB, error) {
return database.Connect(config.Database.Driver, config.Database.DSN)
})
return nil
}
func (p *DatabaseProvider) Boot(app *nimbus.App) error {
return nil
}
Registering providers
Register providers in bin/server.go inside the Boot() function, after creating the app:
// bin/server.go
func Boot() *nimbus.App {
config.Load()
app := nimbus.New()
// Register providers in dependency order
app.Register(&providers.DatabaseProvider{})
app.Register(&providers.CacheProvider{})
app.Register(&providers.MailProvider{})
start.RegisterMiddleware(app)
start.RegisterRoutes(app)
return app
}
Provider boot order
app.Boot() (called internally by app.Run()) executes in two passes:
- Pass 1:
Register(app)called on each provider in order. All container bindings are set up. - Pass 2:
Boot(app)called on each provider in order. Container is fully populated, so any binding can be resolved.
This guarantees that a provider's Boot method can safely call app.Container.Make("db") even if the database provider was registered after it — all registrations happen before any boot.
Accessing the container from providers
Inside Boot, resolve any registered service from the container:
func (p *QueueProvider) Boot(app *nimbus.App) error {
db := app.Container.MustMake("db").(*gorm.DB)
q := queue.New(db)
app.Container.Singleton("queue", func() *queue.Queue {
return q
})
return nil
}
When to use providers
- Database connections and ORM setup
- Mail, queue, and cache driver configuration
- Event listener registration
- Third-party SDK initialisation
- Any service that other parts of the app depend on