main 4b4b039e5d5b cached
28 files
24.7 KB
7.1k tokens
55 symbols
1 requests
Download .txt
Repository: anthonycepeda/fastapi-sqlmodel
Branch: main
Commit: 4b4b039e5d5b
Files: 28
Total size: 24.7 KB

Directory structure:
gitextract_iiii9og7/

├── .github/
│   └── workflows/
│       └── python-app.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Pipfile
├── README.md
├── api/
│   ├── app.py
│   ├── auth/
│   │   └── __init__.py
│   ├── config.py
│   ├── database.py
│   ├── public/
│   │   ├── __init__.py
│   │   ├── health/
│   │   │   ├── crud.py
│   │   │   ├── models.py
│   │   │   └── views.py
│   │   ├── hero/
│   │   │   ├── crud.py
│   │   │   ├── models.py
│   │   │   └── views.py
│   │   └── team/
│   │       ├── crud.py
│   │       ├── models.py
│   │       └── views.py
│   └── utils/
│       ├── generic_models.py
│       ├── logger.py
│       └── mock_data_generator.py
├── asgi.py
├── entrypoint.sh
└── tavern_tests/
    ├── common.yaml
    ├── test_hero.tavern.yaml
    └── test_team.tavern.yaml

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/python-app.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python application

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up Python 3.11
      uses: actions/setup-python@v3
      with:
        python-version: "3.11"
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install black pytest
        if [ -f Pipfile ]; then pip install pipenv; pipenv install; fi
    - name: Lint with black
      run: |
        black ./


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.pylintrc
.vscode
database.db
# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/


================================================
FILE: Dockerfile
================================================
FROM python:3.11.7



WORKDIR /app

COPY tavern_tests /app/tavern_tests
COPY Pipfile /app
COPY Pipfile.lock /app
COPY .env /app/.env
RUN pip install --upgrade pip
RUN pip install pipenv
RUN pipenv install --system --deploy --ignore-pipfile --${PIPENV_ARGS}

RUN cat /etc/ssl/certs/ca-certificates.crt >> `python -m certifi`

COPY api/ /app/api
COPY entry_point.py /app/entry_point.py

EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
CMD ["uvicorn", "asgi:api", "--host", "0.0.0.0", "--port", "8080"]

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Anthony Cepeda

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Pipfile
================================================
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
fastapi = "*"
sqlmodel = "*"
uvicorn = "*"
pydantic-settings = "*"

[dev-packages]
black = "*"
pylint = "*"

[requires]
python_version = "3.11.7"

[pipenv]
allow_prereleases = true


