Python FastAPI Development

Expert in Python, FastAPI, async programming, and REST API development

# Python FastAPI Development Best Practices

Comprehensive guide for building production-ready APIs with FastAPI, async Python, and modern backend development patterns.

---

## Core Principles

1. **Type-First Development**
   - Use Python 3.8+ type hints for all function parameters and return values
   - Leverage Pydantic models for request/response validation
   - Enable strict type checking with mypy in your development workflow
   - Example:
     ```python
     from typing import List, Optional
     from pydantic import BaseModel

     class UserCreate(BaseModel):
         username: str
         email: str
         password: str

     class UserResponse(BaseModel):
         id: int
         username: str
         email: str
         is_active: bool
     ```

2. **Async-First Architecture**
   - Use async/await for all I/O operations (database, HTTP requests, file operations)
   - Implement async database sessions with SQLAlchemy
   - Use async HTTP client libraries like httpx for external API calls
   - Example:
     ```python
     @app.get("/users/{user_id}")
     async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
         user = await db.get(User, user_id)
         if not user:
             raise HTTPException(status_code=404, detail="User not found")
         return user
     ```

3. **Clean Code Standards**
   - Follow PEP 8 style guidelines consistently
   - Use black for code formatting and isort for import organization
   - Implement comprehensive docstrings for all public functions
   - Keep functions small and focused on single responsibilities

---

## Project Structure

4. **Modular Application Architecture**
   - Organize code into logical modules (routers, models, schemas, services)
   - Separate business logic from API logic
   - Example structure:
     ```
     app/
       __init__.py
       main.py              # FastAPI app instance
       config.py            # Configuration settings
       dependencies.py      # Shared dependencies
       models/              # SQLAlchemy models
         __init__.py
         user.py
         product.py
       schemas/             # Pydantic schemas
         __init__.py
         user.py
         product.py
       routers/             # API route handlers
         __init__.py
         users.py
         products.py
       services/            # Business logic
         __init__.py
         user_service.py
         product_service.py
       tests/               # Test modules
         __init__.py
         test_users.py
     ```

5. **Configuration Management**
   - Use Pydantic BaseSettings for environment-based configuration
   - Keep sensitive data in environment variables
   - Implement different configs for development, testing, and production
   - Example:
     ```python
     from pydantic import BaseSettings

     class Settings(BaseSettings):
         database_url: str
         secret_key: str
         algorithm: str = "HS256"
         access_token_expire_minutes: int = 30

         class Config:
             env_file = ".env"

     settings = Settings()
     ```

---

## API Design Patterns

6. **RESTful Resource Design**
   - Use appropriate HTTP methods (GET, POST, PUT, PATCH, DELETE)
   - Implement consistent URL patterns and naming conventions
   - Return appropriate HTTP status codes
   - Example:
     ```python
     from fastapi import APIRouter, status

     router = APIRouter(prefix="/api/v1/users", tags=["users"])

     @router.post("/", status_code=status.HTTP_201_CREATED)
     async def create_user(user: UserCreate) -> UserResponse:
         # Implementation
         pass

     @router.get("/{user_id}")
     async def get_user(user_id: int) -> UserResponse:
         # Implementation
         pass
     ```

7. **Request/Response Models**
   - Create separate Pydantic models for create, update, and response operations
   - Use Field() for validation constraints and documentation
   - Implement proper error response models
   - Example:
     ```python
     from pydantic import BaseModel, Field, EmailStr

     class UserBase(BaseModel):
         username: str = Field(..., min_length=3, max_length=50)
         email: EmailStr

     class UserCreate(UserBase):
         password: str = Field(..., min_length=8)

     class UserUpdate(BaseModel):
         username: Optional[str] = Field(None, min_length=3, max_length=50)
         email: Optional[EmailStr] = None

     class UserResponse(UserBase):
         id: int
         is_active: bool
         created_at: datetime
     ```

8. **Dependency Injection**
   - Use FastAPI's dependency injection system for shared logic
   - Create reusable dependencies for database sessions, authentication, and validation
   - Implement proper dependency scoping
   - Example:
     ```python
     from fastapi import Depends, HTTPException
     from sqlalchemy.ext.asyncio import AsyncSession

     async def get_db() -> AsyncSession:
         async with async_session() as session:
             yield session

     async def get_current_user(
         token: str = Depends(oauth2_scheme),
         db: AsyncSession = Depends(get_db)
     ) -> User:
         # Token validation logic
         pass
     ```

---

## Database Integration

9. **SQLAlchemy Async Patterns**
   - Use asyncio-compatible database drivers (asyncpg for PostgreSQL)
   - Implement async session management
   - Use select() for queries instead of legacy query() methods
   - Example:
     ```python
     from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
     from sqlalchemy.orm import selectinload
     from sqlalchemy import select

     async def get_user_with_posts(db: AsyncSession, user_id: int):
         stmt = select(User).options(selectinload(User.posts)).where(User.id == user_id)
         result = await db.execute(stmt)
         return result.scalar_one_or_none()
     ```

10. **Database Models and Relationships**
    - Define clear SQLAlchemy models with proper relationships
    - Use appropriate column types and constraints
    - Implement proper indexing for performance
    - Example:
      ```python
      from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
      from sqlalchemy.orm import relationship

      class User(Base):
          __tablename__ = "users"

          id = Column(Integer, primary_key=True)
          username = Column(String(50), unique=True, nullable=False, index=True)
          email = Column(String(100), unique=True, nullable=False, index=True)
          created_at = Column(DateTime, default=datetime.utcnow)

          posts = relationship("Post", back_populates="author")
      ```

11. **Database Migrations**
    - Use Alembic for database schema migrations
    - Create meaningful migration messages and version control
    - Test migrations in development before applying to production
    - Example migration workflow:
      ```bash
      alembic revision --autogenerate -m "Add user posts relationship"
      alembic upgrade head
      ```

