A lightweight multitenancy library for FastAPI applications
Project description
Hidra: Lightweight Multi-Tenancy for Python
Hidra is a lightweight, framework-agnostic library for building multi-tenant applications in Python. It provides a simple and flexible way to manage tenants, allowing you to isolate tenant data using different strategies.
Key Features
- FastAPI-Focused: Specifically designed and optimized for FastAPI applications.
- Multiple Tenancy Strategies:
DATABASE_PER_TENANT: Each tenant has a separate database.SCHEMA_PER_TENANT: Each tenant has a separate schema within the same database.ROW_LEVEL: All tenants share the same database and tables, with data isolated by a tenant identifier column.
- Context-Aware: Uses
contextvarsto safely manage the current tenant's context, making it suitable for asynchronous applications. - FastAPI Integration: Provides ready-to-use middlewares and decorators specifically for FastAPI.
- Easy Setup: Simple configuration with
quick_start()andinitialize_hidra_fastapi()functions - Enhanced Decorators: Improved
requires_tenant()decorator with more flexible options - Simplified Database Access:
HidraDBclass for easier database session management - Schema Management: Advanced schema management tools to ensure proper tenant schema creation and naming compatibility with PostgreSQL (e.g., replacing hyphens with underscores).
- Developer-Controlled Tenants Table: The structure of the
tenantstable is defined by the developer to match business requirements, with optional convenience functions for common use cases. - Automatic Schema Validation: Built-in validation and cleaning of tenant names for PostgreSQL compatibility.
- Diagnostic Tools: Built-in functions to diagnose configuration issues
- Helpful Error Messages: Clear error messages with suggestions for resolution
Installation
Install the library using pip:
# Core library
pip install hidra
# To include optional dependencies for FastAPI
pip install hidra[fastapi]
# For development (includes testing and linting tools)
pip install hidra[dev]
Enhanced Usage Examples
Quick Start Configuration (With Predefined Tenants)
from hidra import quick_start
# Simple configuration with predefined tenants
config = quick_start(
db_config={
"db_driver": "postgresql",
"db_host": "localhost",
"db_port": "5432",
"db_username": "postgres",
"db_password": "password",
"db_name": "multitenant"
},
tenants={
"company1": {"plan": "premium"},
"company2": {"plan": "basic"}
}
)
Minimal Configuration for FastAPI (No Predefined Tenants Required)
from fastapi import FastAPI
from hidra import create_hidra_app
# Create FastAPI app with minimal configuration
app = create_hidra_app(
db_config={
"db_driver": "postgresql",
"db_host": "localhost",
"db_port": "5432",
"db_username": "postgres",
"db_password": "password",
"db_name": "multitenant"
},
# Tenants are loaded automatically when requested
enable_auto_loading=True,
auto_tenant_validation=True # Validate tenants exist before processing
)
# Protected endpoint - no need to define tenants in code
@app.get("/data")
async def get_data():
from hidra import get_current_tenant_id
tenant_id = get_current_tenant_id()
return {"tenant_id": tenant_id, "message": "Data retrieved"}
Enhanced Decorators
from hidra import requires_tenant
# Any tenant with access
@app.get("/data")
@requires_tenant()
async def get_data():
pass
# Specific tenant only
@app.get("/premium-data")
@requires_tenant("premium_company")
async def get_premium_data():
pass
# Multiple tenants allowed
@app.get("/shared-data")
@requires_tenant(["company1", "company2"])
async def get_shared_data():
pass
Simplified Database Access
from hidra import HidraDB
# Create database access
hidra_db = HidraDB({
"db_driver": "postgresql",
"db_host": "localhost",
"db_port": "5432",
"db_username": "postgres",
"db_password": "password",
"db_name": "multitenant"
})
# Use with FastAPI Depends
@app.get("/users")
async def get_users(db: Session = Depends(hidra_db.get_tenant_db())):
pass
Basic Usage (with FastAPI)
Here's a quick example of how to use Hidra with FastAPI.
1. Configure Tenants
First, configure your tenants and the tenancy strategy.
# main.py
from hidra import tenant_context, TenancyStrategy, MultiTenantManager
# Initialize the tenant manager
manager = MultiTenantManager()
# Configure individual tenants
manager.configure_tenant("tenant1", {"db_connection": "postgresql://user:pass@host/db1"})
manager.configure_tenant("tenant2", {"db_connection": "postgresql://user:pass@host/db2"})
# Set the default strategy
manager.set_default_strategy(TenancyStrategy.DATABASE_PER_TENANT)
# Set the manager in the global tenant context
tenant_context.tenant_manager = manager
2. Add the Middleware
The middleware identifies the tenant from the request (e.g., using a header) and sets it in the context.
# main.py
from fastapi import FastAPI
from hidra.middleware import TenantMiddleware
from hidra.decorators import tenant_required
app = FastAPI()
# Add the middleware to your application
app.add_middleware(TenantMiddleware)
@app.get("/items")
@tenant_required
async def get_items():
# The tenant is now available in the context
current_tenant = tenant_context.require_tenant()
# You can get tenant-specific configuration
config = tenant_context.tenant_manager.get_tenant_config(current_tenant)
return {"tenant_id": current_tenant, "db_connection": config.get("db_connection")}
3. Run the Application
To run this example, you would make a request with the X-Tenant-ID header:
curl -X GET "http://127.0.0.1:8000/items" -H "X-Tenant-ID: tenant1"
The response would be:
{
"tenant_id": "tenant1",
"db_connection": "postgresql://user:pass@host/db1"
}
Complete FastAPI Integration
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from hidra import create_hidra_app, requires_tenant
# Create FastAPI app with Hidra integration
app = create_hidra_app(
db_config={
"db_driver": "postgresql",
"db_host": "localhost",
"db_port": "5432",
"db_username": "postgres",
"db_password": "password",
"db_name": "multitenant"
}
)
# Protected endpoint
@app.get("/users")
@requires_tenant() # Requires tenant
async def get_users():
from hidra import get_current_tenant_id
tenant_id = get_current_tenant_id()
return {"tenant_id": tenant_id, "message": "Users retrieved"}
Diagnostic Tools
from hidra import diagnose_setup, print_diagnosis
# Get diagnosis as dictionary
diagnosis = diagnose_setup()
# Print formatted diagnosis
print_diagnosis()
Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue. If you want to contribute code, please follow these steps:
- Fork the repository.
- Create a new branch for your feature or bug fix.
- Write your code and add tests.
- Ensure all tests pass and the code is formatted with
blackand linted withruff. - Submit a pull request.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hidra-0.2.0b1.tar.gz.
File metadata
- Download URL: hidra-0.2.0b1.tar.gz
- Upload date:
- Size: 26.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
831c0e2d26bee941a92ad39a1d23e7648010216d81515f18b9ae892893f64a5d
|
|
| MD5 |
72051f63406170cd8f5f21298bfb3bd0
|
|
| BLAKE2b-256 |
901d957788317628edcd1ebd1049f7263012d98e2d90e0275cf5958859889788
|
File details
Details for the file hidra-0.2.0b1-py3-none-any.whl.
File metadata
- Download URL: hidra-0.2.0b1-py3-none-any.whl
- Upload date:
- Size: 25.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6d0a238b75b520ea7ce9a0090b8a0c4ee4c07581dddfcd0a48faee9b1dcacc4
|
|
| MD5 |
f5a8bf84f315d2271b421d499ecb73f5
|
|
| BLAKE2b-256 |
2078ee5f641047b6063d6fa6ba1358aa4f953d7421d465b403b186a7e2277a28
|