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. Start the App: - Using Python: `pipenv run python asgi.py` - Using Docker: `docker build -t sqlmodel-api:latest . && docker run -p 8080:8080 sqlmodel-api:latest` 2. Use Openapi at: `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