SQLArgon¶
SQLAlchemy repository pattern and utilities
Version: 1.0.0b1
Docs: https://asynq-io.github.io/sqlargon/
Repository: https://github.com/asynq-io/sqlargon
About¶
SQLArgon provides glue code to use SQLAlchemy async sessions, core queries and ORM models from one object which provides somewhat of a repository pattern. This solution has a few advantages:
- no need to pass a
sessionobject to every function/method — sessions are context-local and resolved by the repository itself - write data access queries in one place
- no need to import
insert,update,delete,selectfrom SQLAlchemy over and over again - implicit cast of results to
.scalars().all(),.one(),.mappings(), ... - a dialect-aware query builder for upserts,
RETURNINGand advisory locks - your view model (e.g. FastAPI routes) does not need to know about the underlying storage — the repository class can be replaced at any moment with any object providing a similar interface
- engines and routing policy are separate, so the same repository runs against one database, a primary with read replicas, or a set of shards
Installation¶
or
Drivers and optional features ship as extras:
| Extra | Contents |
|---|---|
postgres |
asyncpg |
sqlite |
aiosqlite |
mysql |
asyncmy |
pagination |
sqlakeyset, required for cursor pagination |
cron |
croniter, anyio |
opentelemetry |
opentelemetry-instrumentation-sqlalchemy |
standard |
all of the above |
Quickstart¶
import asyncio
import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column
from sqlargon import Base, Database, SQLAlchemyRepository, set_default_database
from sqlargon.mixins import CreatedUpdatedMixin, UUIDModelMixin
set_default_database(Database(url="postgresql+asyncpg://localhost:5432/app"))
class User(UUIDModelMixin, CreatedUpdatedMixin, Base):
name: Mapped[str] = mapped_column(sa.Unicode(255))
last_name: Mapped[str | None] = mapped_column(sa.Unicode(255), nullable=True)
class UserRepository(SQLAlchemyRepository[User]):
default_order_by = User.created_at.desc()
async def get_by_name(self, name: str) -> User:
return await self.select().filter_by(name=name).one()
async def main() -> None:
users = UserRepository()
await users.create(name="John")
print(await users.list(User.name == "John"))
asyncio.run(main())
The model is taken from the generic parameter, and __init__ takes no arguments — the
repository resolves its database when a statement runs, from set_default_database(...)
or from DATABASE_* environment variables.
Where to go next¶
- Usage — models, CRUD, query building, transactions and units of work.
- Database Routing — replicas, shards, routers and FastAPI wiring.
- Pagination — page-number, offset/limit and cursor strategies.
- Examples — end-to-end recipes: a FastAPI service, batch workers, multi-tenant sharding, testing.
- Reference — types and mixins, dialects, settings and the API reference.
- Alembic Migrations — async-only migration setup.