Providers
Providers are the fundamental building blocks of Nika. A Provider is essentially a service, repository, factory, helper, or any other object that can be injected into controllers and other providers through the DI container.
Core Concept
In Nika, almost everything is treated as a Provider — services, repositories, factories, and helpers. Providers are plain Go structs or constructor functions that are registered in the DI container.
Creating a Provider
Simple Service
A provider can be as simple as a struct with methods:
package src
type UserService struct{}
func NewUserService() *UserService {
return &UserService{}
}
func (s *UserService) FindAll() []string {
return []string{"Alice", "Bob", "Charlie"}
}
func (s *UserService) FindByID(id string) *User {
// find user logic
return nil
}
Provider with Dependencies
Providers can depend on other providers. Nika's DI container automatically resolves these dependencies:
package src
import (
"context"
"github.com/nika-framework/nika/common/mongodb/repository"
)
type UserRepository struct {
repo *repository.BaseRepository[User]
}
// Nika automatically resolves *mongo.Collection from the DI container
func NewUserRepository(db *mongo.Database) *UserRepository {
return &UserRepository{
repo: repository.NewBaseRepository[User](db.Collection("users")),
}
}
type UserService struct {
userRepo *UserRepository
}
// Nika automatically resolves *UserRepository from the DI container
func NewUserService(userRepo *UserRepository) *UserService {
return &UserService{
userRepo: userRepo,
}
}
Registering Providers
Register providers in a module's Providers() method. You can pass either an instance or a constructor function:
func (m *AppModule) Providers() []interface{} {
return []interface{}{
// Constructor function — Nika will auto-resolve dependencies
NewUserService,
NewUserRepository,
// Direct instance — registered as-is
&ConfigService{},
}
}
How DI Resolution Works
When Nika encounters a constructor function, it:
- Inspects the function's parameter types using reflection
- Looks up each parameter type in the DI container
- Calls the function with the resolved dependencies
- Registers the return value in the container
// The DI container resolves the chain:
// NewUserRepository needs *mongo.Database → found in container
// NewUserService needs *UserRepository → found in container (registered above)
RegisterSingleton
You can also manually register singletons directly in the App:
func main() {
app := nika.NewApp()
// Register a singleton directly
app.RegisterSingleton(myInstance)
app.LoadModule(rootModule)
app.Listen(":3000")
}
Common Package Providers
Nika's common packages follow the Setup() pattern, which creates and registers the provider:
// These Setup functions register their instances in the DI container
config.Setup(app, "")
mongodb.Setup(app, mongodb.Config{URI: "mongodb://localhost:27017", Database: "mydb"})
cache.Setup(app, cache.Config{Driver: "redis", URL: "redis://localhost:6379"})
validator.Setup(app)
Summary
| Registration Method | Example | Auto-resolve Deps? |
|---|---|---|
| Constructor function | NewUserService |
✅ Yes |
| Direct instance | &ConfigService{} |
❌ No |
RegisterSingleton |
app.RegisterSingleton(x) |
❌ No |
Setup() function |
config.Setup(app, "") |
✅ Yes (returns value) |
Next Steps
Learn about Modules to organize your providers.