{"id":17133478,"url":"https://github.com/hadrien/FastSQLA","last_synced_at":"2025-07-25T10:32:58.440Z","repository":{"id":255557396,"uuid":"820593126","full_name":"hadrien/fastapi-async-sqla","owner":"hadrien","description":"SQLAlchemy extension for FastAPI with support for asyncio and pagination.","archived":false,"fork":false,"pushed_at":"2024-09-09T19:10:01.000Z","size":62,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-14T19:42:49.221Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hadrien.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-26T19:34:49.000Z","updated_at":"2024-09-09T23:11:39.000Z","dependencies_parsed_at":"2024-09-06T02:42:12.909Z","dependency_job_id":"cda14a1c-069a-4ad2-8cdd-c5694d40c341","html_url":"https://github.com/hadrien/fastapi-async-sqla","commit_stats":null,"previous_names":["hadrien/fastapi-async-sqla"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadrien%2Ffastapi-async-sqla","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadrien%2Ffastapi-async-sqla/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadrien%2Ffastapi-async-sqla/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hadrien%2Ffastapi-async-sqla/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hadrien","download_url":"https://codeload.github.com/hadrien/fastapi-async-sqla/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227564805,"owners_count":17787070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:42:16.821Z","updated_at":"2025-07-25T10:32:58.410Z","avatar_url":"https://github.com/hadrien.png","language":"Python","readme":"# FastSQLA\n\n_Async SQLAlchemy 2.0+ for FastAPI — boilerplate, pagination, and seamless session management._\n\n[![PyPI - Version](https://img.shields.io/pypi/v/FastSQLA?color=brightgreen)](https://pypi.org/project/FastSQLA/)\n[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/hadrien/fastsqla/ci.yml?branch=main\u0026logo=github\u0026label=CI)](https://github.com/hadrien/FastSQLA/actions?query=branch%3Amain+event%3Apush)\n[![Codecov](https://img.shields.io/codecov/c/github/hadrien/fastsqla?token=XK3YT60MWK\u0026logo=codecov)](https://codecov.io/gh/hadrien/FastSQLA)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-brightgreen.svg)](https://conventionalcommits.org)\n[![GitHub License](https://img.shields.io/github/license/hadrien/fastsqla)](https://github.com/hadrien/FastSQLA/blob/main/LICENSE)\n[![🍁 With love from Canada](https://img.shields.io/badge/With%20love%20from%20Canada-ffffff?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIiB2aWV3Qm94PSItMjAxNSAtMjAwMCA0MDMwIDQwMzAiPjxwYXRoIGZpbGw9IiNmMDAiIGQ9Im0tOTAgMjAzMCA0NS04NjNhOTUgOTUgMCAwIDAtMTExLTk4bC04NTkgMTUxIDExNi0zMjBhNjUgNjUgMCAwIDAtMjAtNzNsLTk0MS03NjIgMjEyLTk5YTY1IDY1IDAgMCAwIDM0LTc5bC0xODYtNTcyIDU0MiAxMTVhNjUgNjUgMCAwIDAgNzMtMzhsMTA1LTI0NyA0MjMgNDU0YTY1IDY1IDAgMCAwIDExMS01N2wtMjA0LTEwNTIgMzI3IDE4OWE2NSA2NSAwIDAgMCA5MS0yN2wzMzItNjUyIDMzMiA2NTJhNjUgNjUgMCAwIDAgOTEgMjdsMzI3LTE4OS0yMDQgMTA1MmE2NSA2NSAwIDAgMCAxMTEgNTdsNDIzLTQ1NCAxMDUgMjQ3YTY1IDY1IDAgMCAwIDczIDM4bDU0Mi0xMTUtMTg2IDU3MmE2NSA2NSAwIDAgMCAzNCA3OWwyMTIgOTktOTQxIDc2MmE2NSA2NSAwIDAgMC0yMCA3M2wxMTYgMzIwLTg1OS0xNTFhOTUgOTUgMCAwIDAtMTExIDk4bDQ1IDg2M3oiLz48L3N2Zz4K)](https://montrealpython.org)\n\n**Documentation**: [https://hadrien.github.io/FastSQLA/](https://hadrien.github.io/FastSQLA/)\n\n**Github Repo:** [https://github.com/hadrien/fastsqla](https://github.com/hadrien/fastsqla)\n\n-----------------------------------------------------------------------------------------\n\n`FastSQLA` is an async [`SQLAlchemy 2.0+`](https://docs.sqlalchemy.org/en/20/)\nextension for [`FastAPI`](https://fastapi.tiangolo.com/) with built-in pagination,\n[`SQLModel`](http://sqlmodel.tiangolo.com/) support and more.\n\nIt streamlines the configuration and asynchronous connection to relational databases by\nproviding boilerplate and intuitive helpers. Additionally, it offers built-in\ncustomizable pagination and automatically manages the `SQLAlchemy` session lifecycle\nfollowing [`SQLAlchemy`'s best practices](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it).\n\n\n## Features\n\n* Easy setup at app startup using\n  [`FastAPI` Lifespan](https://fastapi.tiangolo.com/advanced/events/#lifespan):\n\n    ```python\n    from fastapi import FastAPI\n    from fastsqla import lifespan\n\n    app = FastAPI(lifespan=lifespan)\n    ```\n\n* `SQLAlchemy` async session dependency:\n\n    ```python\n    ...\n    from fastsqla import Session\n    from sqlalchemy import select\n    ...\n\n    @app.get(\"/heros\")\n    async def get_heros(session:Session):\n        stmt = select(...)\n        result = await session.execute(stmt)\n        ...\n    ```\n\n* `SQLAlchemy` async session with an async context manager:\n\n    ```python\n    from fastsqla import open_session\n\n    async def background_job():\n        async with open_session() as session:\n            stmt = select(...)\n            result = await session.execute(stmt)\n            ...\n    ```\n\n* Built-in pagination:\n\n    ```python\n    ...\n    from fastsqla import Page, Paginate\n    from sqlalchemy import select\n    ...\n\n    @app.get(\"/heros\", response_model=Page[HeroModel])\n    async def get_heros(paginate:Paginate):\n        return await paginate(select(Hero))\n    ```\n\n    \u003ccenter\u003e\n\n    👇 `/heros?offset=10\u0026limit=10` 👇\n\n    \u003c/center\u003e\n\n    ```json\n    {\n      \"data\": [\n        {\n          \"name\": \"The Flash\",\n          \"secret_identity\": \"Barry Allen\",\n          \"id\": 11\n        },\n        {\n          \"name\": \"Green Lantern\",\n          \"secret_identity\": \"Hal Jordan\",\n          \"id\": 12\n        }\n      ],\n      \"meta\": {\n        \"offset\": 10,\n        \"total_items\": 12,\n        \"total_pages\": 2,\n        \"page_number\": 2\n      }\n    }\n    ```\n\n* Pagination customization:\n    ```python\n    ...\n    from fastapi import Page, new_pagination\n    ...\n\n    Paginate = new_pagination(min_page_size=5, max_page_size=500)\n\n    @app.get(\"/heros\", response_model=Page[HeroModel])\n    async def get_heros(paginate:Paginate):\n        return paginate(select(Hero))\n    ```\n* Session lifecycle management: session is commited on request success or rollback on\n  failure.\n\n* [`SQLModel`](http://sqlmodel.tiangolo.com/) support:\n    ```python\n    ...\n    from fastsqla import Item, Page, Paginate, Session\n    from sqlmodel import Field, SQLModel\n    ...\n\n    class Hero(SQLModel, table=True):\n        id: int | None = Field(default=None, primary_key=True)\n        name: str\n        secret_identity: str\n        age: int\n\n\n    @app.get(\"/heroes\", response_model=Page[Hero])\n    async def get_heroes(paginate: Paginate):\n        return await paginate(select(Hero))\n\n\n    @app.get(\"/heroes/{hero_id}\", response_model=Item[Hero])\n    async def get_hero(session: Session, hero_id: int):\n        hero = await session.get(Hero, hero_id)\n        if hero is None:\n            raise HTTPException(status_code=HTTPStatus.NOT_FOUND)\n        return {\"data\": hero}\n    ```\n\n## Installing\n\nUsing [uv](https://docs.astral.sh/uv/):\n```bash\nuv add fastsqla\n```\n\nUsing [pip](https://pip.pypa.io/):\n```\npip install fastsqla\n```\n\n## Quick Example\n\n### `example.py`\n\nLet's write some tiny app in `example.py`:\n\n```python\n# example.py\nfrom http import HTTPStatus\n\nfrom fastapi import FastAPI, HTTPException\nfrom fastsqla import Base, Item, Page, Paginate, Session, lifespan\nfrom pydantic import BaseModel, ConfigDict\nfrom sqlalchemy import select\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.orm import Mapped, mapped_column\n\n\napp = FastAPI(lifespan=lifespan)\n\n\nclass Hero(Base):\n    __tablename__ = \"hero\"\n    id: Mapped[int] = mapped_column(primary_key=True)\n    name: Mapped[str] = mapped_column(unique=True)\n    secret_identity: Mapped[str]\n    age: Mapped[int]\n\n\nclass HeroBase(BaseModel):\n    name: str\n    secret_identity: str\n    age: int\n\n\nclass HeroModel(HeroBase):\n    model_config = ConfigDict(from_attributes=True)\n    id: int\n\n\n@app.get(\"/heros\", response_model=Page[HeroModel])\nasync def list_heros(paginate: Paginate):\n    stmt = select(Hero)\n    return await paginate(stmt)\n\n\n@app.get(\"/heros/{hero_id}\", response_model=Item[HeroModel])\nasync def get_hero(hero_id: int, session: Session):\n    hero = await session.get(Hero, hero_id)\n    if hero is None:\n        raise HTTPException(HTTPStatus.NOT_FOUND, \"Hero not found\")\n    return {\"data\": hero}\n\n\n@app.post(\"/heros\", response_model=Item[HeroModel])\nasync def create_hero(new_hero: HeroBase, session: Session):\n    hero = Hero(**new_hero.model_dump())\n    session.add(hero)\n    try:\n        await session.flush()\n    except IntegrityError:\n        raise HTTPException(HTTPStatus.CONFLICT, \"Duplicate hero name\")\n    return {\"data\": hero}\n```\n\n### Database\n\n💡 This example uses an `SQLite` database for simplicity: `FastSQLA` is compatible with\nall asynchronous db drivers that `SQLAlchemy` is compatible with.\n\nLet's create an `SQLite` database using `sqlite3` and insert 12 rows in the `hero` table:\n\n```bash\nsqlite3 db.sqlite \u003c\u003cEOF\n-- Create Table hero\nCREATE TABLE hero (\n    id              INTEGER PRIMARY KEY AUTOINCREMENT,\n    name            TEXT NOT NULL UNIQUE, -- Unique hero name (e.g., Superman)\n    secret_identity TEXT NOT NULL,        -- Secret identity (e.g., Clark Kent)\n    age             INTEGER NOT NULL      -- Age of the hero (e.g., 30)\n);\n\n-- Insert heroes with their name, secret identity, and age\nINSERT INTO hero (name, secret_identity, age) VALUES ('Superman',        'Clark Kent',       30);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Batman',          'Bruce Wayne',      35);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Wonder Woman',    'Diana Prince',     30);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Iron Man',        'Tony Stark',       45);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Spider-Man',      'Peter Parker',     25);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Captain America', 'Steve Rogers',     100);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Black Widow',     'Natasha Romanoff', 35);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Thor',            'Thor Odinson',     1500);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Scarlet Witch',   'Wanda Maximoff',   30);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Doctor Strange',  'Stephen Strange',  40);\nINSERT INTO hero (name, secret_identity, age) VALUES ('The Flash',       'Barry Allen',      28);\nINSERT INTO hero (name, secret_identity, age) VALUES ('Green Lantern',   'Hal Jordan',       35);\nEOF\n```\n\n### Run the app\n\nLet's install required dependencies:\n```bash\npip install uvicorn aiosqlite fastsqla\n```\nLet's run the app:\n```\nsqlalchemy_url=sqlite+aiosqlite:///db.sqlite?check_same_thread=false \\\n  uvicorn example:app\n```\n\n### Check the result\n\nExecute `GET /heros?offset=10\u0026limit=10` using `curl`:\n```bash\ncurl -X 'GET' -H 'accept: application/json' 'http://127.0.0.1:8000/heros?offset=10\u0026limit=10'\n```\nReturns:\n```json\n{\n  \"data\": [\n    {\n      \"name\": \"The Flash\",\n      \"secret_identity\": \"Barry Allen\",\n      \"id\": 11\n    },\n    {\n      \"name\": \"Green Lantern\",\n      \"secret_identity\": \"Hal Jordan\",\n      \"id\": 12\n    }\n  ],\n  \"meta\": {\n    \"offset\": 10,\n    \"total_items\": 12,\n    \"total_pages\": 2,\n    \"page_number\": 2\n  }\n}\n```\n\nYou can also check the generated openapi doc by opening your browser to\n[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).\n\n![OpenAPI generated documentation of the example API](https://raw.githubusercontent.com/hadrien/FastSQLA/refs/heads/main/docs/images/example-openapi-generated-doc.png)\n\n## License\n\nThis project is licensed under the terms of the [MIT license](https://github.com/hadrien/FastSQLA/blob/main/LICENSE).\n","funding_links":[],"categories":["Third-Party Extensions"],"sub_categories":["Databases"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhadrien%2FFastSQLA","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhadrien%2FFastSQLA","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhadrien%2FFastSQLA/lists"}