Metadata¶
Code¶
.
├── api
│ ├── __init__.py
│ ├── items.py
│ └── users.py
├── config.py
├── db.py
├── dependencies.py
├── __init__.py
├── main.py
└── models.py
app/__init__.py
app/models.py
from typing import List, Optional
from sqlmodel import Field, Relationship, SQLModel
class ItemBase(SQLModel):
title: str
description: Optional[str] = None
class Item(ItemBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="user.id")
user: Optional["User"] = Relationship(back_populates="items")
class UserBase(SQLModel):
username: str
class User(UserBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
is_active: bool = True
items: List[Item] = Relationship(back_populates="user")
class UserWithItems(UserBase):
id: int
is_active: bool
items: List[Item]
app/db.py
from sqlmodel import create_engine
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
app/dependencies.py
import secrets
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlmodel import Session
from .config import secret_token
from .db import engine
bearer_scheme = HTTPBearer()
def authenticate_token(
credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
) -> str:
if not secrets.compare_digest(credentials.credentials, secret_token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials"
)
return credentials.credentials
def get_session():
with Session(engine) as session:
yield session
app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlmodel import SQLModel
from . import db, models
from .api import items, users
def create_db_and_tables():
assert models, "Models should be imported so SQLModel has them registered"
SQLModel.metadata.create_all(db.engine)
app = FastAPI(
title="My Super App",
version="0.3.0",
)
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
"https://fastapiworkshop.yaquelinehoyos.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(users.router, tags=["users"])
app.include_router(items.router, tags=["items"])
@app.on_event("startup")
def startup_event():
create_db_and_tables()
app/config.py
# Don't do this in production, read this token from an environment variable
secret_token = "supersecret"
app/api/__init__.py
app/api/users.py
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from ..dependencies import authenticate_token, get_session
from ..models import User, UserBase, UserWithItems
router = APIRouter()
@router.post(
"/users/",
response_model=User,
dependencies=[Depends(authenticate_token)],
)
def create_user(user: UserBase, session: Session = Depends(get_session)):
"""
Create a new user.
"""
db_user = session.exec(select(User).where(User.username == user.username)).first()
if db_user:
raise HTTPException(status_code=400, detail="Username already registered")
db_user = User.from_orm(user)
session.add(db_user)
session.commit()
session.refresh(db_user)
return db_user
@router.get("/users/", response_model=List[User])
def read_users(
offset: int = 0, limit: int = 100, session: Session = Depends(get_session)
):
"""
Read users.
Doesn't require authentication.
"""
users = session.exec(select(User).offset(offset).limit(limit)).all()
return users
@router.get("/users/{user_id}", response_model=UserWithItems)
def read_user(user_id: int, session: Session = Depends(get_session)):
"""
Read the data for a specific user.
Doesn't require authentication.
"""
db_user = session.exec(select(User).where(User.id == user_id)).first()
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
app/api/items.py
from typing import List
from fastapi import APIRouter, Depends
from sqlmodel import Session, select
from ..dependencies import authenticate_token, get_session
from ..models import Item, ItemBase
router = APIRouter()
@router.post(
"/users/{user_id}/items/",
response_model=Item,
dependencies=[Depends(authenticate_token)],
)
def create_item_for_user(
user_id: int, item: ItemBase, session: Session = Depends(get_session)
):
"""
Create an item for a specific user.
"""
db_item = Item.from_orm(item, update={"user_id": user_id})
session.add(db_item)
session.commit()
session.refresh(db_item)
return db_item
@router.get("/items/", response_model=List[Item])
def read_items(
offset: int = 0, limit: int = 100, session: Session = Depends(get_session)
):
"""
Read all the items. Doesn't need authentication.
"""
items = session.exec(select(Item).offset(offset).limit(limit)).all()
return items
Server¶
Run the live server:
$ uvicorn app.main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
API docs¶
Now you can open the API docs UI at: http://127.0.0.1:8000/docs.
Admin Dashboard¶
You can go to the admin dashboard (created for this workshop) to interact with your API: https://fastapiworkshop.yaquelinehoyos.com.