Metadata-Version: 2.4
Name: fastapi-fabric-core
Version: 0.1.0
Summary: Shared foundation for fastapi-fabric — DB engine, config loading, HTTP middleware, rate limiting.
Project-URL: Homepage, https://git.punakawanlab.id/PunakawanLab/fastapi-fabric
Project-URL: Repository, https://git.punakawanlab.id/PunakawanLab/fastapi-fabric
Author-email: Yoiq S Rambadian <yoiqsram@punakawanlab.id>
License-Expression: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Requires-Python: >=3.10
Requires-Dist: aiosqlite>=0.20
Requires-Dist: fastapi[standard]>=0.136.3
Requires-Dist: pydantic-settings>=2.14.1
Requires-Dist: sqlalchemy[asyncio]>=2.0
Requires-Dist: structlog>=25.5.0
Requires-Dist: uuid-utils>=0.16.0
Description-Content-Type: text/markdown

# fastapi-fabric-core

Shared foundation for the fastapi-fabric ecosystem. Provides database engine management, config loading, HTTP middleware, and utilities used by all other packages.

## Install

`fastapi-fabric-core` is installed automatically as a dependency of any fastapi-fabric extra:

```bash
pip install fastapi-fabric[auth]
```

## What's included

| Feature | Description |
|---|---|
| **Config loading** | `config.yaml` + environment variable overrides via pydantic-settings |
| **DB engine** | Async SQLAlchemy engine init, session factory, `BaseSchema` for ORM models |
| **HTTP logging** | Structured request/response logging middleware |
| **Request ID** | Per-request UUID propagated through headers and logs |
| **Rate limiting** | In-memory sliding window rate limiter (single-process) |
| **Pagination** | Shared `Page` response model |
| **Error helpers** | Standard error response shapes |

## Config

Create `config.yaml` next to your app entry point:

```yaml
auth:
  secret_key: "your-random-32-char-secret"
  system_admin_password: "changeme"

server:
  debug: false
  secure_cookies: true   # set false behind HTTP (dev only)
```

The config path is resolved relative to the file that first imports `fastapi_fabric.core.config`. Override with the `CONFIG_YAML` environment variable.

```python
from fastapi_fabric.core.config import get_config

config = get_config()  # cached; reads config.yaml once
```

## Database

```python
from fastapi_fabric.core.db import DatabaseModel, get_engine, get_session, init_engine

# Call once at startup
init_engine("sqlite+aiosqlite:///./app.db")
# or
init_engine("postgresql+asyncpg://user:pass@localhost/mydb")

# Create tables for all registered ORM models
engine = get_engine()
async with engine.begin() as conn:
    await conn.run_sync(DatabaseModel.metadata.create_all)

# Use in FastAPI routes
async def my_route(session: AsyncSession = Depends(get_session)):
    ...
```

ORM models subclass `DatabaseModel` (never SQLAlchemy's `DeclarativeBase` directly):

```python
from fastapi_fabric.core.db import DatabaseModel
from sqlalchemy.orm import Mapped, mapped_column

class MyModel(DatabaseModel):
    __tablename__ = "my_table"
    id: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str]
```

## Middleware

Add structured HTTP access logging to your app:

```python
from fastapi_fabric.core.middleware.http_log import HTTPAccessLogMiddleware

app.add_middleware(HTTPAccessLogMiddleware)
```

Per-request IDs (UUIDv7) are available via a dependency rather than middleware:

```python
from fastapi_fabric.core.dependencies.request_id import get_request_id

@app.get("/items")
async def list_items(request_id: str = Depends(get_request_id)):
    ...
```

## Rate limiting

```python
from fastapi_fabric.core.dependencies.rate_limit import RateLimitConfig, rate_limiter

@app.post(
    "/login",
    dependencies=[Depends(rate_limiter())],
    openapi_extra={"x-rate-limit": RateLimitConfig(limit=5, window=60)},
)
async def login():
    ...
```

Limits are per-IP (unauthenticated) or per-principal (authenticated). State is in-process and does not synchronize across workers.
