skills/awais68/fastapi-app/SKILL.md
Use when creating FastAPI backend applications - route handlers, dependencies, CORS config, or Pydantic models. NOT when frontend logic, non-Python backends, or unrelated server-side code. Triggers: "FastAPI", "student endpoint", "API route", "dependency injection", "CORS", "Pydantic model".
npx skillsauth add aiskillstore/marketplace fastapi-appInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert guidance for building FastAPI backend applications with route decorators, dependency injection, CORS configuration, and Pydantic v2 validation. Supports ERP endpoints for students, fees, attendance, and authentication.
This skill triggers when users request:
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routers import students, fees, attendance, auth
from dependencies.database import get_db
from dependencies.auth import get_current_user
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
logger.info("Starting up FastAPI application...")
yield
# Shutdown
logger.info("Shutting down FastAPI application...")
app = FastAPI(
title="ERP API",
description="Educational Resource Planning API",
version="1.0.0",
lifespan=lifespan,
docs_url="/docs",
redoc_url="/redoc",
)
# CORS Configuration
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "http://localhost:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers with prefix and tags
app.include_router(
auth.router,
prefix="/api/v1/auth",
tags=["Authentication"],
)
app.include_router(
students.router,
prefix="/api/v1/students",
tags=["Students"],
dependencies=[Depends(get_current_user)],
)
app.include_router(
fees.router,
prefix="/api/v1/fees",
tags=["Fees"],
dependencies=[Depends(get_current_user)],
)
app.include_router(
attendance.router,
prefix="/api/v1/attendance",
tags=["Attendance"],
dependencies=[Depends(get_current_user)],
)
Requirements:
# routers/students.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List, Optional
from pydantic import BaseModel, EmailStr, Field
from dependencies.database import get_db
from dependencies.auth import get_current_user, get_admin_user
from models.student import Student as StudentModel
from schemas.student import StudentCreate, StudentUpdate, StudentResponse
router = APIRouter()
@router.get("/", response_model=List[StudentResponse])
async def get_students(
skip: int = 0,
limit: int = 100,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_current_user),
):
"""Get all students with pagination"""
students = await db.execute(
select(StudentModel)
.offset(skip)
.limit(limit)
)
return students.scalars().all()
@router.get("/{student_id}", response_model=StudentResponse)
async def get_student(
student_id: str,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_current_user),
):
"""Get a single student by ID"""
student = await db.execute(
select(StudentModel).where(StudentModel.id == student_id)
)
student = student.scalar_one_or_none()
if not student:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Student not found"
)
return student
@router.post("/", response_model=StudentResponse, status_code=status.HTTP_201_CREATED)
async def create_student(
student_data: StudentCreate,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_admin_user),
):
"""Create a new student"""
# Check if email exists
existing = await db.execute(
select(StudentModel).where(StudentModel.email == student_data.email)
)
if existing.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Email already registered"
)
student = StudentModel(**student_data.model_dump())
db.add(student)
await db.commit()
await db.refresh(student)
return student
@router.put("/{student_id}", response_model=StudentResponse)
async def update_student(
student_id: str,
student_data: StudentUpdate,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_admin_user),
):
"""Update a student"""
student = await db.execute(
select(StudentModel).where(StudentModel.id == student_id)
)
student = student.scalar_one_or_none()
if not student:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Student not found"
)
update_data = student_data.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(student, field, value)
await db.commit()
await db.refresh(student)
return student
@router.delete("/{student_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_student(
student_id: str,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_admin_user),
):
"""Delete a student"""
student = await db.execute(
select(StudentModel).where(StudentModel.id == student_id)
)
student = student.scalar_one_or_none()
if not student:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Student not found"
)
await db.delete(student)
await db.commit()
Requirements:
# dependencies/database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from databases import Database
import os
DATABASE_URL = os.getenv(
"DATABASE_URL",
"postgresql+asyncpg://user:password@localhost:5432/erp_db"
)
engine = create_async_engine(DATABASE_URL, echo=True)
async_session_maker = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
async def get_db() -> AsyncSession:
async with async_session_maker() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
async def init_db():
"""Initialize database tables"""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# dependencies/auth.py
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import JWTError, jwt
from datetime import datetime, timedelta
from typing import Optional
import os
SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
security = HTTPBearer()
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=[ALGORITHM]
)
user_id: str = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return {"user_id": user_id, "role": payload.get("role")}
async def get_admin_user(current_user = Depends(get_current_user)):
if current_user.get("role") not in ["admin", "teacher"]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin access required"
)
return current_user
Requirements:
# schemas/student.py
from pydantic import BaseModel, EmailStr, Field, ConfigDict
from datetime import datetime
from typing import Optional
from enum import Enum
class StudentRole(str, Enum):
STUDENT = "student"
TEACHER = "teacher"
ADMIN = "admin"
# Base model with config
class StudentBase(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr
phone: Optional[str] = Field(None, pattern=r'^\+?[\d\s-]+$')
# Create schema
class StudentCreate(StudentBase):
password: str = Field(..., min_length=8)
class_id: Optional[str] = None
# Update schema (partial updates)
class StudentUpdate(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: Optional[str] = Field(None, min_length=2, max_length=100)
email: Optional[EmailStr] = None
phone: Optional[str] = None
class_id: Optional[str] = None
# Response schema
class StudentResponse(StudentBase):
id: str
class_id: Optional[str] = None
created_at: datetime
updated_at: datetime
# Pagination response
class PaginatedResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
data: list[StudentResponse]
meta: dict = {
"total": 0,
"page": 1,
"page_size": 100,
"total_pages": 1
}
# schemas/fees.py
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional
from enum import Enum
class FeeStatus(str, Enum):
PENDING = "pending"
PAID = "paid"
OVERDUE = "overdue"
WAIVED = "waived"
class FeeBase(BaseModel):
student_id: str
amount: float = Field(..., gt=0)
description: str = Field(..., max_length=500)
due_date: datetime
class FeeCreate(FeeBase):
pass
class FeeUpdate(BaseModel):
status: Optional[FeeStatus] = None
paid_date: Optional[datetime] = None
notes: Optional[str] = None
class FeeResponse(FeeBase):
id: str
status: FeeStatus
paid_date: Optional[datetime] = None
created_at: datetime
Requirements:
Main Application:
main.py - FastAPI app initializationconfig.py - Settings and environment variablesRouters:
routers/__init__.pyrouters/students.pyrouters/fees.pyrouters/attendance.pyrouters/auth.pyDependencies:
dependencies/__init__.pydependencies/database.pydependencies/auth.pyModels and Schemas:
models/__init__.pymodels/student.pyschemas/__init__.pyschemas/student.pyschemas/fees.pyInitialize App
Setup Database
Create Dependencies
Define Schemas
Build Routes
Test and Document
Before completing any FastAPI implementation:
# routers/students.py
@router.get("/students/", response_model=List[StudentResponse])
async def get_students(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
class_id: Optional[str] = None,
db: AsyncSession = Depends(get_db),
current_user = Depends(get_current_user),
):
"""Get students with pagination and optional class filter"""
query = select(StudentModel)
if class_id:
query = query.where(StudentModel.class_id == class_id)
query = query.offset(skip).limit(limit).order_by(StudentModel.created_at.desc())
result = await db.execute(query)
return result.scalars().all()
# main.py
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000", # Next.js dev
"http://localhost:5173", # Vite dev
"https://yourdomain.com", # Production
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
allow_headers=["Authorization", "Content-Type"],
)
# dependencies/database.py
async def get_db() -> AsyncSession:
async with async_session_maker() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
# dependencies/auth.py
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer
oauth2_scheme = HTTPBearer()
async def get_token_data(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token"
)
# dependencies/rate_limit.py
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
def rate_limit(requests: int = 60, seconds: int = 60):
def decorator(func):
return limiter.limit(f"{requests}/{seconds}")(func)
return decorator
# Usage
@router.get("/students/")
@rate_limit(requests=100, seconds=60)
async def get_students():
return {"message": "Students list"}
# config.py
from pydantic_settings import BaseSettings
from typing import List
class Settings(BaseSettings):
APP_NAME: str = "ERP API"
DEBUG: bool = False
API_V1_PREFIX: str = "/api/v1"
# Database
DATABASE_URL: str = "postgresql+asyncpg://user:password@localhost:5432/erp_db"
# JWT
JWT_SECRET_KEY: str
JWT_ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
# CORS
CORS_ORIGINS: List[str] = ["http://localhost:3000"]
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()
# Install dependencies
pip install fastapi uvicorn[standard] sqlalchemy[asyncio] asyncpg
pip install python-jose[cryptography] passlib[bcrypt]
pip install slowapi pydantic-settings
# Run development
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# Run production
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.