fastapi-app
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".
Best use case
fastapi-app is best used when you need a repeatable AI agent workflow instead of a one-off prompt. It is especially useful for teams working in multi. 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".
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".
Users should expect a more consistent workflow output, faster repeated execution, and less time spent rewriting prompts from scratch.
Practical example
Example input
Use the "fastapi-app" skill to help with this workflow task. Context: 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".
Example output
A structured workflow result with clearer steps, more consistent formatting, and an output that is easier to reuse in the next run.
When to use this skill
- Use this skill when you want a reusable workflow rather than writing the same prompt again and again.
When not to use this skill
- Do not use this when you only need a one-off answer and do not need a reusable workflow.
- Do not use it if you cannot install or maintain the related files, repository context, or supporting tools.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/fastapi-app/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How fastapi-app Compares
| Feature / Agent | fastapi-app | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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".
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# FastAPI Application Skill
## Overview
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.
## When This Skill Applies
This skill triggers when users request:
- **App Setup**: "Create FastAPI app", "Initialize FastAPI", "Lifespan events"
- **Routes**: "Student endpoint", "API route", "GET/POST handler", "APIRouter"
- **Dependencies**: "DB dependency", "Auth dependency", "Depends()", "JWT auth"
- **CORS**: "CORS enable frontend", "Cross-origin config", "credentials"
- **Models**: "Pydantic model", "Student schema", "Fee validation"
## Core Rules
### 1. Init: FastAPI App and Lifespan
```python
# 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:**
- Use FastAPI() with lifespan context manager
- Configure CORS for frontend origins
- Include routers with APIRouter
- Set up logging for production
- Enable Swagger docs at /docs
### 2. Routes: APIRouter with Tags and Responses
```python
# 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:**
- Use APIRouter with prefix and tags
- Response models with Pydantic schemas
- Proper HTTP status codes (200, 201, 204, 404, 400)
- Dependencies for auth and DB
- Pagination with skip/limit
### 3. Dependencies: Auth and DB Sessions
```python
# 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)
```
```python
# 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:**
- Async SQLAlchemy sessions with context manager
- JWT token creation and validation
- HTTPBearer for token extraction
- Role-based access control
- Environment variables for secrets
### 4. Pydantic v2 Models
```python
# 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
}
```
```python
# 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:**
- Use Pydantic v2 ConfigDict instead of Config class
- Field validation with min/max, patterns, gt/lt
- EmailStr for email validation
- Enum for status fields
- Optional fields with defaults
- From_attributes for ORM compatibility
## Output Requirements
### Code Files
1. **Main Application**:
- `main.py` - FastAPI app initialization
- `config.py` - Settings and environment variables
2. **Routers**:
- `routers/__init__.py`
- `routers/students.py`
- `routers/fees.py`
- `routers/attendance.py`
- `routers/auth.py`
3. **Dependencies**:
- `dependencies/__init__.py`
- `dependencies/database.py`
- `dependencies/auth.py`
4. **Models and Schemas**:
- `models/__init__.py`
- `models/student.py`
- `schemas/__init__.py`
- `schemas/student.py`
- `schemas/fees.py`
### Integration Requirements
- **@api-client**: JSON response formatting for frontend
- **@auth-integration**: JWT validation
- **@react-component**: Error response schemas
### Documentation
- **PHR**: Create Prompt History Record for auth/DB decisions
- **ADR**: Document auth strategy (JWT vs session), DB choice (async SQLAlchemy)
- **Comments**: Document endpoint purposes and validation rules
## Workflow
1. **Initialize App**
- Create FastAPI instance with lifespan
- Configure CORS middleware
- Set up logging
2. **Setup Database**
- Create async SQLAlchemy engine
- Define session dependency
- Create database models
3. **Create Dependencies**
- Auth dependency with JWT
- Role-based access
- DB session management
4. **Define Schemas**
- Pydantic v2 models for requests/responses
- Validation rules
- Response formatting
5. **Build Routes**
- Create APIRouter for each domain
- Implement CRUD operations
- Add pagination and filtering
6. **Test and Document**
- Verify Swagger docs
- Test authentication
- Validate error responses
## Quality Checklist
Before completing any FastAPI implementation:
- [ ] **Pydantic v2 Validation**: ConfigDict, Field validators
- [ ] **SQLAlchemy Async**: Use async sessions, avoid blocking calls
- [ ] **Rate Limiting**: Implement slowapi or similar for endpoints
- [ ] **Swagger Docs Auto**: /docs shows all endpoints
- [ ] **Error Handling**: Proper HTTPException with status codes
- [ ] **Auth Protected**: All endpoints with dependencies
- [ ] **Pagination**: skip/limit for list endpoints
- [ ] **Type Hints**: All functions fully typed
- [ ] **Environment Config**: Secrets in environment variables
- [ ] **CORS Config**: Allow frontend origins with credentials
## Common Patterns
### Student Endpoint with Auth
```python
# 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()
```
### CORS Enable Frontend
```python
# 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"],
)
```
### DB Dependency with Session
```python
# 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()
```
### JWT Auth Dependency
```python
# 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"
)
```
## Rate Limiting
```python
# 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"}
```
## Environment Configuration
```python
# 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()
```
## Running the Application
```bash
# 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
```
## References
- FastAPI Documentation: https://fastapi.tiangolo.com
- Pydantic v2: https://docs.pydantic.dev/latest/
- SQLAlchemy Async: https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html
- JWT with FastAPI: https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
- SlowAPI Rate Limiting: https://pypi.org/project/slowapi/Related Skills
python-fastapi-development
Python FastAPI backend development with async patterns, SQLAlchemy, Pydantic, authentication, and production API patterns.
fastapi-templates
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
fastapi-router-py
Create FastAPI routers with CRUD operations, authentication dependencies, and proper response models. Use when building REST API endpoints, creating new routes, implementing CRUD operations, or adding authenticated endpoints in FastAPI applications.
fastapi-pro
Build high-performance async APIs with FastAPI, SQLAlchemy 2.0, and Pydantic V2. Master microservices, WebSockets, and modern Python async patterns. Use PROACTIVELY for FastAPI development, async optimization, or API architecture.
py-fastapi-patterns
FastAPI patterns for API design. Use when creating endpoints, handling dependencies, error handling, or working with OpenAPI schemas.
fastapi-development
Build async APIs with FastAPI, including endpoints, dependency injection, validation, and testing. Use when creating REST APIs, web backends, or microservices.
scaffolding-fastapi-dapr
Build production-grade FastAPI backends with SQLModel, Dapr integration, and JWT authentication. Use when building REST APIs with Neon PostgreSQL, implementing event-driven microservices with Dapr pub/sub, scheduling jobs, or creating CRUD endpoints with JWT/JWKS verification. NOT when building simple scripts or non-microservice architectures.
backend-fastapi
Documentation for the FastAPI backend, endpoints, and dependency injection.
fastapi-mastery
Comprehensive FastAPI development skill covering REST API creation, routing, request/response handling, validation, authentication, database integration, middleware, and deployment. Use when working with FastAPI projects, building APIs, implementing CRUD operations, setting up authentication/authorization, integrating databases (SQL/NoSQL), adding middleware, handling WebSockets, or deploying FastAPI applications. Triggered by requests involving .py files with FastAPI code, API endpoint creation, Pydantic models, or FastAPI-specific features.
python-fastapi
Python FastAPI development with uv package manager, modular project structure, SQLAlchemy ORM, and production-ready patterns.
python-fastapi-patterns
FastAPI web framework patterns. Triggers on: fastapi, api endpoint, dependency injection, pydantic model, openapi, swagger, starlette, async api, rest api, uvicorn.
azure-quotas
Check/manage Azure quotas and usage across providers. For deployment planning, capacity validation, region selection. WHEN: "check quotas", "service limits", "current usage", "request quota increase", "quota exceeded", "validate capacity", "regional availability", "provisioning limits", "vCPU limit", "how many vCPUs available in my subscription".