================================================
FILE: README.md
================================================
![alt text](./img/SQLModel.png)
## FastAPI + SQLModel Boilerplate App
A RestAPI real world app based on SQLModel [documentation example](https://sqlmodel.tiangolo.com/tutorial/), using [FastAPI](https://fastapi.tiangolo.com/) and [SQLModel](https://sqlmodel.tiangolo.com/)


### Quickstart
1.  <b>Start the App</b>:
  - Using Python:
    `pipenv run python asgi.py`

  - Using Docker:
    `docker build -t sqlmodel-api:latest . && docker run -p 8080:8080 sqlmodel-api:latest`

2. <b>Use Openapi at</b>: `http://localhost:8080/#/`


### Running Tests:
While your app is running, open another terminal:
`pytest -v tavern_tests/`


![alt text](./img/SQLModelAPI_openapi.png)

================================================
FILE: api/app.py
================================================
from contextlib import asynccontextmanager

from fastapi import FastAPI

from api.config import Settings
from api.database import create_db_and_tables
from api.public import api as public_api
from api.utils.logger import logger_config
from api.utils.mock_data_generator import create_heroes_and_teams

logger = logger_config(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    create_heroes_and_teams()

    logger.info("startup: triggered")

    yield

    logger.info("shutdown: triggered")


def create_app(settings: Settings):
    app = FastAPI(
        title=settings.PROJECT_NAME,
        version=settings.VERSION,
        docs_url="/",
        description=settings.DESCRIPTION,
        lifespan=lifespan,
    )

    app.include_router(public_api)

    return app


================================================
FILE: api/auth/__init__.py
================================================
import secrets

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials

from api.config import settings

basic_auth = HTTPBasic(auto_error=False)


def authent(
    credentials: HTTPBasicCredentials = Depends(basic_auth),
):
    if check_basic_auth_creds(credentials):
        return True

    raise HTTPException(status_code=403, detail="invalid user/password provided")


def check_basic_auth_creds(
    credentials: HTTPBasicCredentials = Depends(basic_auth),
):
    correct_username = secrets.compare_digest(
        credentials.username, settings.API_USERNAME
    )
    correct_password = secrets.compare_digest(
        credentials.password, settings.API_PASSWORD
    )

    if correct_username and correct_password:
        return True

    return False


================================================
FILE: api/config.py
================================================
import os
import secrets
from typing import Literal

from pydantic_settings import BaseSettings


class Settings(BaseSettings):
    PROJECT_NAME: str = f"SQLModel API - {os.getenv('ENV', 'development').capitalize()}"
    DESCRIPTION: str = "A FastAPI + SQLModel production-ready API"
    ENV: Literal["development", "staging", "production"] = "development"
    VERSION: str = "0.1"
    SECRET_KEY: str = secrets.token_urlsafe(32)
    DATABASE_URI: str = "sqlite:////Users/anth/dev/fastapi-sqlmodel/database.db"
    API_USERNAME: str = "svc_test"
    API_PASSWORD: str = "superstrongpassword"

    class Config:
        case_sensitive = True


settings = Settings()


class TestSettings(Settings):
    class Config:
        case_sensitive = True


test_settings = TestSettings()


================================================
FILE: api/database.py
================================================
from sqlmodel import Session, SQLModel, create_engine

from api.config import settings

connect_args = {"check_same_thread": False}
engine = create_engine(settings.DATABASE_URI, echo=True, connect_args=connect_args)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def get_session():
    with Session(engine) as session:
        yield session


================================================
FILE: api/public/__init__.py
================================================
from fastapi import APIRouter, Depends

from api.auth import authent
from api.public.health import views as health
from api.public.hero import views as heroes
from api.public.team import views as teams

api = APIRouter()


api.include_router(
    health.router,
    prefix="/health",
    tags=["Health"],
    dependencies=[Depends(authent)],
)
api.include_router(
    heroes.router,
    prefix="/heroes",
    tags=["Heroes"],
    dependencies=[Depends(authent)],
)
api.include_router(
    teams.router,
    prefix="/teams",
    tags=["Teams"],
    dependencies=[Depends(authent)],
)


================================================
FILE: api/public/health/crud.py
================================================
from fastapi import Depends
from sqlmodel import Session, text

from api.config import settings
from api.database import get_session
from api.public.health.models import Health, Stats, Status
from api.public.hero.crud import read_heroes
from api.utils.logger import logger_config

logger = logger_config(__name__)


def get_health(db: Session) -> Health:
    db_status = health_db(db=db)
    logger.info("%s.get_health.db_status: %s", __name__, db_status)
    return Health(app_status=Status.OK, db_status=db_status, environment=settings.ENV)


def get_stats(db: Session) -> Stats:
    stats = Stats(heroes=count_from_db("hero", db), teams=count_from_db("team", db))
    logger.info("%sget_stats: %s", __name__, stats)
    return stats


def count_from_db(table: str, db: Session = Depends(get_session)):
    teams = db.exec(text(f"SELECT COUNT(id) FROM {table};")).one_or_none()
    return teams[0] if teams else 0


def health_db(db: Session = Depends(get_session)) -> Status:
    try:
        db.exec(text(f"SELECT COUNT(id) FROM hero;")).one_or_none()
        return Status.OK
    except Exception as e:
        logger.exception(e)

    return Status.KO


================================================
FILE: api/public/health/models.py
================================================
from enum import Enum
from typing import Literal

from pydantic import BaseModel


class Status(str, Enum):
    OK = "OK"
    KO = "KO"


class Health(BaseModel):
    app_status: Status | None
    db_status: Status | None
    environment: Literal["development", "staging", "production"] | None


class Stats(BaseModel):
    heroes: int | None
    teams: int | None


================================================
FILE: api/public/health/views.py
================================================
from fastapi import APIRouter, Depends, status
from sqlmodel import Session

from api.database import Session, get_session
from api.public.health.crud import get_health, get_stats
from api.public.health.models import Health, Stats
from api.utils.logger import logger_config

router = APIRouter()
logger = logger_config(__name__)


@router.get(
    "",
    response_model=Health,
    status_code=status.HTTP_200_OK,
    responses={200: {"model": Health}},
)
def health(db: Session = Depends(get_session)):
    return get_health(db=db)


@router.get(
    "/stats",
    response_model=Stats,
    status_code=status.HTTP_200_OK,
    responses={200: {"model": Stats}},
)
def health_stats(db: Session = Depends(get_session)):
    return get_stats(db=db)


================================================
FILE: api/public/hero/crud.py
================================================
from fastapi import Depends, HTTPException, status
from sqlmodel import Session, select

from api.database import get_session
from api.public.hero.models import Hero, HeroCreate, HeroUpdate


def create_hero(hero: HeroCreate, db: Session = Depends(get_session)):
    hero_to_db = Hero.model_validate(hero)
    db.add(hero_to_db)
    db.commit()
    db.refresh(hero_to_db)
    return hero_to_db


def read_heroes(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):
    heroes = db.exec(select(Hero).offset(offset).limit(limit)).all()
    return heroes


def read_hero(hero_id: int, db: Session = Depends(get_session)):
    hero = db.get(Hero, hero_id)
    if not hero:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hero not found with id: {hero_id}",
        )
    return hero


def update_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):
    hero_to_update = db.get(Hero, hero_id)
    if not hero_to_update:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hero not found with id: {hero_id}",
        )

    team_data = hero.model_dump(exclude_unset=True)
    for key, value in team_data.items():
        setattr(hero_to_update, key, value)

    db.add(hero_to_update)
    db.commit()
    db.refresh(hero_to_update)
    return hero_to_update


