[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7CSJEGXCTR66QT92JSZKCVB":3,"skill-same:01M7CSJEGXCTR66QT92JSZKCVB":48},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":14,"repositoryHtmlUrl":29,"repositoryHomepageUrl":6,"ownerHtmlUrl":30,"ownerBlogUrl":31,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M7CSJEGXCTR66QT92JSZKCVB","fastapi-patterns",null,"FastAPI best practices covering project structure, Pydantic v2 schemas, dependency injection, async handlers, authentication, authorization, transactional service layers, and testing with httpx and pytest.","Henrique1601\u002FSkiilsToIAs","Henrique1601","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F92006691?v=4","01M7CRXVT0GVXVK80JT1GR687S","Henrique Bezerra Dos Santos","01M7CRXVTN5Y8TMWJ1MDSKCQ1T",0,"MIT","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Fbackend-database\u002Ffastapi-patterns\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: fastapi-patterns\ndescription: FastAPI best practices covering project structure, Pydantic v2 schemas, dependency injection, async handlers, authentication, authorization, transactional service layers, and testing with httpx and pytest.\nmetadata:\n  origin: ECC\n---\n\n# FastAPI Patterns\n\nModern, production-grade FastAPI development: project layout, Pydantic v2 schemas, dependency injection, async patterns, auth, transactional service methods, and testing.\n\n## Project Structure\n\n```text\nmy_app\u002F\n|-- app\u002F\n|   |-- main.py               # App factory, lifespan, middleware\n|   |-- config.py             # Settings via pydantic-settings\n|   |-- dependencies.py       # Shared FastAPI dependencies\n|   |-- database.py           # SQLAlchemy engine + session\n|   |-- routers\u002F\n|   |   `-- users.py\n|   |-- models\u002F               # SQLAlchemy ORM models\n|   |   `-- user.py\n|   |-- schemas\u002F              # Pydantic request\u002Fresponse schemas\n|   |   `-- user.py\n|   `-- services\u002F             # Business logic layer\n|       `-- user_service.py\n|-- tests\u002F\n|   |-- conftest.py\n|   `-- test_users.py\n|-- pyproject.toml\n`-- .env\n```\n\n---\n\n## App Factory and Lifespan\n\n```python\n# app\u002Fmain.py\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\nfrom app.config import settings\nfrom app.database import engine, Base\nfrom app.routers import users\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    # Automatically create tables on startup for ease of use in dev\u002Fdemo environments.\n    # For strict production applications, manage schemas via Alembic migrations instead.\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n    yield\n    # Shutdown: close pooled resources.\n    await engine.dispose()\n\n\ndef create_app() -> FastAPI:\n    app = FastAPI(\n        title=settings.app_name,\n        version=settings.app_version,\n        lifespan=lifespan,\n    )\n\n    app.add_middleware(\n        CORSMiddleware,\n        allow_origins=settings.allowed_origins,\n        allow_credentials=settings.allow_credentials,\n        allow_methods=settings.allowed_methods,\n        allow_headers=settings.allowed_headers,\n    )\n\n    app.include_router(users.router, prefix=\"\u002Fusers\", tags=[\"users\"])\n\n    return app\n\n\napp = create_app()\n```\n\n---\n\n## Configuration with pydantic-settings\n\n```python\n# app\u002Fconfig.py\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nclass Settings(BaseSettings):\n    model_config = SettingsConfigDict(env_file=\".env\", env_file_encoding=\"utf-8\")\n\n    app_name: str = \"My App\"\n    app_version: str = \"0.1.0\"\n    debug: bool = False\n\n    database_url: str\n    secret_key: str\n    algorithm: str = \"HS256\"\n    access_token_expire_minutes: int = 30\n\n    # Pydantic-settings v2 safely evaluates mutable list literals directly\n    allowed_origins: list[str] = [\"http:\u002F\u002Flocalhost:3000\"]\n    allowed_methods: list[str] = [\"GET\", \"POST\", \"PATCH\", \"DELETE\", \"OPTIONS\"]\n    allowed_headers: list[str] = [\"Authorization\", \"Content-Type\"]\n    allow_credentials: bool = True\n\n\nsettings = Settings()\n```\n\n---\n\n## Pydantic Schemas (v2)\n\n```python\n# app\u002Fschemas\u002Fuser.py\nfrom datetime import datetime\nfrom pydantic import BaseModel, EmailStr, Field, model_validator\n\n\nclass UserBase(BaseModel):\n    email: EmailStr\n    username: str = Field(min_length=3, max_length=50)\n\n\nclass UserCreate(UserBase):\n    password: str = Field(min_length=8)\n    password_confirm: str\n\n    @model_validator(mode=\"after\")\n    def passwords_match(self) -> \"UserCreate\":\n        if self.password != self.password_confirm:\n            raise ValueError(\"Passwords do not match\")\n        return self\n\n\nclass UserUpdate(BaseModel):\n    username: str | None = Field(default=None, min_length=3, max_length=50)\n    email: EmailStr | None = None\n\n\nclass UserResponse(UserBase):\n    id: int\n    is_active: bool\n    created_at: datetime\n\n    model_config = {\"from_attributes\": True}\n\n\nclass UserListResponse(BaseModel):\n    total: int\n    items: list[UserResponse]\n```\n\n---\n\n## Dependency Injection\n\n```python\n# app\u002Fdependencies.py\nfrom typing import Annotated, AsyncGenerator\nfrom fastapi import Depends, HTTPException, status\nfrom fastapi.security import OAuth2PasswordBearer\nfrom jose import JWTError, jwt\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.config import settings\nfrom app.database import AsyncSessionLocal\nfrom app.models.user import User\n\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"\u002Fusers\u002Ftoken\")\n\n\nasync def get_db() -> AsyncGenerator[AsyncSession, None]:\n    async with AsyncSessionLocal() as session:\n        try:\n            yield session\n        except Exception:\n            await session.rollback()\n            raise\n\n\nasync def get_current_user(\n    token: Annotated[str, Depends(oauth2_scheme)],\n    db: Annotated[AsyncSession, Depends(get_db)],\n) -> User:\n    credentials_exception = HTTPException(\n        status_code=status.HTTP_401_UNAUTHORIZED,\n        detail=\"Could not validate credentials\",\n        headers={\"WWW-Authenticate\": \"Bearer\"},\n    )\n    try:\n        payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])\n        subject = payload.get(\"sub\")\n        if subject is None:\n            raise credentials_exception\n        user_id = int(subject)\n    except (JWTError, TypeError, ValueError):\n        raise credentials_exception\n\n    user = await db.get(User, user_id)\n    if user is None:\n        raise credentials_exception\n    return user\n\n\nasync def get_current_active_user(\n    current_user: Annotated[User, Depends(get_current_user)],\n) -> User:\n    if not current_user.is_active:\n        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=\"Inactive user\")\n    return current_user\n\n\nDbDep = Annotated[AsyncSession, Depends(get_db)]\nCurrentUserDep = Annotated[User, Depends(get_current_user)]\nActiveUserDep = Annotated[User, Depends(get_current_active_user)]\n```\n\n---\n\n## Router and Endpoint Design\n\n```python\n# app\u002Frouters\u002Fusers.py\nfrom typing import Annotated\nfrom fastapi import APIRouter, HTTPException, Query, status\nfrom fastapi.security import OAuth2PasswordRequestForm\n\nfrom app.dependencies import ActiveUserDep, DbDep\nfrom app.schemas.user import UserCreate, UserResponse, UserUpdate, UserListResponse\nfrom app.services.user_service import DuplicateUserError, UserService\n\nrouter = APIRouter()\n\n\n@router.post(\"\u002F\", response_model=UserResponse, status_code=status.HTTP_201_CREATED)\nasync def create_user(payload: UserCreate, db: DbDep) -> UserResponse:\n    service = UserService(db)\n    try:\n        return await service.create(payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n\n@router.get(\"\u002Fme\", response_model=UserResponse)\nasync def get_me(current_user: ActiveUserDep) -> UserResponse:\n    return current_user\n\n\n@router.get(\"\u002F\", response_model=UserListResponse)\nasync def list_users(\n    db: DbDep,\n    current_user: ActiveUserDep,\n    skip: Annotated[int, Query(ge=0)] = 0,\n    limit: Annotated[int, Query(ge=1, le=100)] = 20,\n) -> UserListResponse:\n    service = UserService(db)\n    users, total = await service.list(skip=skip, limit=limit)\n    return UserListResponse(total=total, items=users)\n\n\n@router.patch(\"\u002F{user_id}\", response_model=UserResponse)\nasync def update_user(\n    user_id: int,\n    payload: UserUpdate,\n    db: DbDep,\n    current_user: ActiveUserDep,\n) -> UserResponse:\n    if current_user.id != user_id:\n        raise HTTPException(status_code=403, detail=\"Not authorized\")\n    service = UserService(db)\n    try:\n        user = await service.update(user_id, payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n    if user is None:\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    return user\n\n\n@router.post(\"\u002Ftoken\")\nasync def login(\n    form_data: Annotated[OAuth2PasswordRequestForm, Depends()],\n    db: DbDep,\n) -> dict[str, str]:\n    service = UserService(db)\n    token = await service.authenticate(form_data.username, form_data.password)\n    if token is None:\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Incorrect username or password\",\n            headers={\"WWW-Authenticate\": \"Bearer\"},\n        )\n    return {\"access_token\": token, \"token_type\": \"bearer\"}\n```\n\n---\n\n## Service Layer\n\n```python\n# app\u002Fservices\u002Fuser_service.py\nfrom datetime import datetime, timedelta, timezone\n\nfrom jose import jwt\nfrom passlib.context import CryptContext\nfrom sqlalchemy import func, select\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.config import settings\nfrom app.models.user import User\nfrom app.schemas.user import UserCreate, UserUpdate\n\npwd_context = CryptContext(schemes=[\"bcrypt\"], deprecated=\"auto\")\n\n\nclass DuplicateUserError(Exception):\n    \"\"\"Raised when a unique user field conflicts with an existing row.\"\"\"\n\n\nclass UserService:\n    def __init__(self, db: AsyncSession) -> None:\n        self.db = db\n\n    async def get_by_email(self, email: str) -> User | None:\n        result = await self.db.execute(select(User).where(User.email == email))\n        return result.scalar_one_or_none()\n\n    async def create(self, payload: UserCreate) -> User:\n        user = User(\n            email=payload.email,\n            username=payload.username,\n            hashed_password=pwd_context.hash(payload.password),\n        )\n        self.db.add(user)\n        try:\n            # Rely on atomic DB constraints rather than race-prone application-level prechecks\n            await self.db.commit()\n        except IntegrityError as exc:\n            await self.db.rollback()\n            raise DuplicateUserError from exc\n        await self.db.refresh(user)\n        return user\n\n    async def list(self, skip: int = 0, limit: int = 20) -> tuple[list[User], int]:\n        total_result = await self.db.execute(select(func.count(User.id)))\n        total = total_result.scalar_one()\n        # Enforce explicit deterministic ordering to ensure reliable pagination\n        result = await self.db.execute(\n            select(User).order_by(User.id).offset(skip).limit(limit)\n        )\n        return list(result.scalars()), total\n\n    async def update(self, user_id: int, payload: UserUpdate) -> User | None:\n        user = await self.db.get(User, user_id)\n        if user is None:\n            return None\n        for field, value in payload.model_dump(exclude_unset=True).items():\n            setattr(user, field, value)\n        try:\n            await self.db.commit()\n        except IntegrityError as exc:\n            await self.db.rollback()\n            raise DuplicateUserError from exc\n        await self.db.refresh(user)\n        return user\n\n    async def authenticate(self, email: str, password: str) -> str | None:\n        user = await self.get_by_email(email)\n        if user is None or not pwd_context.verify(password, user.hashed_password):\n            return None\n        expire = datetime.now(timezone.utc) + timedelta(\n            minutes=settings.access_token_expire_minutes\n        )\n        return jwt.encode(\n            {\"sub\": str(user.id), \"exp\": expire},\n            settings.secret_key,\n            algorithm=settings.algorithm,\n        )\n```\n\n> **Note on Database Design:** Application-level unique handling requires an underlying unique database index (e.g., `unique=True` on your SQLAlchemy mapping attributes). Without underlying constraints, application layer error-catching cannot safely prevent concurrent race conditions.\n\n---\n\n## Testing with httpx and pytest\n\n```python\n# tests\u002Fconftest.py\nimport pytest_asyncio\nfrom httpx import ASGITransport, AsyncClient\nfrom sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine\n\nfrom app.database import Base\nfrom app.dependencies import get_db\nfrom app.main import create_app\n\nTEST_DATABASE_URL = \"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\"\n\nengine = create_async_engine(TEST_DATABASE_URL)\nTestingSessionLocal = async_sessionmaker(engine, expire_on_commit=False)\n\n\n@pytest_asyncio.fixture(autouse=True)\nasync def setup_db():\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n    yield\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.drop_all)\n\n\n@pytest_asyncio.fixture\nasync def db_session():\n    async with TestingSessionLocal() as session:\n        yield session\n        await session.rollback()\n\n\n@pytest_asyncio.fixture\nasync def client(db_session: AsyncSession):\n    app = create_app()\n\n    async def override_get_db():\n        yield db_session\n\n    app.dependency_overrides[get_db] = override_get_db\n\n    async with AsyncClient(\n        transport=ASGITransport(app=app), base_url=\"http:\u002F\u002Ftest\"\n    ) as ac:\n        yield ac\n\n\n@pytest_asyncio.fixture\nasync def registered_user(client: AsyncClient) -> dict:\n    resp = await client.post(\"\u002Fusers\u002F\", json={\n        \"email\": \"test@example.com\",\n        \"username\": \"testuser\",\n        \"password\": \"securepass1\",\n        \"password_confirm\": \"securepass1\",\n    })\n    assert resp.status_code == 201\n    return resp.json()\n\n\n@pytest_asyncio.fixture\nasync def auth_token(client: AsyncClient, registered_user: dict) -> str:\n    resp = await client.post(\"\u002Fusers\u002Ftoken\", data={\n        \"username\": \"test@example.com\",\n        \"password\": \"securepass1\",\n    })\n    assert resp.status_code == 200\n    return resp.json()[\"access_token\"]\n\n\n@pytest_asyncio.fixture\nasync def auth_client(client: AsyncClient, auth_token: str) -> AsyncClient:\n    client.headers.update({\"Authorization\": f\"Bearer {auth_token}\"})\n    return client\n```\n\n---\n\n## Anti-Patterns\n\n```python\n# Bad: business logic inside route handlers.\n@router.post(\"\u002Fusers\u002F\")\nasync def create_user(payload: UserCreate, db: DbDep):\n    hashed = bcrypt.hash(payload.password)\n    user = User(email=payload.email, hashed_password=hashed)\n    db.add(user)\n    await db.commit()\n    return user\n\n# Good: thin route, transactional service handling.\n@router.post(\"\u002Fusers\u002F\", response_model=UserResponse, status_code=201)\nasync def create_user(payload: UserCreate, db: DbDep):\n    try:\n        return await UserService(db).create(payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n\n# Bad: sync DB calls in async routes block the event loop.\n@router.get(\"\u002Fitems\u002F\")\nasync def list_items(db: Session = Depends(get_db)):\n    return db.query(Item).all()\n\n# Good: use async SQLAlchemy executions.\n@router.get(\"\u002Fitems\u002F\")\nasync def list_items(db: AsyncSession = Depends(get_db)):\n    result = await db.execute(select(Item))\n    return result.scalars().all()\n```\n\n---\n\n## Best Practices\n\n- Always declare a typed `response_model` to prevent accidental PII\u002Fdata leaks and output clean OpenAPI schemas.\n- Consolidate standard middleware dependency injections via type-aliasing: `DbDep = Annotated[AsyncSession, Depends(get_db)]`.\n- Wrap database mutation boundaries gracefully within transactions inside your service layer, catching structural database errors directly.\n- Parse JWT parameters defensively, expecting potential string\u002Finteger cast mismatches from modern payload variations.\n- Enforce deterministic sorting (e.g., `.order_by(Model.id)`) on all offset\u002Flimit paginated endpoints to avoid data skips.\n- Isolate authorization checks from core authentication dependencies to provide precise REST status signals (`401` vs `403`).\n","# FastAPI Patterns\n\nModern, production-grade FastAPI development: project layout, Pydantic v2 schemas, dependency injection, async patterns, auth, transactional service methods, and testing.\n\n## Project Structure\n\n```text\nmy_app\u002F\n|-- app\u002F\n|   |-- main.py               # App factory, lifespan, middleware\n|   |-- config.py             # Settings via pydantic-settings\n|   |-- dependencies.py       # Shared FastAPI dependencies\n|   |-- database.py           # SQLAlchemy engine + session\n|   |-- routers\u002F\n|   |   `-- users.py\n|   |-- models\u002F               # SQLAlchemy ORM models\n|   |   `-- user.py\n|   |-- schemas\u002F              # Pydantic request\u002Fresponse schemas\n|   |   `-- user.py\n|   `-- services\u002F             # Business logic layer\n|       `-- user_service.py\n|-- tests\u002F\n|   |-- conftest.py\n|   `-- test_users.py\n|-- pyproject.toml\n`-- .env\n```\n\n---\n\n## App Factory and Lifespan\n\n```python\n# app\u002Fmain.py\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\nfrom app.config import settings\nfrom app.database import engine, Base\nfrom app.routers import users\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    # Automatically create tables on startup for ease of use in dev\u002Fdemo environments.\n    # For strict production applications, manage schemas via Alembic migrations instead.\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n    yield\n    # Shutdown: close pooled resources.\n    await engine.dispose()\n\n\ndef create_app() -> FastAPI:\n    app = FastAPI(\n        title=settings.app_name,\n        version=settings.app_version,\n        lifespan=lifespan,\n    )\n\n    app.add_middleware(\n        CORSMiddleware,\n        allow_origins=settings.allowed_origins,\n        allow_credentials=settings.allow_credentials,\n        allow_methods=settings.allowed_methods,\n        allow_headers=settings.allowed_headers,\n    )\n\n    app.include_router(users.router, prefix=\"\u002Fusers\", tags=[\"users\"])\n\n    return app\n\n\napp = create_app()\n```\n\n---\n\n## Configuration with pydantic-settings\n\n```python\n# app\u002Fconfig.py\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nclass Settings(BaseSettings):\n    model_config = SettingsConfigDict(env_file=\".env\", env_file_encoding=\"utf-8\")\n\n    app_name: str = \"My App\"\n    app_version: str = \"0.1.0\"\n    debug: bool = False\n\n    database_url: str\n    secret_key: str\n    algorithm: str = \"HS256\"\n    access_token_expire_minutes: int = 30\n\n    # Pydantic-settings v2 safely evaluates mutable list literals directly\n    allowed_origins: list[str] = [\"http:\u002F\u002Flocalhost:3000\"]\n    allowed_methods: list[str] = [\"GET\", \"POST\", \"PATCH\", \"DELETE\", \"OPTIONS\"]\n    allowed_headers: list[str] = [\"Authorization\", \"Content-Type\"]\n    allow_credentials: bool = True\n\n\nsettings = Settings()\n```\n\n---\n\n## Pydantic Schemas (v2)\n\n```python\n# app\u002Fschemas\u002Fuser.py\nfrom datetime import datetime\nfrom pydantic import BaseModel, EmailStr, Field, model_validator\n\n\nclass UserBase(BaseModel):\n    email: EmailStr\n    username: str = Field(min_length=3, max_length=50)\n\n\nclass UserCreate(UserBase):\n    password: str = Field(min_length=8)\n    password_confirm: str\n\n    @model_validator(mode=\"after\")\n    def passwords_match(self) -> \"UserCreate\":\n        if self.password != self.password_confirm:\n            raise ValueError(\"Passwords do not match\")\n        return self\n\n\nclass UserUpdate(BaseModel):\n    username: str | None = Field(default=None, min_length=3, max_length=50)\n    email: EmailStr | None = None\n\n\nclass UserResponse(UserBase):\n    id: int\n    is_active: bool\n    created_at: datetime\n\n    model_config = {\"from_attributes\": True}\n\n\nclass UserListResponse(BaseModel):\n    total: int\n    items: list[UserResponse]\n```\n\n---\n\n## Dependency Injection\n\n```python\n# app\u002Fdependencies.py\nfrom typing import Annotated, AsyncGenerator\nfrom fastapi import Depends, HTTPException, status\nfrom fastapi.security import OAuth2PasswordBearer\nfrom jose import JWTError, jwt\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.config import settings\nfrom app.database import AsyncSessionLocal\nfrom app.models.user import User\n\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"\u002Fusers\u002Ftoken\")\n\n\nasync def get_db() -> AsyncGenerator[AsyncSession, None]:\n    async with AsyncSessionLocal() as session:\n        try:\n            yield session\n        except Exception:\n            await session.rollback()\n            raise\n\n\nasync def get_current_user(\n    token: Annotated[str, Depends(oauth2_scheme)],\n    db: Annotated[AsyncSession, Depends(get_db)],\n) -> User:\n    credentials_exception = HTTPException(\n        status_code=status.HTTP_401_UNAUTHORIZED,\n        detail=\"Could not validate credentials\",\n        headers={\"WWW-Authenticate\": \"Bearer\"},\n    )\n    try:\n        payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])\n        subject = payload.get(\"sub\")\n        if subject is None:\n            raise credentials_exception\n        user_id = int(subject)\n    except (JWTError, TypeError, ValueError):\n        raise credentials_exception\n\n    user = await db.get(User, user_id)\n    if user is None:\n        raise credentials_exception\n    return user\n\n\nasync def get_current_active_user(\n    current_user: Annotated[User, Depends(get_current_user)],\n) -> User:\n    if not current_user.is_active:\n        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=\"Inactive user\")\n    return current_user\n\n\nDbDep = Annotated[AsyncSession, Depends(get_db)]\nCurrentUserDep = Annotated[User, Depends(get_current_user)]\nActiveUserDep = Annotated[User, Depends(get_current_active_user)]\n```\n\n---\n\n## Router and Endpoint Design\n\n```python\n# app\u002Frouters\u002Fusers.py\nfrom typing import Annotated\nfrom fastapi import APIRouter, HTTPException, Query, status\nfrom fastapi.security import OAuth2PasswordRequestForm\n\nfrom app.dependencies import ActiveUserDep, DbDep\nfrom app.schemas.user import UserCreate, UserResponse, UserUpdate, UserListResponse\nfrom app.services.user_service import DuplicateUserError, UserService\n\nrouter = APIRouter()\n\n\n@router.post(\"\u002F\", response_model=UserResponse, status_code=status.HTTP_201_CREATED)\nasync def create_user(payload: UserCreate, db: DbDep) -> UserResponse:\n    service = UserService(db)\n    try:\n        return await service.create(payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n\n@router.get(\"\u002Fme\", response_model=UserResponse)\nasync def get_me(current_user: ActiveUserDep) -> UserResponse:\n    return current_user\n\n\n@router.get(\"\u002F\", response_model=UserListResponse)\nasync def list_users(\n    db: DbDep,\n    current_user: ActiveUserDep,\n    skip: Annotated[int, Query(ge=0)] = 0,\n    limit: Annotated[int, Query(ge=1, le=100)] = 20,\n) -> UserListResponse:\n    service = UserService(db)\n    users, total = await service.list(skip=skip, limit=limit)\n    return UserListResponse(total=total, items=users)\n\n\n@router.patch(\"\u002F{user_id}\", response_model=UserResponse)\nasync def update_user(\n    user_id: int,\n    payload: UserUpdate,\n    db: DbDep,\n    current_user: ActiveUserDep,\n) -> UserResponse:\n    if current_user.id != user_id:\n        raise HTTPException(status_code=403, detail=\"Not authorized\")\n    service = UserService(db)\n    try:\n        user = await service.update(user_id, payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n    if user is None:\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    return user\n\n\n@router.post(\"\u002Ftoken\")\nasync def login(\n    form_data: Annotated[OAuth2PasswordRequestForm, Depends()],\n    db: DbDep,\n) -> dict[str, str]:\n    service = UserService(db)\n    token = await service.authenticate(form_data.username, form_data.password)\n    if token is None:\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Incorrect username or password\",\n            headers={\"WWW-Authenticate\": \"Bearer\"},\n        )\n    return {\"access_token\": token, \"token_type\": \"bearer\"}\n```\n\n---\n\n## Service Layer\n\n```python\n# app\u002Fservices\u002Fuser_service.py\nfrom datetime import datetime, timedelta, timezone\n\nfrom jose import jwt\nfrom passlib.context import CryptContext\nfrom sqlalchemy import func, select\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.config import settings\nfrom app.models.user import User\nfrom app.schemas.user import UserCreate, UserUpdate\n\npwd_context = CryptContext(schemes=[\"bcrypt\"], deprecated=\"auto\")\n\n\nclass DuplicateUserError(Exception):\n    \"\"\"Raised when a unique user field conflicts with an existing row.\"\"\"\n\n\nclass UserService:\n    def __init__(self, db: AsyncSession) -> None:\n        self.db = db\n\n    async def get_by_email(self, email: str) -> User | None:\n        result = await self.db.execute(select(User).where(User.email == email))\n        return result.scalar_one_or_none()\n\n    async def create(self, payload: UserCreate) -> User:\n        user = User(\n            email=payload.email,\n            username=payload.username,\n            hashed_password=pwd_context.hash(payload.password),\n        )\n        self.db.add(user)\n        try:\n            # Rely on atomic DB constraints rather than race-prone application-level prechecks\n            await self.db.commit()\n        except IntegrityError as exc:\n            await self.db.rollback()\n            raise DuplicateUserError from exc\n        await self.db.refresh(user)\n        return user\n\n    async def list(self, skip: int = 0, limit: int = 20) -> tuple[list[User], int]:\n        total_result = await self.db.execute(select(func.count(User.id)))\n        total = total_result.scalar_one()\n        # Enforce explicit deterministic ordering to ensure reliable pagination\n        result = await self.db.execute(\n            select(User).order_by(User.id).offset(skip).limit(limit)\n        )\n        return list(result.scalars()), total\n\n    async def update(self, user_id: int, payload: UserUpdate) -> User | None:\n        user = await self.db.get(User, user_id)\n        if user is None:\n            return None\n        for field, value in payload.model_dump(exclude_unset=True).items():\n            setattr(user, field, value)\n        try:\n            await self.db.commit()\n        except IntegrityError as exc:\n            await self.db.rollback()\n            raise DuplicateUserError from exc\n        await self.db.refresh(user)\n        return user\n\n    async def authenticate(self, email: str, password: str) -> str | None:\n        user = await self.get_by_email(email)\n        if user is None or not pwd_context.verify(password, user.hashed_password):\n            return None\n        expire = datetime.now(timezone.utc) + timedelta(\n            minutes=settings.access_token_expire_minutes\n        )\n        return jwt.encode(\n            {\"sub\": str(user.id), \"exp\": expire},\n            settings.secret_key,\n            algorithm=settings.algorithm,\n        )\n```\n\n> **Note on Database Design:** Application-level unique handling requires an underlying unique database index (e.g., `unique=True` on your SQLAlchemy mapping attributes). Without underlying constraints, application layer error-catching cannot safely prevent concurrent race conditions.\n\n---\n\n## Testing with httpx and pytest\n\n```python\n# tests\u002Fconftest.py\nimport pytest_asyncio\nfrom httpx import ASGITransport, AsyncClient\nfrom sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine\n\nfrom app.database import Base\nfrom app.dependencies import get_db\nfrom app.main import create_app\n\nTEST_DATABASE_URL = \"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\"\n\nengine = create_async_engine(TEST_DATABASE_URL)\nTestingSessionLocal = async_sessionmaker(engine, expire_on_commit=False)\n\n\n@pytest_asyncio.fixture(autouse=True)\nasync def setup_db():\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n    yield\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.drop_all)\n\n\n@pytest_asyncio.fixture\nasync def db_session():\n    async with TestingSessionLocal() as session:\n        yield session\n        await session.rollback()\n\n\n@pytest_asyncio.fixture\nasync def client(db_session: AsyncSession):\n    app = create_app()\n\n    async def override_get_db():\n        yield db_session\n\n    app.dependency_overrides[get_db] = override_get_db\n\n    async with AsyncClient(\n        transport=ASGITransport(app=app), base_url=\"http:\u002F\u002Ftest\"\n    ) as ac:\n        yield ac\n\n\n@pytest_asyncio.fixture\nasync def registered_user(client: AsyncClient) -> dict:\n    resp = await client.post(\"\u002Fusers\u002F\", json={\n        \"email\": \"test@example.com\",\n        \"username\": \"testuser\",\n        \"password\": \"securepass1\",\n        \"password_confirm\": \"securepass1\",\n    })\n    assert resp.status_code == 201\n    return resp.json()\n\n\n@pytest_asyncio.fixture\nasync def auth_token(client: AsyncClient, registered_user: dict) -> str:\n    resp = await client.post(\"\u002Fusers\u002Ftoken\", data={\n        \"username\": \"test@example.com\",\n        \"password\": \"securepass1\",\n    })\n    assert resp.status_code == 200\n    return resp.json()[\"access_token\"]\n\n\n@pytest_asyncio.fixture\nasync def auth_client(client: AsyncClient, auth_token: str) -> AsyncClient:\n    client.headers.update({\"Authorization\": f\"Bearer {auth_token}\"})\n    return client\n```\n\n---\n\n## Anti-Patterns\n\n```python\n# Bad: business logic inside route handlers.\n@router.post(\"\u002Fusers\u002F\")\nasync def create_user(payload: UserCreate, db: DbDep):\n    hashed = bcrypt.hash(payload.password)\n    user = User(email=payload.email, hashed_password=hashed)\n    db.add(user)\n    await db.commit()\n    return user\n\n# Good: thin route, transactional service handling.\n@router.post(\"\u002Fusers\u002F\", response_model=UserResponse, status_code=201)\nasync def create_user(payload: UserCreate, db: DbDep):\n    try:\n        return await UserService(db).create(payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n\n# Bad: sync DB calls in async routes block the event loop.\n@router.get(\"\u002Fitems\u002F\")\nasync def list_items(db: Session = Depends(get_db)):\n    return db.query(Item).all()\n\n# Good: use async SQLAlchemy executions.\n@router.get(\"\u002Fitems\u002F\")\nasync def list_items(db: AsyncSession = Depends(get_db)):\n    result = await db.execute(select(Item))\n    return result.scalars().all()\n```\n\n---\n\n## Best Practices\n\n- Always declare a typed `response_model` to prevent accidental PII\u002Fdata leaks and output clean OpenAPI schemas.\n- Consolidate standard middleware dependency injections via type-aliasing: `DbDep = Annotated[AsyncSession, Depends(get_db)]`.\n- Wrap database mutation boundaries gracefully within transactions inside your service layer, catching structural database errors directly.\n- Parse JWT parameters defensively, expecting potential string\u002Finteger cast mismatches from modern payload variations.\n- Enforce deterministic sorting (e.g., `.order_by(Model.id)`) on all offset\u002Flimit paginated endpoints to avoid data skips.\n- Isolate authorization checks from core authentication dependencies to provide precise REST status signals (`401` vs `403`).\n",{"name":5,"metadata":24,"description":7},{"origin":25},"ECC","main","a73152352c0612252131e5cf84e91312a6cf17dc","backend-database\u002Ffastapi-patterns","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs","https:\u002F\u002Fgithub.com\u002FHenrique1601","https:\u002F\u002Fbezerraportifolio.netlify.app\u002F","Full Stack Developer | React, Next.js, TypeScript, Node.js & IA | Construindo produtos digitais e soluções SaaS","2026-11-15 02:56:04",{"source":8,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add Henrique1601\u002FSkiilsToIAs --skill fastapi-patterns",{"tool":40,"command":41},"bunx","bunx skills add Henrique1601\u002FSkiilsToIAs --skill fastapi-patterns",{"tool":43,"command":44},"pnpm","pnpm dlx skills add Henrique1601\u002FSkiilsToIAs --skill fastapi-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Fbackend-database\u002Ffastapi-patterns\u002FSKILL.md",true,[],[49,56,63,71,78,85],{"skillNo":50,"name":51,"nameZh":6,"description":52,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":53,"categories":54,"keywords":55,"publishedAt":19},"01M7E2ANBW9V6AVX5JXNCEAWF8","rag-architect-agent","Agente Arquiteto de RAG, Embeddings & Busca Vetorial. Especialista em design e implementação de pipelines de Retrieval-Augmented Generation de alta fidelidade: chunking semântico, modelagem pgvector no PostgreSQL (Neon\u002FSupabase), busca híbrida (cosseno + BM25), reranking, validação via TypeSafe AI e filosofia Ponytail (anti-overengineering).","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Fcustom-agents\u002Frag-architect-agent\u002FSKILL.md",[],[],{"skillNo":57,"name":58,"nameZh":6,"description":59,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":60,"categories":61,"keywords":62,"publishedAt":19},"01M7E29KKF8R02ZRGYE2MJM67G","rag-architecture-patterns","Padrões arquiteturais e implementação de pipelines RAG (Retrieval-Augmented Generation) de alta fidelidade para qualquer projeto ou sistema. Cobre chunking semântico, embeddings, busca híbrida (pgvector + BM25), reranking, validação via TypeSafe AI e filosofia Ponytail (anti-overengineering).","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Fai-agents\u002Frag-architecture-patterns\u002FSKILL.md",[],[],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":67,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":70,"publishedAt":19},"01M7CWRA8E3BJ7CPY8N8EG28J1","skill-repair","Use this to fix and re-install agent skills that have failed installation.\nThis skill provides the necessary context and permissions to surgically update\nthe `manifest.json` after a fix has been applied.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Futilities\u002Fskill-repair\u002FSKILL.md",[],[],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":19},"01M7CWR66FZP8FA47WVZ4Z2EXC","security-scan","Scan your Claude Code configuration (.claude\u002F directory) for security vulnerabilities, misconfigurations, and injection risks using AgentShield. Checks CLAUDE.md, settings.json, MCP servers, hooks, and agent definitions.","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Futilities\u002Fsecurity-scan\u002FSKILL.md",[],[],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":19},"01M7CWR0QPY58QPTR1P759TFNG","security-review","Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment\u002Fsensitive features. Provides comprehensive security checklist and patterns.","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Futilities\u002Fsecurity-review\u002FSKILL.md",[],[],{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":19},"01M7CWQXCE40XWT4FZ3VWZFGDP","security-bounty-hunter","Hunt for exploitable, bounty-worthy security issues in repositories. Focuses on remotely reachable vulnerabilities that qualify for real reports instead of noisy local-only findings.","https:\u002F\u002Fgithub.com\u002FHenrique1601\u002FSkiilsToIAs\u002Fblob\u002Fmain\u002Futilities\u002Fsecurity-bounty-hunter\u002FSKILL.md",[],[]]