Metadata-Version: 2.4
Name: fastapi-error-handler
Version: 0.1.0
Summary: Elegant, consistent error handling for FastAPI applications
License: MIT
Project-URL: Homepage, https://github.com/shahabRDZ/fastapi-error-handler
Project-URL: Repository, https://github.com/shahabRDZ/fastapi-error-handler
Project-URL: Issues, https://github.com/shahabRDZ/fastapi-error-handler/issues
Keywords: fastapi,error-handling,exceptions,api,rest
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.100.0
Dynamic: license-file

<h1 align="center">fastapi-error-handler</h1>

<p align="center">
  <strong>Elegant, consistent error handling for FastAPI — one line setup</strong>
</p>

<p align="center">
  <img src="https://img.shields.io/pypi/v/fastapi-error-handler?color=blue" alt="PyPI" />
  <img src="https://img.shields.io/badge/python-3.10+-blue?logo=python&logoColor=white" alt="Python" />
  <img src="https://img.shields.io/badge/FastAPI-0.100+-009688?logo=fastapi&logoColor=white" alt="FastAPI" />
  <img src="https://img.shields.io/badge/license-MIT-green" alt="License" />
</p>

---

## The Problem

Every FastAPI app needs error handling. Without it, your API returns inconsistent, ugly responses:

```json
{"detail": "Not Found"}
{"detail": [{"loc": ["body", "email"], "msg": "field required", "type": "value_error.missing"}]}
500 Internal Server Error
```

Different formats, missing context, no error codes. Your frontend team hates you.

## The Solution

```bash
pip install fastapi-error-handler
```

```python
from fastapi import FastAPI
from fastapi_error_handler import setup_error_handlers

app = FastAPI()
setup_error_handlers(app)  # That's it.
```

Now **every** error returns a clean, consistent JSON response:

```json
{
    "error": "not_found",
    "message": "User with id '123' not found",
    "status": 404,
    "path": "/users/123"
}
```

## Built-in Exceptions

Raise these anywhere in your routes — they automatically produce the right HTTP response:

```python
from fastapi_error_handler import (
    NotFoundError,      # 404
    BadRequestError,    # 400
    UnauthorizedError,  # 401
    ForbiddenError,     # 403
    ConflictError,      # 409
    ValidationError,    # 422
    RateLimitError,     # 429
    InternalError,      # 500
)

@app.get("/users/{id}")
async def get_user(id: int):
    user = await db.get_user(id)
    if not user:
        raise NotFoundError("User", id=str(id))
    return user

@app.post("/users")
async def create_user(email: str):
    if await db.email_exists(email):
        raise ConflictError("A user with this email already exists")
    ...
```

## What Gets Handled

| Exception | Status | Response `error` field |
|-----------|--------|----------------------|
| `NotFoundError` | 404 | `not_found` |
| `BadRequestError` | 400 | `bad_request` |
| `UnauthorizedError` | 401 | `unauthorized` |
| `ForbiddenError` | 403 | `forbidden` |
| `ConflictError` | 409 | `conflict` |
| `ValidationError` | 422 | `validation_error` |
| `RateLimitError` | 429 | `rate_limit_exceeded` |
| `InternalError` | 500 | `internal_error` |
| Pydantic errors | 422 | `validation_error` (with field details) |
| Starlette HTTP | any | mapped from status code |
| Unhandled `Exception` | 500 | `internal_error` (safe message) |

## Configuration

```python
setup_error_handlers(
    app,
    log_errors=True,       # Log 4xx as WARNING, 5xx as ERROR
    include_path=True,     # Include request path in response
    on_error=my_callback,  # Custom callback on every error
)
```

### Custom Error Callback

```python
def send_to_sentry(request, exc):
    sentry_sdk.capture_exception(exc)

setup_error_handlers(app, on_error=send_to_sentry)
```

## Custom Exceptions

Extend `AppError` for domain-specific errors:

```python
from fastapi_error_handler import AppError

class InsufficientBalanceError(AppError):
    status_code = 402
    error_code = "insufficient_balance"

    def __init__(self, balance: float, required: float):
        super().__init__(
            message=f"Insufficient balance: have {balance}, need {required}",
            details={"balance": balance, "required": required},
        )
```

## Response Format

Every error response follows this schema:

```json
{
    "error": "string",       // machine-readable error code
    "message": "string",     // human-readable message
    "status": 404,           // HTTP status code
    "path": "/users/123",    // request path (optional)
    "details": {}            // additional context (optional)
}
```

## License

MIT
