Skip to main content

Ready-made FastAPI abstractions: repository, unit of work, service etc.

Project description

FastAPI Abstractions

Ready-made FastAPI abstractions: repository, unit of work, service etc.

Features

  • Focus on business logic: No need to write boilerplate code.
  • Scalable: You can use the provided abstractions and easily extend them.
  • Portable: Works with any database supported by SQLAlchemy.
  • Intuitive: Great editor support. Completion everywhere. Less time debugging.
  • Easy: Designed to be easy to use and learn. Less time reading docs.
  • Asynchronous: Built-in support for async/await.
  • Use anywhere: Use it with FastAPI, Aiogram, or any other framework (best with FastAPI).

Requirements

FastAPI Abstractions stands on the shoulders of giants:

Installation

pip install fastabc

Concepts

1. Models

from datetime import datetime

from sqlalchemy import Identity
from sqlalchemy.orm import Mapped, mapped_column

from fastabc import DeclarativeBase, AlchemyEntity
from fastabc.alchemy import SoftDeletable, HasID, HasTimestamp


# Classic way
class User(DeclarativeBase):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]
    created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    updated_at: Mapped[datetime] = mapped_column(
        default=datetime.now, onupdate=datetime.now
    )


# Using AlchemyEntity
class UserAlchemyEntity(AlchemyEntity):
    __tablename__ = "users"

    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]

    # These columns will be added automatically
    # id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    # created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    # updated_at: Mapped[datetime] = mapped_column(
    #     default=datetime.now, onupdate=datetime.now
    # )


# Using mixins
class UserMixins(HasID, HasTimestamp, SoftDeletable):
    __tablename__ = "users"

    email: Mapped[str] = mapped_column(unique=True, index=True)
    password: Mapped[str]
    # These columns will be added automatically
    # id: Mapped[int]
    # created_at: Mapped[datetime] = mapped_column(default=datetime.now)
    # updated_at: Mapped[datetime] = mapped_column(
    #     default=datetime.now, onupdate=datetime.now
    # )
    # deleted_at: Mapped[datetime | None] = None

2. Repositories

from fastabc import AlchemyRepository
from .models import User


class UserRepository(AlchemyRepository[User]):
    model_type = User

3. Unit of Work

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

from fastabc import AlchemyUOW
from .repositories import UserRepository


class UOW(AlchemyUOW):
    users: UserRepository

    async def on_open(self) -> None:
        self.users = UserRepository(self.session)


async_engine = create_async_engine("sqlite+aiosqlite:///memory")
async_session = async_sessionmaker(async_engine)

uow = UOW(async_session)

4. Services

from fastabc import AlchemyService
from .models import User
from .uow import UOW


class UserService(AlchemyService):
    uow: UOW

    async def create(self, email: str, password: str) -> User:
        user = User(email=email, password=password)
        self.uow.users.add(user)
        await self.uow.commit()

        return user

    async def get(self, user_id: int) -> User | None:
        return await self.uow.users.get(user_id)

    async def get_by_email(self, email: str) -> User | None:
        return await self.uow.users.get_by_where(where=[User.email == email])

    async def create_many(self, users: list[User]) -> list[User]:
        await self.uow.users.insert(*users)
        await self.uow.commit()

        return users

    async def get_by_domain(self, domain: str) -> list[User]:
        return await self.uow.users.get_many(where=[User.email.like(f"%@{domain}")])

    # And more...

5. Use Cases

from faker import Faker

from .models import User
from .service import UserService
from .uow import uow

fake = Faker()


def get_users(domains: list[str], number: int = 10) -> list[User]:
    users = [User(email=fake.email(domain=domain), password=fake.password()) for _ in range(number) for domain in
             domains]
    return users


async def main():
    async with uow:
        service = UserService(uow)

        user = await service.create("user@example.com", "qwerty12345")
        print(user)

        user = await service.get(user.id)
        print(user)

        user = await service.get_by_email("user@example.com")
        print(user)

        users = get_users(["gmail.com", "yahoo.com"])
        await service.create_many(*users)

        users = await service.get_by_domain("gmail.com")
        print(users)

Made with love ❤️

Download files

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

Source Distribution

fastabc-0.1.8.tar.gz (11.1 kB view details)

Uploaded Source

Built Distribution

fastabc-0.1.8-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file fastabc-0.1.8.tar.gz.

File metadata

  • Download URL: fastabc-0.1.8.tar.gz
  • Upload date:
  • Size: 11.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11

File hashes

Hashes for fastabc-0.1.8.tar.gz
Algorithm Hash digest
SHA256 9db255b5d378c5c660ed7152c0912f618fb08066b8fb6302dbc6259d2ee7d6f0
MD5 f0d0e6c595c24cd6220db15436558d87
BLAKE2b-256 68460e67e7a5df91af0c6cb62ae4aa9dabd4908f386da989765f2276513739c5

See more details on using hashes here.

File details

Details for the file fastabc-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: fastabc-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11

File hashes

Hashes for fastabc-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 7b13f6549877b4737fa8519347272fbc834b11ca5292e4f7c9786811c2b51446
MD5 7f64c5bed3051da1e235bf1b98999647
BLAKE2b-256 21cf66715b26e424cac90de6dd425cad824d2e03f3a773ccbb2a257c4cac750d

See more details on using hashes here.

Supported by

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