Metadata-Version: 2.4
Name: macss-modular-api
Version: 0.4.6
Summary: Use-case-centric toolkit for building modular APIs with Starlette. Define UseCase classes (input → validate → execute → output), connect them to HTTP routes, and expose OpenAPI documentation automatically.
Author: ccisne.dev
License-Expression: MIT
Project-URL: Homepage, https://github.com/macss-dev/modular_api
Project-URL: Repository, https://github.com/macss-dev/modular_api/tree/main/code/py
Project-URL: Issues, https://github.com/macss-dev/modular_api/issues
Project-URL: Documentation, https://github.com/macss-dev/modular_api/tree/main/code/py#readme
Keywords: api,usecase,openapi,starlette,modular,macss
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: starlette>=0.40
Requires-Dist: pydantic>=2.0
Provides-Extra: serve
Requires-Dist: uvicorn>=0.30; extra == "serve"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: uvicorn>=0.30; extra == "dev"
Dynamic: license-file

# modular-api

Use-case-centric toolkit for building modular APIs with Starlette.  
Define `UseCase` classes (input → validate → execute → output), connect them to HTTP routes, and get automatic OpenAPI documentation.

> Also available in **Dart**: [modular_api](https://pub.dev/packages/modular_api) · **TypeScript**: [@macss/modular-api](https://www.npmjs.com/package/@macss/modular-api)

---

## Quick start

```python
from modular_api import ModularApi, ModuleBuilder

# ─── Module builder (separate file in real projects) ──────────
def build_greetings_module(m: ModuleBuilder) -> None:
    m.usecase("hello", HelloWorld)

# ─── Server ───────────────────────────────────────────────────
api = ModularApi(base_path="/api")

api.module("greetings", build_greetings_module)

api.serve(port=8080)
```

```bash
curl -X POST http://localhost:8080/api/greetings/hello \
  -H "Content-Type: application/json" \
  -d '{"name":"World"}'
```

```json
{ "message": "Hello, World!" }
```

**Docs** → `http://localhost:8080/docs`  
**Health** → `http://localhost:8080/health`  
**OpenAPI JSON** → `http://localhost:8080/openapi.json` *(also /openapi.yaml)*  
**Metrics** → `http://localhost:8080/metrics` *(opt-in)*

See `example/example.py` for the full implementation including Input, Output, UseCase with `validate()`, and the builder.

---

## Features

- `UseCase[I, O]` — pure business logic, no HTTP concerns
- `Input` / `Output` — DTOs with automatic OpenAPI schema generation via Pydantic `Field()`
- `Output.status_code` — custom HTTP status codes per response
- `UseCaseException` — structured error handling (status_code, message, error_code, details)
- `ModularApi` + `ModuleBuilder` — module registration and routing
- Constructor-based unit testing with fake dependency injection
- `cors_middleware` — built-in CORS support
- Scalar docs at `/docs` — auto-generated from registered use cases
- OpenAPI spec at `/openapi.json` and `/openapi.yaml` — raw spec download
- Health check at `GET /health` — [IETF Health Check Response Format](doc/health_check_guide.md)
- Prometheus metrics at `GET /metrics` — [Prometheus exposition format](doc/metrics_guide.md)
- Structured JSON logging — Loki/Grafana compatible, [request-scoped with trace_id](doc/logger_guide.md)
- All endpoints default to `POST` (configurable per use case)
- Full type annotations with `py.typed` marker (PEP 561)

---

## Installation

```bash
pip install modular-api
```

With Uvicorn for `api.serve()`:

```bash
pip install modular-api[serve]
```

---

## Error handling

```python
async def execute(self) -> FoundUserOutput:
    user = await repository.find_by_id(self.input.user_id)
    if not user:
        raise UseCaseException(
            status_code=404,
            message="User not found",
            error_code="USER_NOT_FOUND",
        )
    return FoundUserOutput(name=user.name)
```

---

## Testing

```python
async def test_hello_world():
    usecase = HelloWorld(HelloInput(name="World"))
    error = usecase.validate()
    assert error is None

    output = await usecase.execute()
    assert output.message == "Hello, World!"
```

See [doc/testing_guide.md](doc/testing_guide.md) for the full testing guide.

---

## License

MIT — see [LICENSE](LICENSE).
