Skip to main content

Context Helpers

Most of the time, you should prefer handler parameter injection over manually reading values from context. The contextx package is designed for lower-level code that only has access to context.Context.

Overview

The contextx package provides type-safe getters and setters for request-scoped values. It transparently handles both fiber.Ctx (via Locals) and standard context.Context (via context.WithValue).

API Reference

Context Keys

KeyTypeDescription
KeyRequestIDstringUnique request identifier
KeyRequestIPstringClient IP address
KeyPrincipal*security.PrincipalAuthenticated user principal
KeyLoggerlogx.LoggerRequest-scoped logger
KeyDBorm.DBRequest-scoped database connection
KeyDataPermAppliersecurity.DataPermissionApplierData permission applier

Request ID

// Get the request ID
id := contextx.RequestID(ctx) // Returns "" if not set

// Set the request ID
ctx = contextx.SetRequestID(ctx, "req-abc-123")

Request IP

// Get the client IP address
ip := contextx.RequestIP(ctx) // Returns "" if not set

// Set the client IP
ctx = contextx.SetRequestIP(ctx, "192.168.1.1")

Principal (Current User)

// Get the authenticated principal
principal := contextx.Principal(ctx) // Returns nil if not authenticated

// Set the principal
ctx = contextx.SetPrincipal(ctx, principal)

// Common usage
if p := contextx.Principal(ctx); p != nil {
userID := p.ID
tenantID := p.TenantID
}

Logger

// Get the request-scoped logger
logger := contextx.Logger(ctx)

// With fallback loggers (returns the first non-nil)
logger := contextx.Logger(ctx, fallbackLogger1, fallbackLogger2)

// Set the logger
ctx = contextx.SetLogger(ctx, logger)

The logger stored in context is enriched with request identity (request ID, user ID) and operation identity, making it useful for request-correlated logging in deeper service layers.

Database (orm.DB)

// Get the request-scoped DB
db := contextx.DB(ctx)

// With fallback (useful when context DB may not be set)
db := contextx.DB(ctx, globalDB)

// Set the DB
ctx = contextx.SetDB(ctx, db)

Important: The request-scoped orm.DB is different from a raw global DB instance. It includes operator information (current user) used for automatic audit field population (created_by, updated_by).

Data Permission Applier

// Get the data permission applier
applier := contextx.DataPermApplier(ctx) // Returns nil if not set

// Set the applier
ctx = contextx.SetDataPermApplier(ctx, applier)

Fiber / stdlib Transparency

The contextx package handles both Fiber and standard contexts transparently:

Context TypeGetSet
fiber.Ctxctx.Value(key) → type assertctx.Locals(key, value)
context.Contextctx.Value(key) → type assertcontext.WithValue(ctx, key, value)

This means the same contextx calls work whether you're in a Fiber handler or in a service layer using standard context.Context.

When to Use Handler Injection Instead

For API handlers, prefer direct parameter injection:

// ✅ Preferred: explicit dependencies in signature
func (r *UserResource) FindPage(ctx fiber.Ctx, db orm.DB, principal *security.Principal) error {
// ...
}

// ❌ Avoid: hidden dependencies via context
func (r *UserResource) FindPage(ctx fiber.Ctx) error {
db := contextx.DB(ctx)
principal := contextx.Principal(ctx)
// ...
}

When contextx Makes Sense

Use contextx when:

  • You are inside service code reused by multiple entry points
  • You are writing helper libraries below the handler layer
  • You only have context.Context, not the full handler signature
  • You need request-correlated logging in deep call stacks

Who Sets These Values

The framework middleware chain populates context values automatically:

ValueSet By
Request IDApp middleware (before routing)
Request IPApp middleware (before routing)
LoggerApp middleware + contextual middleware
PrincipalAuth middleware
DBContextual middleware
DataPermApplierData permission middleware

Next Step

Read Custom Param Resolvers if you want to avoid repeated context access by extending handler injection directly.