hidra


Namehidra JSON
Version 0.2.0b1 PyPI version JSON
download
home_pageNone
SummaryA lightweight multitenancy library for FastAPI applications
upload_time2025-11-01 14:44:15
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords multitenancy fastapi postgresql sqlalchemy tenancy
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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.

[![PyPI version](https://badge.fury.io/py/hidra.svg)](https://badge.fury.io/py/hidra)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 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 `contextvars` to 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()` and `initialize_hidra_fastapi()` functions
- **Enhanced Decorators:** Improved `requires_tenant()` decorator with more flexible options
- **Simplified Database Access:** `HidraDB` class 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 `tenants` table 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`:

```bash
# 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)

```python
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)

```python
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

```python
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

```python
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.

```python
# 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.

```python
# 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:

```bash
curl -X GET "http://127.0.0.1:8000/items" -H "X-Tenant-ID: tenant1"
```

The response would be:

```json
{
  "tenant_id": "tenant1",
  "db_connection": "postgresql://user:pass@host/db1"
}
```

### Complete FastAPI Integration

```python
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

```python
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:

1.  Fork the repository.
2.  Create a new branch for your feature or bug fix.
3.  Write your code and add tests.
4.  Ensure all tests pass and the code is formatted with `black` and linted with `ruff`.
5.  Submit a pull request.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "hidra",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "multitenancy, fastapi, postgresql, sqlalchemy, tenancy",
    "author": null,
    "author_email": "Hamilton Pati\u00f1o Solano <hamiltonpatinosolano@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/90/1d/957788317628edcd1ebd1049f7263012d98e2d90e0275cf5958859889788/hidra-0.2.0b1.tar.gz",
    "platform": null,
    "description": "# Hidra: Lightweight Multi-Tenancy for Python\r\n\r\nHidra 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.\r\n\r\n[![PyPI version](https://badge.fury.io/py/hidra.svg)](https://badge.fury.io/py/hidra)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\n## Key Features\r\n\r\n- **FastAPI-Focused:** Specifically designed and optimized for FastAPI applications.\r\n- **Multiple Tenancy Strategies:**\r\n    - `DATABASE_PER_TENANT`: Each tenant has a separate database.\r\n    - `SCHEMA_PER_TENANT`: Each tenant has a separate schema within the same database.\r\n    - `ROW_LEVEL`: All tenants share the same database and tables, with data isolated by a tenant identifier column.\r\n- **Context-Aware:** Uses `contextvars` to safely manage the current tenant's context, making it suitable for asynchronous applications.\r\n- **FastAPI Integration:** Provides ready-to-use middlewares and decorators specifically for FastAPI.\r\n- **Easy Setup:** Simple configuration with `quick_start()` and `initialize_hidra_fastapi()` functions\r\n- **Enhanced Decorators:** Improved `requires_tenant()` decorator with more flexible options\r\n- **Simplified Database Access:** `HidraDB` class for easier database session management\r\n- **Schema Management:** Advanced schema management tools to ensure proper tenant schema creation and naming compatibility with PostgreSQL (e.g., replacing hyphens with underscores).\r\n- **Developer-Controlled Tenants Table:** The structure of the `tenants` table is defined by the developer to match business requirements, with optional convenience functions for common use cases.\r\n- **Automatic Schema Validation:** Built-in validation and cleaning of tenant names for PostgreSQL compatibility.\r\n- **Diagnostic Tools:** Built-in functions to diagnose configuration issues\r\n- **Helpful Error Messages:** Clear error messages with suggestions for resolution\r\n\r\n## Installation\r\n\r\nInstall the library using `pip`:\r\n\r\n```bash\r\n# Core library\r\npip install hidra\r\n\r\n# To include optional dependencies for FastAPI\r\npip install hidra[fastapi]\r\n\r\n# For development (includes testing and linting tools)\r\npip install hidra[dev]\r\n```\r\n\r\n## Enhanced Usage Examples\r\n\r\n### Quick Start Configuration (With Predefined Tenants)\r\n\r\n```python\r\nfrom hidra import quick_start\r\n\r\n# Simple configuration with predefined tenants\r\nconfig = quick_start(\r\n    db_config={\r\n        \"db_driver\": \"postgresql\",\r\n        \"db_host\": \"localhost\",\r\n        \"db_port\": \"5432\",\r\n        \"db_username\": \"postgres\",\r\n        \"db_password\": \"password\",\r\n        \"db_name\": \"multitenant\"\r\n    },\r\n    tenants={\r\n        \"company1\": {\"plan\": \"premium\"},\r\n        \"company2\": {\"plan\": \"basic\"}\r\n    }\r\n)\r\n```\r\n\r\n### Minimal Configuration for FastAPI (No Predefined Tenants Required)\r\n\r\n```python\r\nfrom fastapi import FastAPI\r\nfrom hidra import create_hidra_app\r\n\r\n# Create FastAPI app with minimal configuration\r\napp = create_hidra_app(\r\n    db_config={\r\n        \"db_driver\": \"postgresql\",\r\n        \"db_host\": \"localhost\",\r\n        \"db_port\": \"5432\",\r\n        \"db_username\": \"postgres\",\r\n        \"db_password\": \"password\",\r\n        \"db_name\": \"multitenant\"\r\n    },\r\n    # Tenants are loaded automatically when requested\r\n    enable_auto_loading=True,\r\n    auto_tenant_validation=True  # Validate tenants exist before processing\r\n)\r\n\r\n# Protected endpoint - no need to define tenants in code\r\n@app.get(\"/data\")\r\nasync def get_data():\r\n    from hidra import get_current_tenant_id\r\n    tenant_id = get_current_tenant_id()\r\n    return {\"tenant_id\": tenant_id, \"message\": \"Data retrieved\"}\r\n```\r\n\r\n### Enhanced Decorators\r\n\r\n```python\r\nfrom hidra import requires_tenant\r\n\r\n# Any tenant with access\r\n@app.get(\"/data\")\r\n@requires_tenant()\r\nasync def get_data():\r\n    pass\r\n\r\n# Specific tenant only\r\n@app.get(\"/premium-data\")\r\n@requires_tenant(\"premium_company\")\r\nasync def get_premium_data():\r\n    pass\r\n\r\n# Multiple tenants allowed\r\n@app.get(\"/shared-data\")\r\n@requires_tenant([\"company1\", \"company2\"])\r\nasync def get_shared_data():\r\n    pass\r\n```\r\n\r\n### Simplified Database Access\r\n\r\n```python\r\nfrom hidra import HidraDB\r\n\r\n# Create database access\r\nhidra_db = HidraDB({\r\n    \"db_driver\": \"postgresql\",\r\n    \"db_host\": \"localhost\",\r\n    \"db_port\": \"5432\",\r\n    \"db_username\": \"postgres\",\r\n    \"db_password\": \"password\",\r\n    \"db_name\": \"multitenant\"\r\n})\r\n\r\n# Use with FastAPI Depends\r\n@app.get(\"/users\")\r\nasync def get_users(db: Session = Depends(hidra_db.get_tenant_db())):\r\n    pass\r\n```\r\n\r\n### Basic Usage (with FastAPI)\r\n\r\nHere's a quick example of how to use Hidra with FastAPI.\r\n\r\n#### 1. Configure Tenants\r\n\r\nFirst, configure your tenants and the tenancy strategy.\r\n\r\n```python\r\n# main.py\r\nfrom hidra import tenant_context, TenancyStrategy, MultiTenantManager\r\n\r\n# Initialize the tenant manager\r\nmanager = MultiTenantManager()\r\n\r\n# Configure individual tenants\r\nmanager.configure_tenant(\"tenant1\", {\"db_connection\": \"postgresql://user:pass@host/db1\"})\r\nmanager.configure_tenant(\"tenant2\", {\"db_connection\": \"postgresql://user:pass@host/db2\"})\r\n\r\n# Set the default strategy\r\nmanager.set_default_strategy(TenancyStrategy.DATABASE_PER_TENANT)\r\n\r\n# Set the manager in the global tenant context\r\ntenant_context.tenant_manager = manager\r\n```\r\n\r\n#### 2. Add the Middleware\r\n\r\nThe middleware identifies the tenant from the request (e.g., using a header) and sets it in the context.\r\n\r\n```python\r\n# main.py\r\nfrom fastapi import FastAPI\r\nfrom hidra.middleware import TenantMiddleware\r\nfrom hidra.decorators import tenant_required\r\n\r\napp = FastAPI()\r\n\r\n# Add the middleware to your application\r\napp.add_middleware(TenantMiddleware)\r\n\r\n@app.get(\"/items\")\r\n@tenant_required\r\nasync def get_items():\r\n    # The tenant is now available in the context\r\n    current_tenant = tenant_context.require_tenant()\r\n    \r\n    # You can get tenant-specific configuration\r\n    config = tenant_context.tenant_manager.get_tenant_config(current_tenant)\r\n    \r\n    return {\"tenant_id\": current_tenant, \"db_connection\": config.get(\"db_connection\")}\r\n```\r\n\r\n### 3. Run the Application\r\n\r\nTo run this example, you would make a request with the `X-Tenant-ID` header:\r\n\r\n```bash\r\ncurl -X GET \"http://127.0.0.1:8000/items\" -H \"X-Tenant-ID: tenant1\"\r\n```\r\n\r\nThe response would be:\r\n\r\n```json\r\n{\r\n  \"tenant_id\": \"tenant1\",\r\n  \"db_connection\": \"postgresql://user:pass@host/db1\"\r\n}\r\n```\r\n\r\n### Complete FastAPI Integration\r\n\r\n```python\r\nfrom fastapi import FastAPI, Depends\r\nfrom sqlalchemy.orm import Session\r\nfrom hidra import create_hidra_app, requires_tenant\r\n\r\n# Create FastAPI app with Hidra integration\r\napp = create_hidra_app(\r\n    db_config={\r\n        \"db_driver\": \"postgresql\",\r\n        \"db_host\": \"localhost\",\r\n        \"db_port\": \"5432\",\r\n        \"db_username\": \"postgres\",\r\n        \"db_password\": \"password\",\r\n        \"db_name\": \"multitenant\"\r\n    }\r\n)\r\n\r\n# Protected endpoint\r\n@app.get(\"/users\")\r\n@requires_tenant()  # Requires tenant\r\nasync def get_users():\r\n    from hidra import get_current_tenant_id\r\n    tenant_id = get_current_tenant_id()\r\n    return {\"tenant_id\": tenant_id, \"message\": \"Users retrieved\"}\r\n```\r\n\r\n## Diagnostic Tools\r\n\r\n```python\r\nfrom hidra import diagnose_setup, print_diagnosis\r\n\r\n# Get diagnosis as dictionary\r\ndiagnosis = diagnose_setup()\r\n\r\n# Print formatted diagnosis\r\nprint_diagnosis()\r\n```\r\n\r\n## Contributing\r\n\r\nContributions 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:\r\n\r\n1.  Fork the repository.\r\n2.  Create a new branch for your feature or bug fix.\r\n3.  Write your code and add tests.\r\n4.  Ensure all tests pass and the code is formatted with `black` and linted with `ruff`.\r\n5.  Submit a pull request.\r\n\r\n## License\r\n\r\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A lightweight multitenancy library for FastAPI applications",
    "version": "0.2.0b1",
    "project_urls": null,
    "split_keywords": [
        "multitenancy",
        " fastapi",
        " postgresql",
        " sqlalchemy",
        " tenancy"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2078ee5f641047b6063d6fa6ba1358aa4f953d7421d465b403b186a7e2277a28",
                "md5": "f5a8bf84f315d2271b421d499ecb73f5",
                "sha256": "f6d0a238b75b520ea7ce9a0090b8a0c4ee4c07581dddfcd0a48faee9b1dcacc4"
            },
            "downloads": -1,
            "filename": "hidra-0.2.0b1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f5a8bf84f315d2271b421d499ecb73f5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 25210,
            "upload_time": "2025-11-01T14:44:14",
            "upload_time_iso_8601": "2025-11-01T14:44:14.659842Z",
            "url": "https://files.pythonhosted.org/packages/20/78/ee5f641047b6063d6fa6ba1358aa4f953d7421d465b403b186a7e2277a28/hidra-0.2.0b1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "901d957788317628edcd1ebd1049f7263012d98e2d90e0275cf5958859889788",
                "md5": "72051f63406170cd8f5f21298bfb3bd0",
                "sha256": "831c0e2d26bee941a92ad39a1d23e7648010216d81515f18b9ae892893f64a5d"
            },
            "downloads": -1,
            "filename": "hidra-0.2.0b1.tar.gz",
            "has_sig": false,
            "md5_digest": "72051f63406170cd8f5f21298bfb3bd0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 26426,
            "upload_time": "2025-11-01T14:44:15",
            "upload_time_iso_8601": "2025-11-01T14:44:15.472591Z",
            "url": "https://files.pythonhosted.org/packages/90/1d/957788317628edcd1ebd1049f7263012d98e2d90e0275cf5958859889788/hidra-0.2.0b1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-01 14:44:15",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "hidra"
}
        
Elapsed time: 1.67271s