def delete_hero(hero_id: int, db: Session = Depends(get_session)):
    hero = db.get(Hero, hero_id)
    if not hero:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hero not found with id: {hero_id}",
        )

    db.delete(hero)
    db.commit()
    return {"ok": True}


================================================
FILE: api/public/hero/models.py
================================================
from sqlmodel import Field, Relationship, SQLModel

from api.public.team.models import Team
from api.utils.generic_models import HeroTeamLink


class HeroBase(SQLModel):
    name: str
    secret_name: str
    age: int | None = None

    class Config:
        json_schema_extra = {
            "example": {
                "id": 1,
                "name": "Super Man",
                "secret_name": "Clark Kent",
                "age": 27,
                "team_id": 1,
            }
        }


class Hero(HeroBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink)


class HeroCreate(HeroBase):
    pass


class HeroRead(HeroBase):
    id: int
    name: str | None = None
    secret_name: str | None = None
    age: int | None = None
    teams: list[Team] = None


class HeroUpdate(HeroBase):
    name: str | None = None
    secret_name: str | None = None
    age: int | None = None
    teams: list[Team] = None

    class Config:
        json_schema_extra = {
            "example": {
                "name": "Super Man",
                "secret_name": "Clark Kent",
                "age": 27,
                "team_id": 1,
            }
        }


================================================
FILE: api/public/hero/views.py
================================================
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session

from api.database import get_session
from api.public.hero.crud import (
    create_hero,
    delete_hero,
    read_hero,
    read_heroes,
    update_hero,
)
from api.public.hero.models import HeroCreate, HeroRead, HeroUpdate

router = APIRouter()


@router.post("", response_model=HeroRead)
def create_a_hero(hero: HeroCreate, db: Session = Depends(get_session)):
    return create_hero(hero=hero, db=db)


@router.get("", response_model=list[HeroRead])
def get_heroes(
    offset: int = 0,
    limit: int = Query(default=100, lte=100),
    db: Session = Depends(get_session),
):
    return read_heroes(offset=offset, limit=limit, db=db)


@router.get("/{hero_id}", response_model=HeroRead)
def get_a_hero(hero_id: int, db: Session = Depends(get_session)):
    return read_hero(hero_id=hero_id, db=db)


@router.patch("/{hero_id}", response_model=HeroRead)
def update_a_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):
    return update_hero(hero_id=hero_id, hero=hero, db=db)


@router.delete("/{hero_id}")
def delete_a_hero(hero_id: int, db: Session = Depends(get_session)):
    return delete_hero(hero_id=hero_id, db=db)


================================================
FILE: api/public/team/crud.py
================================================
from fastapi import Depends, HTTPException, status
from sqlmodel import Session, select, text

from api.database import get_session
from api.public.team.models import Team, TeamCreate, TeamUpdate


def create_team(team: TeamCreate, db: Session = Depends(get_session)):
    team_to_db = Team.model_validate(team)
    db.add(team_to_db)
    db.commit()
    db.refresh(team_to_db)
    return team_to_db


