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.0.tar.gz (112.9 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.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: auen-0.2.0.tar.gz
  • Upload date:
  • Size: 112.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for auen-0.2.0.tar.gz
Algorithm Hash digest
SHA256 82b3100c7b89dedcca3b9cf65fe4ac3b86260110cf5e11caa9297b3ed699ae92
MD5 2472d95e80ffbc16be374d56a1ba8ffb
BLAKE2b-256 3c6ec2de41bf854f3bcd87f42fe1eadb52ae85d03c865affbb1ac12709d49193

See more details on using hashes here.

File details

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

File metadata

  • Download URL: auen-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for auen-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c5d7880d966bea0b0a2193fb8af7947c3d7c9c2a15153bca15a179ab45aea86a
MD5 b615634b0d26061eefd4a6f7f129cd2f
BLAKE2b-256 577702e7ca407a14f24b1b7f1cf835d69b60da55cdb038d6fe3e9b5feabfa38f

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