# auen

Auto-generate FastAPI CRUD endpoints from SQLModel models.

## Why auen?

Every CRUD API requires the same scaffolding: endpoint functions, request/response schemas, pagination, filtering, sorting, auth gating, row-level policies, and error handling. Whether written by hand or generated by an AI coding agent, this code is structurally identical across projects — yet every team writes it from scratch, introducing subtle bugs each time.

auen is a trusted library that replaces this repetition. Instead of copy-pasting hundreds of lines of endpoint boilerplate, you declare what you need and auen generates correct, tested endpoints. Every line left in your codebase carries real information about your domain.

## Features

- **One-line CRUD**: `CrudRouterBuilder` generates POST, GET, PATCH, DELETE endpoints — replacing ~150 lines of hand-written route code per model
- **Schema separation**: Explicit Create/Read/Update schemas prevent field leakage — a security best practice that's tedious to maintain by hand
- **Auth hooks**: Pluggable authentication via FastAPI dependencies — standardizes a pattern every project copies
- **Row-level policies**: Filter LIST queries and gate access per-object — frequently reimplemented incorrectly when done manually
- **Pagination & filtering**: Configurable limits, sorting, and field filters — boilerplate every API writes the same way
- **Lifecycle hooks**: before/after hooks for create, update, delete — cross-cutting concerns without endpoint duplication
- **Bulk operations**: Batch create, update, delete — common patterns that are error-prone to implement correctly
- **Typed**: Full type annotations with `py.typed` marker (PEP 561)

## Installation

```bash
pip install auen
```

## Quick Example

```python
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
from sqlmodel import Field, SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession

from auen import CrudRouterBuilder

engine = create_async_engine("sqlite+aiosqlite:///bookstore.db")


class Book(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    isbn: str
    author_id: int | None = Field(default=None, foreign_key="author.id")


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)
    yield


async def get_session() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSession(engine) as session:
        yield session


app = FastAPI(lifespan=lifespan)
app.include_router(
    CrudRouterBuilder.for_model(Book, get_session).build()
)
```