def read_teams(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):
    teams = db.exec(select(Team).offset(offset).limit(limit)).all()
    return teams


def read_team(team_id: int, db: Session = Depends(get_session)):
    team = db.get(Team, team_id)
    if not team:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Team not found with id: {team_id}",
        )
    return team


def update_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):
    team_to_update = db.get(Team, team_id)
    if not team_to_update:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Team not found with id: {team_id}",
        )

    team_data = team.model_dump(exclude_unset=True)
    for key, value in team_data.items():
        setattr(team_to_update, key, value)

    db.add(team_to_update)
    db.commit()
    db.refresh(team_to_update)
    return team_to_update


def delete_team(team_id: int, db: Session = Depends(get_session)):
    team = db.get(Team, team_id)
    if not team:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Team not found with id: {team_id}",
        )

    db.delete(team)
    db.commit()
    return {"ok": True}


================================================
FILE: api/public/team/models.py
================================================
from sqlmodel import Field, Relationship, SQLModel

from api.utils.generic_models import HeroTeamLink


class TeamBase(SQLModel):
    name: str
    headquarters: str

    class Config:
        json_schema_extra = {
            "example": {
                "name": "wonderful league",
                "headquarters": "Fortress of Solitude",
            }
        }


class Team(TeamBase, table=True):
    id: int | None = Field(default=None, primary_key=True)

    heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink)  # type: ignore


class TeamCreate(TeamBase):
    pass


class TeamRead(TeamBase):
    id: int
    name: str | None = None
    headquarters: str | None = None
    heroes: list | None = None


class TeamUpdate(TeamBase):
    name: str | None = None
    headquarters: str | None = None


================================================
FILE: api/public/team/views.py
================================================
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session

from api.database import get_session
from api.public.team.crud import (
    create_team,
    delete_team,
    read_team,
    read_teams,
    update_team,
)
from api.public.team.models import TeamCreate, TeamRead, TeamUpdate
from api.utils.logger import logger_config

router = APIRouter()

logger = logger_config(__name__)


@router.post("", response_model=TeamRead)
def create_a_team(team: TeamCreate, db: Session = Depends(get_session)):
    logger.info("%s.create_a_team: %s", __name__, team)
    return create_team(team=team, db=db)


@router.get("", response_model=list[TeamRead])
def get_teams(
    offset: int = 0,
    limit: int = Query(default=100, lte=100),
    db: Session = Depends(get_session),
):
    logger.info("%s.get_teams: triggered", __name__)
    return read_teams(offset=offset, limit=limit, db=db)


@router.get("/{team_id}", response_model=TeamRead)
def get_a_team(team_id: int, db: Session = Depends(get_session)):
    logger.info("%s.get_a_team.id: %s", __name__, team_id)
    return read_team(team_id=team_id, db=db)


@router.patch("/{team_id}", response_model=TeamRead)
def update_a_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):
    logger.info("%s.update_a_team.id: %s", __name__, team_id)
    return update_team(team_id=team_id, team=team, db=db)


@router.delete("/{team_id}")
def delete_a_team(team_id: int, db: Session = Depends(get_session)):
    logger.info("%s.delete_a_team: %s triggered", __name__, team_id)
    return delete_team(team_id=team_id, db=db)


================================================
FILE: api/utils/generic_models.py
================================================
from enum import Enum

from sqlmodel import Field, SQLModel


class HeroTeamLink(SQLModel, table=True):
    team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True)
    hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True)


================================================
FILE: api/utils/logger.py
================================================
import logging


def logger_config(module):
    """
    Logger function. Extends Python loggin module and set a custom config.
    params: Module Name. e.i: logger_config(__name__).
    return: Custom logger_config Object.
    """
    formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
    handler = logging.StreamHandler()
    handler.setFormatter(formatter)

    custom_logger = logging.getLogger(module)
    custom_logger.setLevel(logging.DEBUG)

    custom_logger.addHandler(handler)

    return custom_logger


================================================
FILE: api/utils/mock_data_generator.py
================================================
from sqlmodel import Session

from api.database import engine
from api.public.hero.models import Hero
from api.public.team.models import Team
from api.utils.logger import logger_config

logger = logger_config(__name__)


