Skip to content

Gabicle/multi-tenant-localisation-api

Repository files navigation

Multi-Tenant Localisation API

A simple demo Web API demonstrating multi-tenant localisation and globalisation patterns. Each tenant is a company in a different European country. The API automatically resolves their language and culture on every request.

Architecture

Request → TenantResolutionMiddleware → Culture Set → Endpoint → Localised Response

How It Works

  1. Every request must include an X-API-Key header
  2. The middleware hashes the key (SHA256) and checks the cache
  3. On cache miss, BCrypt verifies the key against the database
  4. On success, CurrentCulture and CurrentUICulture are set for the request
  5. The endpoint returns a response in the tenant's language

Tenants

Tenant Company Country Culture
tenant-be Gale Belgium Belgium fr-BE
tenant-uk Gale UK United Kingdom en-GB
tenant-de Gale Germany Germany de-DE
tenant-fr Gale France France fr-FR

Tech Stack

  • Runtime: .NET 10
  • Database: PostgreSQL 16
  • ORM: Entity Framework Core 10
  • Caching: IDistributedCache (in-memory, Redis-ready)
  • Password Hashing: BCrypt.Net-Next
  • Testing: xUnit + WebApplicationFactory + SQLite in-memory

Project Structure

MultiTenantLocalisation/
├── MultiTenantLocalisationApi/
│   ├── Data/
│   │   ├── Configuration/       # EF Core entity configurations
│   │   ├── Entities/            # Database entities
│   │   ├── Migrations/          # EF Core migrations
│   │   ├── AppDbContext.cs
│   │   └── SeedData.cs
│   ├── Middleware/
│   │   └── TenantResolutionMiddleware.cs
│   ├── Models/
│   │   ├── ApiError.cs          # Structured error response shape
│   │   ├── ApiErrors.cs         # Error factory
│   │   └── TenantInfo.cs        # Per-request tenant DTO
│   ├── Resources/
│   │   ├── SharedResource.resx      # Default (English)
│   │   ├── SharedResource.fr.resx   # French
│   │   └── SharedResource.de.resx   # German
│   ├── Services/
│   │   └── TenantCacheService.cs
│   ├── SharedResource.cs        # Localisation marker class
│   └── Program.cs
└── MultiTenantLocalisationApi.Tests/
    └── Middleware/
        ├── CustomWebApplicationFactory.cs
        └── TenantResolutionMiddlewareTests.cs

Getting Started

Prerequisites

  • .NET 10 SDK
  • Docker

1. Start PostgreSQL

docker run --name saas-localisation-db \
  -e POSTGRES_USER=saasuser \
  -e POSTGRES_PASSWORD=saaspassword \
  -e POSTGRES_DB=saaslocalisationdb \
  -p 5433:5432 \
  -d postgres:16

2. Apply Migrations

cd MultiTenantLocalisationApi
dotnet ef database update

3. Run the API

dotnet run

The API starts on http://localhost:5000. On first run, tenants are seeded automatically.

API Reference

Greet Endpoint

GET /greet/{name}
Headers: X-API-Key: <api-key>

Example: Gale UK:

curl -H "X-API-Key: gale-uk-api-key-2024" http://localhost:5000/greet/John
{ "message": "Hello, John! Welcome to our platform." }

Example: Gale France:

curl -H "X-API-Key: gale-france-api-key-2024" http://localhost:5000/greet/John
{ "message": "Bonjour, John! Bienvenue sur notre plateforme." }

Culture Demo Endpoint

GET /culture-demo
Headers: X-API-Key: <api-key>

Demonstrates globalisation i.e the same date, currency, and number formatted differently per tenant culture. No translation involved as .NET formats automatically based on CurrentCulture.

Example: Gale France:

{
  "tenant": "Gale France",
  "culture": "fr-FR",
  "date": "mercredi 6 mai 2026",
  "shortDate": "06/05/2026",
  "currency": "1 234 567,89 €",
  "number": "1 234 567,890"
}

Example: Gale UK:

{
  "tenant": "Gale UK",
  "culture": "en-GB",
  "date": "Wednesday, 6 May 2026",
  "shortDate": "06/05/2026",
  "currency": "£1,234,567.89",
  "number": "1,234,567.890"
}

Example: Gale Germany:

{
  "tenant": "Gale Germany",
  "culture": "de-DE",
  "date": "Mittwoch, 6. Mai 2026",
  "shortDate": "06.05.2026",
  "currency": "1.234.567,89 €",
  "number": "1.234.567,890"
}

Error Responses

All errors follow a consistent structure:

{
  "code": "MISSING_API_KEY",
  "message": "Missing API key",
  "timestamp": "2026-05-06T10:23:11Z",
  "traceId": "0HNLBA6AK80OQ:00000001"
}
HTTP Status Code Reason
401 MISSING_API_KEY No X-API-Key header
401 INVALID_API_KEY Key not found in database
403 TENANT_INACTIVE Tenant has been disabled

Running Tests

cd MultiTenantLocalisationApi.Tests
dotnet test

Tests use SQLite in-memory. No Docker required.

Dev API Keys

Tenant API Key
Gale Belgium gale-belgium-api-key-2024
Gale UK gale-uk-api-key-2024
Gale Germany gale-germany-api-key-2024
Gale France gale-france-api-key-2024

These keys are for development only. In production, API keys are generated securely and never stored in plain text.

About

A multi-tenant localisation API built with ASP.NET Core 10, PostgreSQL, and EF Core. Resolves tenant identity and culture per request via API key authentication and middleware.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages