# backend-skill > Generate FastAPI routes, handle requests/responses with validation, and connect to database. Use for REST API development. - Author: Your Name - Repository: muslim785/Hackathon2-phase3 - Version: 20260208121709 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-08 - Source: https://github.com/muslim785/Hackathon2-phase3 - Web: https://mule.run/skillshub/@@muslim785/Hackathon2-phase3~backend-skill:20260208121709 --- --- name: backend-skill description: Generate FastAPI routes, handle requests/responses with validation, and connect to database. Use for REST API development. --- # Backend Skill ## Instructions 1. **Route Generation** - Organize routes by resource using APIRouter - Use proper HTTP methods (GET, POST, PUT, PATCH, DELETE) - Define path and query parameters with types - Implement proper endpoint naming conventions - Group related endpoints logically 2. **Request/Response Handling** - Define Pydantic models for validation - Use proper status codes (200, 201, 400, 401, 404, 500) - Implement error handling with HTTPException - Add response models for automatic documentation - Validate all incoming data 3. **Database Connection** - Set up database session with dependency injection - Use async database operations - Implement connection pooling - Handle transactions properly - Close connections after use 4. **API Structure** - Implement CRUD operations (Create, Read, Update, Delete) - Use service layer for business logic - Add pagination for list endpoints - Implement filtering and sorting - Handle relationships and joins ## Best Practices - Use dependency injection for database sessions and auth - Implement async/await for all I/O operations - Add type hints to all functions - Use Pydantic models for request/response validation - Return proper HTTP status codes - Add docstrings for automatic OpenAPI docs - Handle exceptions with custom exception handlers - Use environment variables for configuration - Implement logging for debugging - Add request validation at multiple levels - Use routers to organize endpoints by domain - Implement rate limiting on public endpoints - Add health check endpoints - Use connection pooling for database efficiency ## Example Structure ```python # main.py - FastAPI application entry point from fastapi import FastAPI from app.routers import users, posts, auth from app.database import engine, Base # Create tables Base.metadata.create_all(bind=engine) app = FastAPI( title="My API", description="FastAPI backend with PostgreSQL", version="1.0.0" ) # CORS configuration app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(users.router, prefix="/api/users", tags=["users"]) app.include_router(posts.router, prefix="/api/posts", tags=["posts"]) @app.get("/") async def root(): return {"message": "API is running"} @app.get("/health") async def health_check(): return {"status": "healthy"} ``` ```python # database.py - Database connection setup from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker import os DATABASE_URL = os.getenv("DATABASE_URL") engine = create_engine( DATABASE_URL, pool_size=20, max_overflow=0, pool_pre_ping=True ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # Dependency for database session def get_db(): db = SessionLocal() try: yield db finally: db.close() ``` ```python # models/user.py - SQLAlchemy database model from sqlalchemy import Column, Integer, String, Boolean, DateTime from sqlalchemy.sql import func from app.database import Base class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, index=True) email = Column(String(255), unique=True, index=True, nullable=False) username = Column(String(50), unique=True, index=True, nullable=False) password_hash = Column(String(255), nullable=False) is_active = Column(Boolean, default=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) ``` ```python # schemas/user.py - Pydantic models for validation from pydantic import BaseModel, EmailStr, Field from datetime import datetime from typing import Optional class UserBase(BaseModel): email: EmailStr username: str = Field(..., min_length=3, max_length=50) class UserCreate(UserBase): password: str = Field(..., min_length=8) class UserUpdate(BaseModel): email: Optional[EmailStr] = None username: Optional[str] = Field(None, min_length=3, max_length=50) class UserResponse(UserBase): id: int is_active: bool created_at: datetime class Config: from_attributes = True class UserListResponse(BaseModel): users: list[UserResponse] total: int page: int page_size: int ``` ```python # routers/users.py - User endpoints from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.orm import Session from typing import List from app.database import get_db from app.models.user import User from app.schemas.user import UserCreate, UserResponse, UserUpdate, UserListResponse from app.dependencies import get_current_user import bcrypt router = APIRouter() @router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED) async def create_user( user_data: UserCreate, db: Session = Depends(get_db) ): """ Create a new user account. - **email**: Valid email address - **username**: Unique username (3-50 characters) - **password**: Secure password (minimum 8 characters) """ # Check if user exists existing_user = db.query(User).filter( (User.email == user_data.email) | (User.username == user_data.username) ).first() if existing_user: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email or username already registered" ) # Hash password hashed_password = bcrypt.hashpw( user_data.password.encode('utf-8'), bcrypt.gensalt() ).decode('utf-8') # Create user db_user = User( email=user_data.email, username=user_data.username, password_hash=hashed_password ) db.add(db_user) db.commit() db.refresh(db_user) return db_user @router.get("/", response_model=UserListResponse) async def list_users( page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Get paginated list of users. - **page**: Page number (default: 1) - **page_size**: Items per page (default: 10, max: 100) """ skip = (page - 1) * page_size users = db.query(User).offset(skip).limit(page_size).all() total = db.query(User).count() return { "users": users, "total": total, "page": page, "page_size": page_size } @router.get("/{user_id}", response_model=UserResponse) async def get_user( user_id: int, db: Session = Depends(get_db) ): """ Get user by ID. """ user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) return user @router.put("/{user_id}", response_model=UserResponse) async def update_user( user_id: int, user_data: UserUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Update user information. """ # Check if user is updating their own profile if current_user.id != user_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized to update this user" ) user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) # Update fields update_data = user_data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(user, field, value) db.commit() db.refresh(user) return user @router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_user( user_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Delete user account. """ if current_user.id != user_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized to delete this user" ) user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) db.delete(user) db.commit() return None ``` ```python # dependencies.py - Shared dependencies from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from app.database import get_db from app.models.user import User import jwt import os security = HTTPBearer() async def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db) ) -> User: """ Dependency to get current authenticated user from JWT token. """ token = credentials.credentials try: payload = jwt.decode( token, os.getenv("JWT_SECRET"), algorithms=["HS256"] ) user_id = payload.get("user_id") if user_id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials" ) except jwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired" ) except jwt.InvalidTokenError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" ) user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found" ) return user ``` ## Common Patterns - **CRUD Operations**: Create, Read, Update, Delete for each resource - **Pagination**: Use skip/limit or cursor-based pagination - **Filtering**: Accept query parameters for filtering results - **Sorting**: Allow sorting by different fields - **Authentication**: Use dependency injection for protected routes - **Transactions**: Use db.commit() and db.rollback() properly - **Relationships**: Use SQLAlchemy relationships for joins - **Bulk Operations**: Support creating/updating multiple items ## Error Handling ```python from fastapi import HTTPException, Request, status from fastapi.responses import JSONResponse @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException): return JSONResponse( status_code=exc.status_code, content={ "error": exc.detail, "status_code": exc.status_code } ) @app.exception_handler(Exception) async def general_exception_handler(request: Request, exc: Exception): return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={ "error": "Internal server error", "status_code": 500 } ) ```