Uncategorized

Building Multi-Tenant SaaS Applications with Hazina

Multi-tenancy is one of the hardest things to retrofit into an application. With Hazina, it’s built in from the start. Every query, every mutation, every API call is automatically scoped to the current tenant.

How It Works

Hazina uses a middleware-based approach to tenant resolution:

  1. Request comes in with tenant identifier (header, subdomain, or JWT claim)
  2. Middleware resolves the tenant and sets the tenant context
  3. All database queries are automatically filtered by TenantId
  4. All creates automatically stamp the TenantId
  5. Cross-tenant access is impossible at the framework level

Setup

builder.Services.AddGenericEntityApi<AppDbContext>();
builder.Services.AddScoped<ITenantContext, TenantContext>();

// Configure tenant resolution
builder.Services.AddTenantResolution(options => {
    options.Strategy = TenantResolutionStrategy.Header;
    options.HeaderName = "X-Tenant-Id";
    // Or: options.Strategy = TenantResolutionStrategy.JwtClaim;
    // Or: options.Strategy = TenantResolutionStrategy.Subdomain;
});

Entity Definition

public class Customer : TenantEntityBase
{
    public string Name { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

What Happens Behind the Scenes

// When Tenant A calls GET /api/customers
// Hazina generates: SELECT * FROM Customers WHERE TenantId = 'tenant-a'

// When Tenant A calls POST /api/customers
// Hazina automatically sets: customer.TenantId = 'tenant-a'

// When Tenant A calls GET /api/customers/42
// If customer 42 belongs to Tenant B: 404 Not Found
// Not 403  - the customer simply doesn't exist in Tenant A's world

Global Entities

Some entities are shared across tenants (categories, settings templates). Use EntityBase for those – they bypass tenant filtering:

public class GlobalCategory : EntityBase  // No tenant filtering
{
    public string Name { get; set; } = string.Empty;
}

public class TenantCategory : TenantEntityBase  // Tenant-scoped
{
    public string Name { get; set; } = string.Empty;
}

Testing Multi-Tenancy

// In your tests, just set the tenant context:
var tenantContext = new TestTenantContext("test-tenant");
services.AddScoped<ITenantContext>(_ => tenantContext);

In the next post, we’ll explore RAG – Hazina’s Retrieval-Augmented Generation engine for smart document search.

Frequently Asked Questions

How does Hazina handle multi-tenancy in SaaS applications?

Hazina manages multi-tenancy by using a middleware-based approach that automatically scopes every query, mutation, and API call to the current tenant. It resolves tenant identifiers through headers, subdomains, or JWT claims, ensuring that database queries are filtered by TenantId.

What are the benefits of using Hazina for multi-tenant applications?

Using Hazina simplifies the implementation of multi-tenancy by eliminating cross-tenant access at the framework level and automatically managing tenant contexts. This leads to improved security and reduced complexity in application development.

How can I set up tenant resolution in Hazina?

To set up tenant resolution in Hazina, you can configure the service using builder.Services.AddTenantResolution() with your preferred strategy, such as Header, JWT Claim, or Subdomain. For example, using Header, you specify the header name like ‘X-Tenant-Id’ for tenant identification.

What is the difference between Global Entities and Tenant Entities in Hazina?

Global Entities in Hazina, such as GlobalCategory, do not have tenant filtering and can be shared across tenants. In contrast, Tenant Entities, like TenantCategory, are scoped to individual tenants, ensuring that each tenant only sees their own data.

Terug naar overzicht
ENNL