def create_heroes_and_teams():
    with Session(engine) as session:
        team_preventers = Team(name="Preventers", headquarters="Sharp Tower")
        team_z_force = Team(name="Z-Force", headquarters="Sister Margaret's Bar")
        wornderful_league = Team(
            name="Wonderful-League", headquarters="Fortress of Solitude"
        )

        hero_deadpond = Hero(
            name="Deadpond",
            secret_name="Dive Wilson",
            age=24,
            teams=[team_z_force, team_preventers],
        )
        hero_rusty_man = Hero(
            name="Rusty-Man",
            secret_name="Tommy Sharp",
            age=48,
            teams=[team_preventers],
        )
        hero_spider_boy = Hero(
            name="Spider-Boy",
            secret_name="Pedro Parqueador",
            age=37,
            teams=[team_preventers],
        )
        hero_super_good_boy = Hero(
            name="Super-Good-Boy",
            secret_name="John Goodman",
            age=30,
            teams=[wornderful_league, team_z_force],
        )

        session.add(hero_deadpond)
        session.add(hero_rusty_man)
        session.add(hero_spider_boy)
        session.add(hero_super_good_boy)
        session.commit()

        session.refresh(hero_deadpond)
        session.refresh(hero_rusty_man)
        session.refresh(hero_spider_boy)
        session.refresh(hero_super_good_boy)

        logger.info("=========== MOCK DATA CREATED ===========")
        logger.info("Deadpond %s", hero_deadpond)
        logger.info("Deadpond teams %s", hero_deadpond.teams)
        logger.info("Rusty-Man %s", hero_rusty_man)
        logger.info("Rusty-Man Teams %s", hero_rusty_man.teams)
        logger.info("Spider-Boy %s", hero_spider_boy)
        logger.info("Spider-Boy Teams %s", hero_spider_boy.teams)
        logger.info("Super-Good-Boy %s", hero_super_good_boy)
        logger.info("Super-Good-Boy Teams: %s", hero_super_good_boy.teams)
        logger.info("===========================================")


================================================
FILE: asgi.py
================================================
import uvicorn

from api.app import create_app
from api.config import settings

api = create_app(settings)

if __name__ == "__main__":
    uvicorn.run("asgi:api", host="0.0.0.0", port=8080, reload=True)


================================================
FILE: entrypoint.sh
================================================
#!/bin/bash

exec "$@"

================================================
FILE: tavern_tests/common.yaml
================================================
---
name: "FastAPI - SQLModel API"
description: Common Settings

variables:
  host: http://localhost:8080
  hero_id: 1
  hero_name: "Super Man"
  hero_secret_name: "Clark Kent"
  hero_age: 27
  team_id: 1
  team_name: "wonderful league"
  team_headquarters: "Fortress of Solitude"

================================================
FILE: tavern_tests/test_hero.tavern.yaml
================================================
---
test_name: Hero Tests
includes:
  - !include common.yaml

stages:
  - name: Create a Hero
    request:
      method: POST
      url: "{host}/heroes"
      json:
        id: 1
        name: "Super Man"
        secret_name: "Clark Kent"
        age: 27
        team_id: 1
    response:
      status_code: 200
      json: {}
  
  - name: Get a Hero
    request:
      method: GET 
      url: "{host}/heroes/1"
    response:
      status_code: 200
      json: []
  
  - name: Get a Hero - Not Found
    request:
      method: GET 
      url: "{host}/heroes/99"
    response:
      status_code: 404
      json: 
        detail: "Hero not found with id: 99"
  
  - name: Get a Heroes
    request:
      method: GET 
      url: "{host}/heroes"
      params:
        offset: 0
        limit: 100
    response:
      status_code: 200
      json: []
  
  - name: Update a Heroe
    request:
      method: PATCH 
      url: "{host}/heroes/1"
      json:
        name: "Super Man"
        secret_name: "Clark Kent"
        age: 28
        team_id: 1
    response:
      status_code: 200
      json:
        id: 1
        name: "Super Man"
        secret_name: "Clark Kent"
        age: 28
        team_id: 1

  - name: Delete a Heroe
    request:
      method: DELETE 
      url: "{host}/heroes/1"
    response:
      json:
        ok: true

================================================
FILE: tavern_tests/test_team.tavern.yaml
================================================
---
test_name: Team Tests
includes:
  - !include common.yaml