---

## Authentication and Security

12. **JWT Authentication**
    - Implement secure JWT token generation and validation
    - Use proper token expiration and refresh mechanisms
    - Store sensitive data securely (never in JWT payload)
    - Example:
      ```python
      from jose import JWTError, jwt
      from passlib.context import CryptContext

      pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

      def create_access_token(data: dict):
          to_encode = data.copy()
          expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
          to_encode.update({"exp": expire})
          return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
      ```

13. **Password Security**
    - Hash passwords using bcrypt with appropriate rounds
    - Implement password strength validation
    - Never store plain text passwords
    - Use secure password reset mechanisms

14. **API Security Headers**
    - Implement CORS properly for cross-origin requests
    - Add security headers for production deployment
    - Use HTTPS in production environments
    - Example:
      ```python
      from fastapi.middleware.cors import CORSMiddleware

      app.add_middleware(
          CORSMiddleware,
          allow_origins=["https://yourdomain.com"],
          allow_credentials=True,
          allow_methods=["GET", "POST", "PUT", "DELETE"],
          allow_headers=["*"],
      )
      ```

---

## Error Handling and Validation

15. **Custom Exception Handling**
    - Create custom exception classes for different error types
    - Implement global exception handlers
    - Return consistent error response formats
    - Example:
      ```python
      from fastapi import HTTPException
      from fastapi.exceptions import RequestValidationError
      from fastapi.responses import JSONResponse

      class UserNotFoundError(Exception):
          pass

      @app.exception_handler(UserNotFoundError)
      async def user_not_found_handler(request, exc):
          return JSONResponse(
              status_code=404,
              content={"error": "User not found", "detail": str(exc)}
          )
      ```

16. **Input Validation**
    - Use Pydantic validators for complex validation logic
    - Implement custom validators for business rules
    - Provide clear error messages for validation failures
    - Example:
      ```python
      from pydantic import validator

      class UserCreate(BaseModel):
          username: str
          email: str
          age: int

          @validator('age')
          def validate_age(cls, v):
              if v < 18:
                  raise ValueError('User must be at least 18 years old')
              return v
      ```

---

## Performance Optimization

17. **Database Query Optimization**
    - Use eager loading to prevent N+1 query problems
    - Implement proper database indexing
    - Use connection pooling for production deployments
    - Monitor and profile database queries

18. **Caching Strategies**
    - Implement Redis caching for frequently accessed data
    - Use appropriate cache expiration strategies
    - Cache expensive computations and external API calls
    - Example:
      ```python
      import redis
      from functools import wraps

      redis_client = redis.Redis(host='localhost', port=6379, db=0)

      def cache_result(expiration: int = 300):
          def decorator(func):
              @wraps(func)
              async def wrapper(*args, **kwargs):
                  cache_key = f"{func.__name__}:{hash(str(args) + str(kwargs))}"
                  cached = redis_client.get(cache_key)
                  if cached:
                      return json.loads(cached)
                  result = await func(*args, **kwargs)
                  redis_client.setex(cache_key, expiration, json.dumps(result))
                  return result
              return wrapper
          return decorator
      ```

19. **Background Tasks**
    - Use FastAPI background tasks for non-blocking operations
    - Implement Celery for complex background job processing
    - Handle task failures and retries appropriately
    - Example:
      ```python
      from fastapi import BackgroundTasks

      def send_email_notification(email: str, message: str):
          # Email sending logic
          pass

      @app.post("/users/")
      async def create_user(user: UserCreate, background_tasks: BackgroundTasks):
          # Create user logic
          background_tasks.add_task(send_email_notification, user.email, "Welcome!")
          return {"message": "User created"}
      ```

---

## Testing

20. **Test Structure**
    - Write comprehensive unit tests for all business logic
    - Implement integration tests for API endpoints
    - Use pytest with async support for testing
    - Example:
      ```python
      import pytest
      from httpx import AsyncClient

      @pytest.mark.asyncio
      async def test_create_user():
          async with AsyncClient(app=app, base_url="http://test") as ac:
              response = await ac.post("/users/", json={
                  "username": "testuser",
                  "email": "test@example.com",
                  "password": "testpass123"
              })
          assert response.status_code == 201
          assert response.json()["username"] == "testuser"
      ```

21. **Test Database Management**
    - Use separate test databases for isolation
    - Implement database fixtures for test data
    - Clean up test data after each test run

---

## Production Deployment

22. **Docker Containerization**
    - Create optimized Docker images for production
    - Use multi-stage builds to minimize image size
    - Implement proper health checks
    - Example Dockerfile:
      ```dockerfile
      FROM python:3.11-slim

      WORKDIR /app
      COPY requirements.txt .
      RUN pip install --no-cache-dir -r requirements.txt

      COPY . .
      EXPOSE 8000

      CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
      ```

23. **Environment Configuration**
    - Use environment variables for all configuration
    - Implement proper logging configuration
    - Set up monitoring and alerting for production

---

## Summary Checklist

- [ ] Use type hints and Pydantic models throughout
- [ ] Implement async/await for all I/O operations
- [ ] Structure project with clear separation of concerns
- [ ] Use dependency injection for shared logic
- [ ] Implement proper authentication and authorization
- [ ] Handle errors consistently with custom exceptions
- [ ] Optimize database queries and implement caching
- [ ] Write comprehensive tests for all functionality
- [ ] Use proper security headers and HTTPS
- [ ] Configure production deployment with Docker
- [ ] Implement monitoring and logging

---

Follow these practices to build robust, scalable, and maintainable FastAPI applications.
Python FastAPI Development - Cursor IDE AI Rule