Skip to main content

Auto-generate FastAPI CRUD endpoints from SQLModel models

Project description

auen

Auto-generate FastAPI CRUD endpoints from SQLModel models.

Installation

pip install auen

Quickstart

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:///heroes.db")


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


@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(Hero, get_session).build())

This gives you:

Method Path Description
POST /heros/ Create a hero
GET /heros/ List heroes
GET /heros/{id} Get a hero
PATCH /heros/{id} Update a hero
DELETE /heros/{id} Delete a hero

Explicit Schemas (Recommended)

For production, define explicit schemas instead of auto-derived ones:

from pydantic import BaseModel
from auen import CrudRouterBuilder, SchemaConfig


class HeroCreate(BaseModel):
    name: str
    secret_name: str
    age: int | None = None


class HeroRead(BaseModel):
    id: int
    name: str
    secret_name: str
    age: int | None = None


class HeroUpdate(BaseModel):
    name: str | None = None
    secret_name: str | None = None
    age: int | None = None


app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_schemas(
        SchemaConfig(
            create=HeroCreate,
            read=HeroRead,
            update=HeroUpdate,
        )
    )
    .build()
)

Authentication

from fastapi import Header, HTTPException
from auen import AuthConfig, CrudRouterBuilder


def get_current_user(authorization: str = Header()) -> str:
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401)
    return authorization[7:]  # return user identity


app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_auth(AuthConfig(dependency=get_current_user))
    .build()
)

Row-Level Policies

from auen import AuthConfig, CrudRouterBuilder


class OwnerPolicy:
    def can_create(self, user, obj_in):
        return True

    def can_read(self, user, db_obj):
        return db_obj.owner_id == user

    def can_update(self, user, db_obj, obj_in):
        return db_obj.owner_id == user

    def can_delete(self, user, db_obj):
        return db_obj.owner_id == user

    def filter_list_query(self, user, query):
        return query.where(Hero.owner_id == user)


app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_auth(AuthConfig(dependency=get_current_user))
    .with_policy(OwnerPolicy())
    .build()
)

Lifecycle Hooks

from auen import CrudRouterBuilder, HooksConfig


async def send_welcome_email(session, user_obj, current_user):
    ...  # send email, log event, publish to queue, etc.


async def validate_before_create(session, obj_in, current_user):
    ...  # raise HTTPException to abort


app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_hooks(
        HooksConfig(
            before_create=validate_before_create,
            after_create=send_welcome_email,
        )
    )
    .build()
)

Pagination & Filtering

from auen import CrudRouterBuilder, PaginationConfig, FilterConfig, FilterFieldConfig

app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_pagination(PaginationConfig(default_limit=20, max_limit=100))
    .with_filters(
        FilterConfig(
            fields={"name": FilterFieldConfig(ops=frozenset({"eq"}))},
            sort_fields=["name", "age"],
        )
    )
    .build()
)

Selecting Operations

from auen import CrudRouterBuilder, Operation

app.include_router(
    CrudRouterBuilder.for_model(Hero, get_session)
    .with_operations({Operation.LIST, Operation.READ})  # read-only API
    .build()
)

Development

# Install dependencies
uv sync

# Run tests
uv run pytest

# Format
uv run ruff format .

# Lint
uv run ruff check .

# Type check
uv run ty check

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

auen-0.2.2.tar.gz (113.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

auen-0.2.2-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file auen-0.2.2.tar.gz.

File metadata

  • Download URL: auen-0.2.2.tar.gz
  • Upload date:
  • Size: 113.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for auen-0.2.2.tar.gz
Algorithm Hash digest
SHA256 ffde63026109964672a613b095e313ea39d0f2b4246d02399aae423e9cb92b4b
MD5 5280c1eccf248ceb73a3b9a0b307e958
BLAKE2b-256 2ebcc885d47357503c77958d6e5e59428a58b14467e52444768c0d44584f7207

See more details on using hashes here.

File details

Details for the file auen-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: auen-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for auen-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bfe88acc0cb7c1a27cf43dbc667d7c333601a4999ecfa264c3692db6ed272266
MD5 eed2d7114b7a395bac62d2c8d75f82cc
BLAKE2b-256 f4942bf11add9726cc0d0fc1e35bcbd6d88ef96358495443f46d0d4c4bc89ad6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page