stages:
  - name: Create a Team
    request:
      method: POST
      url: "{host}/teams"
      json:
        name: "wonderful league"
        headquarters: "Fortress of Solitude"
    response:
      status_code: 200
      json: {}
  
  - name: Get a Team
    request:
      method: GET 
      url: "{host}/teams/1"
    response:
      status_code: 200
      json: []
  
  - name: Get a Team - Not Found
    request:
      method: GET 
      url: "{host}/teams/99"
    response:
      status_code: 404
      json: 
        detail: "Team not found with id: 99"
  
  - name: Get Teams
    request:
      method: GET 
      url: "{host}/teams"
      params:
        offset: 0
        limit: 100
    response:
      status_code: 200
      json: []
  
  - name: Update a Teame
    request:
      method: PATCH 
      url: "{host}/teams/1"
      json:
        name: "wonderful league"
        headquarters: "frozen tundra"
    response:
      status_code: 200
      json:
        id: 1
        name: "wonderful league"
        headquarters: "frozen tundra"

  - name: Delete a Teame
    request:
      method: DELETE 
      url: "{host}/teams/1"
    response:
      json:
        ok: true
Download .txt
gitextract_iiii9og7/

├── .github/
│   └── workflows/
│       └── python-app.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Pipfile
├── README.md
├── api/
│   ├── app.py
│   ├── auth/
│   │   └── __init__.py
│   ├── config.py
│   ├── database.py
│   ├── public/
│   │   ├── __init__.py
│   │   ├── health/
│   │   │   ├── crud.py
│   │   │   ├── models.py
│   │   │   └── views.py
│   │   ├── hero/
│   │   │   ├── crud.py
│   │   │   ├── models.py
│   │   │   └── views.py
│   │   └── team/
│   │       ├── crud.py
│   │       ├── models.py
│   │       └── views.py
│   └── utils/
│       ├── generic_models.py
│       ├── logger.py
│       └── mock_data_generator.py
├── asgi.py
├── entrypoint.sh
└── tavern_tests/
    ├── common.yaml
    ├── test_hero.tavern.yaml
    └── test_team.tavern.yaml
Download .txt
SYMBOL INDEX (55 symbols across 16 files)

FILE: api/app.py
  function lifespan (line 15) | async def lifespan(app: FastAPI):
  function create_app (line 26) | def create_app(settings: Settings):

