Multi-Tenancy

Serve multiple tenants from a single application instance. Automatically resolve the current tenant from subdomain, header, or URL path and scope all data access.

Setup

import "github.com/CodeSyncr/nimbus/tenancy"

app.Use(tenancy.New(tenancy.Config{
    Strategy: "subdomain",     // subdomain | header | path
    Header:   "X-Tenant-ID",   // only used with "header" strategy
}))

Tenant Resolution Strategies

StrategyResolutionExample
subdomainExtract from Host headeracme.myapp.com
headerCustom request headerX-Tenant-ID: acme
pathURL path prefix/tenant/acme/...

Using the Current Tenant

func (ctrl *DashboardController) Index(c *http.Context) error {
    tenant := tenancy.FromContext(c.Request.Context())

    // Query scoped to tenant
    var users []User
    db.Where("tenant_id = ?", tenant.ID).Find(&users)

    return c.JSON(200, users)
}

Real-Life Example: SaaS Dashboard

// Tenant-scoped product listing
func (ctrl *ProductController) Index(c *http.Context) error {
    tenant := tenancy.FromContext(c.Request.Context())

    var products []Product
    db.Where("tenant_id = ?", tenant.ID).
        Order("created_at DESC").
        Find(&products)

    return c.JSON(200, products)
}

// Tenant-scoped analytics
func (ctrl *AnalyticsController) Revenue(c *http.Context) error {
    tenant := tenancy.FromContext(c.Request.Context())

    var total float64
    db.Model(&Order{}).
        Where("tenant_id = ? AND created_at >= ?", tenant.ID, startOfMonth()).
        Select("COALESCE(SUM(total), 0)").
        Scan(&total)

    return c.JSON(200, map[string]float64{"revenue": total})
}

// Tenant-scoped cache keys
func (ctrl *SettingsController) Get(c *http.Context) error {
    tenant := tenancy.FromContext(c.Request.Context())
    key := fmt.Sprintf("settings:%d", tenant.ID)

    settings, _ := cache.RememberT[Settings](key, time.Hour, func() (Settings, error) {
        var s Settings
        db.Where("tenant_id = ?", tenant.ID).First(&s)
        return s, nil
    })

    return c.JSON(200, settings)
}

Best Practices

  • Always scope database queries with tenant_id
  • Include tenant ID in cache keys to prevent data leakage
  • Use subdomain for customer-facing SaaS apps
  • Use header for API-first architectures
  • Create GORM scopes to automatically apply tenant filtering