Fiber Integration
Complete guide for using godi with the Fiber web framework.
Installation
go get github.com/junioryono/godi/v4
go get github.com/junioryono/godi/v4/fiber
Quick Start
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/junioryono/godi/v4"
godifiber "github.com/junioryono/godi/v4/fiber"
)
type UserController struct{}
func NewUserController() *UserController {
return &UserController{}
}
func (c *UserController) List(ctx *fiber.Ctx) error {
return ctx.JSON([]string{"alice", "bob"})
}
func main() {
services := godi.NewCollection()
services.AddScoped(NewUserController)
provider, _ := services.Build()
defer provider.Close()
app := fiber.New()
app.Use(godifiber.ScopeMiddleware(provider))
app.Get("/users", godifiber.Handle((*UserController).List))
app.Listen(":8080")
}
ScopeMiddleware
Creates a request scope for each HTTP request. The scope is stored in fiber.Ctx.Locals and in the UserContext.
app := fiber.New()
app.Use(godifiber.ScopeMiddleware(provider))
Configuration Options
app.Use(godifiber.ScopeMiddleware(provider,
// Custom error handler for scope creation failures
godifiber.WithErrorHandler(func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
"error": "Service unavailable",
})
}),
// Custom handler for scope close errors
godifiber.WithCloseErrorHandler(func(err error) {
log.Printf("Scope close error: %v", err)
}),
// Middleware that runs after scope creation
godifiber.WithMiddleware(func(scope godi.Scope, c *fiber.Ctx) error {
reqCtx := godi.MustResolve[*RequestContext](scope)
reqCtx.UserID = c.Get("X-User-ID")
return nil
}),
))
Handle
Wraps a controller method for type-safe resolution.
type UserController interface {
List(*fiber.Ctx) error
GetByID(*fiber.Ctx) error
Create(*fiber.Ctx) error
}
app.Get("/users", godifiber.Handle(UserController.List))
app.Get("/users/:id", godifiber.Handle(UserController.GetByID))
app.Post("/users", godifiber.Handle(UserController.Create))
Handler Options
app.Get("/users", godifiber.Handle(UserController.List,
// Enable panic recovery
godifiber.WithPanicRecovery(true),
// Custom panic handler
godifiber.WithPanicHandler(func(c *fiber.Ctx, v any) error {
log.Printf("Panic: %v", v)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Unexpected error",
})
}),
// Custom scope error handler
godifiber.WithScopeErrorHandler(func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Session error",
})
}),
// Custom resolution error handler
godifiber.WithResolutionErrorHandler(func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
"error": "Service unavailable",
})
}),
))
Complete Example
package main
import (
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/google/uuid"
"github.com/junioryono/godi/v4"
godifiber "github.com/junioryono/godi/v4/fiber"
)
// === Services ===
type Logger struct{}
func NewLogger() *Logger { return &Logger{} }
func (l *Logger) Info(msg string, args ...any) {
log.Printf("[INFO] "+msg, args...)
}
type RequestContext struct {
ID string
UserID string
StartTime time.Time
}
func NewRequestContext() *RequestContext {
return &RequestContext{
ID: uuid.New().String()[:8],
StartTime: time.Now(),
}
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
type UserService struct {
reqCtx *RequestContext
logger *Logger
}
func NewUserService(reqCtx *RequestContext, logger *Logger) *UserService {
return &UserService{reqCtx: reqCtx, logger: logger}
}
func (s *UserService) GetAll() []User {
s.logger.Info("[%s] Fetching all users", s.reqCtx.ID)
return []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
}
// === Controllers ===
type UserController struct {
service *UserService
reqCtx *RequestContext
}
func NewUserController(service *UserService, reqCtx *RequestContext) *UserController {
return &UserController{service: service, reqCtx: reqCtx}
}
func (c *UserController) List(ctx *fiber.Ctx) error {
users := c.service.GetAll()
ctx.Set("X-Request-ID", c.reqCtx.ID)
return ctx.JSON(fiber.Map{
"users": users,
"duration": time.Since(c.reqCtx.StartTime).String(),
})
}
func (c *UserController) GetByID(ctx *fiber.Ctx) error {
id := ctx.Params("id")
ctx.Set("X-Request-ID", c.reqCtx.ID)
return ctx.JSON(User{ID: 1, Name: "User " + id})
}
// === Main ===
func main() {
// Register services
services := godi.NewCollection()
services.AddSingleton(NewLogger)
services.AddScoped(NewRequestContext)
services.AddScoped(NewUserService)
services.AddScoped(NewUserController)
// Build provider
provider, err := services.Build()
if err != nil {
log.Fatalf("Failed to build provider: %v", err)
}
defer provider.Close()
// Create Fiber app
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
},
})
// Fiber middleware
app.Use(logger.New())
app.Use(recover.New())
// godi scope middleware
app.Use(godifiber.ScopeMiddleware(provider,
godifiber.WithMiddleware(func(scope godi.Scope, c *fiber.Ctx) error {
reqCtx := godi.MustResolve[*RequestContext](scope)
reqCtx.UserID = c.Get("X-User-ID")
return nil
}),
))
// Routes
app.Get("/users", godifiber.Handle((*UserController).List))
app.Get("/users/:id", godifiber.Handle((*UserController).GetByID))
// Health check
app.Get("/health", func(c *fiber.Ctx) error {
return c.SendString("OK")
})
// Start server
log.Println("Server starting on :8080")
app.Listen(":8080")
}
Route Groups
Use with Fiber route groups:
api := app.Group("/api/v1")
users := api.Group("/users")
users.Get("/", godifiber.Handle((*UserController).List))
users.Get("/:id", godifiber.Handle((*UserController).GetByID))
users.Post("/", godifiber.Handle((*UserController).Create))
orders := api.Group("/orders")
orders.Get("/", godifiber.Handle((*OrderController).List))
orders.Post("/", godifiber.Handle((*OrderController).Create))
FromContext Helper
Fiber stores the scope in Locals. Use the helper to retrieve it:
app.Get("/custom", func(c *fiber.Ctx) error {
scope := godifiber.FromContext(c)
if scope == nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "No scope",
})
}
service := godi.MustResolve[*UserService](scope)
users := service.GetAll()
return c.JSON(users)
})
Accessing URL Parameters
Use Fiber’s parameter methods in your controllers:
func (c *UserController) GetByID(ctx *fiber.Ctx) error {
id := ctx.Params("id")
// ...
}
Request Body Parsing
Combine godi with Fiber’s body parser:
type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
}
func (c *UserController) Create(ctx *fiber.Ctx) error {
var req CreateUserRequest
if err := ctx.BodyParser(&req); err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
user := c.service.Create(req.Name, req.Email)
return ctx.Status(fiber.StatusCreated).JSON(user)
}
Important: Fiber’s Context Handling
Fiber reuses *fiber.Ctx between requests for performance. The godi integration handles this correctly by storing the scope in Locals, which is cleared between requests.
// Safe: scope is stored per-request in Locals
scope := godifiber.FromContext(c)
// Also safe: scope is in UserContext
scope, err := godi.FromContext(c.UserContext())
See also: Gin Integration | Chi Integration | Echo Integration | net/http Integration