FILE: api/auth/__init__.py
  function authent (line 11) | def authent(
  function check_basic_auth_creds (line 20) | def check_basic_auth_creds(

FILE: api/config.py
  class Settings (line 8) | class Settings(BaseSettings):
    class Config (line 18) | class Config:
  class TestSettings (line 25) | class TestSettings(Settings):
    class Config (line 26) | class Config:

FILE: api/database.py
  function create_db_and_tables (line 9) | def create_db_and_tables():
  function get_session (line 13) | def get_session():

FILE: api/public/health/crud.py
  function get_health (line 13) | def get_health(db: Session) -> Health:
  function get_stats (line 19) | def get_stats(db: Session) -> Stats:
  function count_from_db (line 25) | def count_from_db(table: str, db: Session = Depends(get_session)):
  function health_db (line 30) | def health_db(db: Session = Depends(get_session)) -> Status:

FILE: api/public/health/models.py
  class Status (line 7) | class Status(str, Enum):
  class Health (line 12) | class Health(BaseModel):
  class Stats (line 18) | class Stats(BaseModel):

FILE: api/public/health/views.py
  function health (line 19) | def health(db: Session = Depends(get_session)):
  function health_stats (line 29) | def health_stats(db: Session = Depends(get_session)):

FILE: api/public/hero/crud.py
  function create_hero (line 8) | def create_hero(hero: HeroCreate, db: Session = Depends(get_session)):
  function read_heroes (line 16) | def read_heroes(offset: int = 0, limit: int = 20, db: Session = Depends(...
  function read_hero (line 21) | def read_hero(hero_id: int, db: Session = Depends(get_session)):
  function update_hero (line 31) | def update_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(ge...
  function delete_hero (line 49) | def delete_hero(hero_id: int, db: Session = Depends(get_session)):

FILE: api/public/hero/models.py
  class HeroBase (line 7) | class HeroBase(SQLModel):
    class Config (line 12) | class Config:
  class Hero (line 24) | class Hero(HeroBase, table=True):
  class HeroCreate (line 29) | class HeroCreate(HeroBase):
  class HeroRead (line 33) | class HeroRead(HeroBase):
  class HeroUpdate (line 41) | class HeroUpdate(HeroBase):
    class Config (line 47) | class Config:

FILE: api/public/hero/views.py
  function create_a_hero (line 18) | def create_a_hero(hero: HeroCreate, db: Session = Depends(get_session)):
  function get_heroes (line 23) | def get_heroes(
  function get_a_hero (line 32) | def get_a_hero(hero_id: int, db: Session = Depends(get_session)):
  function update_a_hero (line 37) | def update_a_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(...
  function delete_a_hero (line 42) | def delete_a_hero(hero_id: int, db: Session = Depends(get_session)):

FILE: api/public/team/crud.py
  function create_team (line 8) | def create_team(team: TeamCreate, db: Session = Depends(get_session)):
  function read_teams (line 16) | def read_teams(offset: int = 0, limit: int = 20, db: Session = Depends(g...
  function read_team (line 21) | def read_team(team_id: int, db: Session = Depends(get_session)):
  function update_team (line 31) | def update_team(team_id: int, team: TeamUpdate, db: Session = Depends(ge...
  function delete_team (line 49) | def delete_team(team_id: int, db: Session = Depends(get_session)):

FILE: api/public/team/models.py
  class TeamBase (line 6) | class TeamBase(SQLModel):
    class Config (line 10) | class Config:
  class Team (line 19) | class Team(TeamBase, table=True):
  class TeamCreate (line 25) | class TeamCreate(TeamBase):
  class TeamRead (line 29) | class TeamRead(TeamBase):
  class TeamUpdate (line 36) | class TeamUpdate(TeamBase):

FILE: api/public/team/views.py
  function create_a_team (line 21) | def create_a_team(team: TeamCreate, db: Session = Depends(get_session)):
  function get_teams (line 27) | def get_teams(
  function get_a_team (line 37) | def get_a_team(team_id: int, db: Session = Depends(get_session)):
  function update_a_team (line 43) | def update_a_team(team_id: int, team: TeamUpdate, db: Session = Depends(...
  function delete_a_team (line 49) | def delete_a_team(team_id: int, db: Session = Depends(get_session)):

FILE: api/utils/generic_models.py
  class HeroTeamLink (line 6) | class HeroTeamLink(SQLModel, table=True):

FILE: api/utils/logger.py
  function logger_config (line 4) | def logger_config(module):

FILE: api/utils/mock_data_generator.py
  function create_heroes_and_teams (line 11) | def create_heroes_and_teams():
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (28K chars).
[
  {
    "path": ".github/workflows/python-app.yml",
    "chars": 806,
    "preview": "# This workflow will install Python dependencies, run tests and lint with a single version of Python\n# For more informat"
  },
  {
    "path": ".gitignore",
    "chars": 1828,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n.pylintrc\n.vscode\ndatabase.db\n# C extensions\n*"
  },
  {
    "path": "Dockerfile",
    "chars": 493,
    "preview": "FROM python:3.11.7\n\n\n\nWORKDIR /app\n\nCOPY tavern_tests /app/tavern_tests\nCOPY Pipfile /app\nCOPY Pipfile.lock /app\nCOPY .e"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2021 Anthony Cepeda\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "Pipfile",
    "chars": 268,
    "preview": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\nfastapi = \"*\"\nsqlmodel = \"*\"\nuvic"
  },
  {
    "path": "README.md",
    "chars": 671,
    "preview": "![alt text](./img/SQLModel.png)\n## FastAPI + SQLModel Boilerplate App\nA RestAPI real world app based on SQLModel [docume"
  },
  {
    "path": "api/app.py",
    "chars": 816,
    "preview": "from contextlib import asynccontextmanager\n\nfrom fastapi import FastAPI\n\nfrom api.config import Settings\nfrom api.databa"
  },
  {
    "path": "api/auth/__init__.py",
    "chars": 818,
    "preview": "import secrets\n\nfrom fastapi import Depends, HTTPException, status\nfrom fastapi.security import HTTPBasic, HTTPBasicCred"
  },
  {
    "path": "api/config.py",
    "chars": 778,
    "preview": "import os\nimport secrets\nfrom typing import Literal\n\nfrom pydantic_settings import BaseSettings\n\n\nclass Settings(BaseSet"
  },
  {
    "path": "api/database.py",
    "chars": 367,
    "preview": "from sqlmodel import Session, SQLModel, create_engine\n\nfrom api.config import settings\n\nconnect_args = {\"check_same_thre"
  },
  {
    "path": "api/public/__init__.py",
    "chars": 583,
    "preview": "from fastapi import APIRouter, Depends\n\nfrom api.auth import authent\nfrom api.public.health import views as health\nfrom "
  },
  {
    "path": "api/public/health/crud.py",
    "chars": 1158,
    "preview": "from fastapi import Depends\nfrom sqlmodel import Session, text\n\nfrom api.config import settings\nfrom api.database import"
  },
  {
    "path": "api/public/health/models.py",
    "chars": 365,
    "preview": "from enum import Enum\nfrom typing import Literal\n\nfrom pydantic import BaseModel\n\n\nclass Status(str, Enum):\n    OK = \"OK"
  },
  {
    "path": "api/public/health/views.py",
    "chars": 748,
    "preview": "from fastapi import APIRouter, Depends, status\nfrom sqlmodel import Session\n\nfrom api.database import Session, get_sessi"
  },
  {
    "path": "api/public/hero/crud.py",
    "chars": 1720,
    "preview": "from fastapi import Depends, HTTPException, status\nfrom sqlmodel import Session, select\n\nfrom api.database import get_se"
  },
  {
    "path": "api/public/hero/models.py",
    "chars": 1260,
    "preview": "from sqlmodel import Field, Relationship, SQLModel\n\nfrom api.public.team.models import Team\nfrom api.utils.generic_model"
  },
  {
    "path": "api/public/hero/views.py",
    "chars": 1225,
    "preview": "from fastapi import APIRouter, Depends, Query\nfrom sqlmodel import Session\n\nfrom api.database import get_session\nfrom ap"
  },
  {
    "path": "api/public/team/crud.py",
    "chars": 1723,
    "preview": "from fastapi import Depends, HTTPException, status\nfrom sqlmodel import Session, select, text\n\nfrom api.database import "
  },
  {
    "path": "api/public/team/models.py",
    "chars": 833,
    "preview": "from sqlmodel import Field, Relationship, SQLModel\n\nfrom api.utils.generic_models import HeroTeamLink\n\n\nclass TeamBase(S"
  },
  {
    "path": "api/public/team/views.py",
    "chars": 1598,
    "preview": "from fastapi import APIRouter, Depends, Query\nfrom sqlmodel import Session\n\nfrom api.database import get_session\nfrom ap"
  },
  {
    "path": "api/utils/generic_models.py",
    "chars": 278,
    "preview": "from enum import Enum\n\nfrom sqlmodel import Field, SQLModel\n\n\nclass HeroTeamLink(SQLModel, table=True):\n    team_id: int"
  },
  {
    "path": "api/utils/logger.py",
    "chars": 534,
    "preview": "import logging\n\n\ndef logger_config(module):\n    \"\"\"\n    Logger function. Extends Python loggin module and set a custom c"
  },
  {
    "path": "api/utils/mock_data_generator.py",
    "chars": 2240,
    "preview": "from sqlmodel import Session\n\nfrom api.database import engine\nfrom api.public.hero.models import Hero\nfrom api.public.te"
  },
  {
    "path": "asgi.py",
    "chars": 203,
    "preview": "import uvicorn\n\nfrom api.app import create_app\nfrom api.config import settings\n\napi = create_app(settings)\n\nif __name__ "
  },
  {
    "path": "entrypoint.sh",
    "chars": 22,
    "preview": "#!/bin/bash\n\nexec \"$@\""
  },
  {
    "path": "tavern_tests/common.yaml",
    "chars": 280,
    "preview": "---\nname: \"FastAPI - SQLModel API\"\ndescription: Common Settings\n\nvariables:\n  host: http://localhost:8080\n  hero_id: 1\n "
  },
  {
    "path": "tavern_tests/test_hero.tavern.yaml",
    "chars": 1332,
    "preview": "---\ntest_name: Hero Tests\nincludes:\n  - !include common.yaml\n\nstages:\n  - name: Create a Hero\n    request:\n      method:"
  },
  {
    "path": "tavern_tests/test_team.tavern.yaml",
    "chars": 1244,
    "preview": "---\ntest_name: Team Tests\nincludes:\n  - !include common.yaml\n\nstages:\n  - name: Create a Team\n    request:\n      method:"
  }
]

About this extraction

This page contains the full source code of the anthonycepeda/fastapi-sqlmodel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (24.7 KB), approximately 7.1k tokens, and a symbol index with 55 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!