Ojasa Mirai

Ojasa Mirai

FastAPI

Loading...

Learning Level

🟢 BeginneršŸ”µ Advanced
šŸš€ What is FastAPIšŸ“š Creating Your First APIšŸ“š Running the ServeršŸ“š HTTP MethodsšŸ“š API EndpointsšŸ“š Request and ResponsešŸ“š DocumentationšŸ“š Testing Basics
Fastapi/Api Basics/First Api

Building Production-Grade APIs šŸ¢

Creating APIs that scale requires proper architecture, error handling, and following best practices.

Project Structure

Organize code for maintainability:

api/
ā”œā”€ā”€ main.py
ā”œā”€ā”€ requirements.txt
ā”œā”€ā”€ .env
ā”œā”€ā”€ .env.example
ā”œā”€ā”€ app/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ config.py
│   ā”œā”€ā”€ database.py
│   ā”œā”€ā”€ models/
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ user.py
│   │   └── item.py
│   ā”œā”€ā”€ schemas/
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ user.py
│   │   └── item.py
│   ā”œā”€ā”€ api/
│   │   ā”œā”€ā”€ __init__.py
│   │   ā”œā”€ā”€ dependencies.py
│   │   └── routes/
│   │       ā”œā”€ā”€ __init__.py
│   │       ā”œā”€ā”€ users.py
│   │       └── items.py
│   └── core/
│       ā”œā”€ā”€ __init__.py
│       ā”œā”€ā”€ security.py
│       └── logging.py
└── tests/
    ā”œā”€ā”€ __init__.py
    ā”œā”€ā”€ conftest.py
    └── test_items.py

Configuration Management

# app/config.py
from pydantic_settings import BaseSettings
from typing import Optional

class Settings(BaseSettings):
    app_name: str = "My API"
    app_version: str = "1.0.0"
    debug: bool = False

    # Database
    database_url: str
    db_pool_size: int = 20
    db_max_overflow: int = 40

    # Security
    secret_key: str
    algorithm: str = "HS256"
    access_token_expire_minutes: int = 30

    # API
    api_v1_str: str = "/api/v1"
    allowed_hosts: list = ["*"]

    class Config:
        env_file = ".env"
        case_sensitive = True

settings = Settings()

Database Integration

# app/database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from app.config import settings

engine = create_engine(
    settings.database_url,
    pool_size=settings.db_pool_size,
    max_overflow=settings.db_max_overflow,
    pool_pre_ping=True,  # Test connections before using
    echo=settings.debug
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db() -> Session:
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

Models and Schemas

# app/models/item.py
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(255), nullable=False, index=True)
    description = Column(String(1000))
    price = Column(Float, nullable=False)
    owner_id = Column(Integer, ForeignKey("users.id"))
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    owner = relationship("User", back_populates="items")

# app/schemas/item.py
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional

class ItemBase(BaseModel):
    title: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    price: float = Field(..., gt=0)

class ItemCreate(ItemBase):
    pass

class ItemUpdate(BaseModel):
    title: Optional[str] = None
    description: Optional[str] = None
    price: Optional[float] = None

class Item(ItemBase):
    id: int
    owner_id: int
    created_at: datetime
    updated_at: datetime

    class Config:
        from_attributes = True

Route Handlers with Error Handling

# app/api/routes/items.py
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List
import logging

from app.database import get_db
from app.models.item import Item as ItemModel
from app.schemas.item import Item, ItemCreate, ItemUpdate

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/items", tags=["items"])

@router.get("", response_model=List[Item])
async def list_items(
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=1, le=100),
    db: Session = Depends(get_db)
):
    '''List items with pagination'''
    items = db.query(ItemModel).offset(skip).limit(limit).all()
    return items

@router.post("", response_model=Item, status_code=status.HTTP_201_CREATED)
async def create_item(
    item: ItemCreate,
    db: Session = Depends(get_db)
):
    '''Create a new item'''
    try:
        db_item = ItemModel(**item.dict())
        db.add(db_item)
        db.commit()
        db.refresh(db_item)
        logger.info(f"Created item: {db_item.id}")
        return db_item
    except Exception as e:
        db.rollback()
        logger.error(f"Error creating item: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to create item"
        )

@router.get("/{item_id}", response_model=Item)
async def get_item(item_id: int, db: Session = Depends(get_db)):
    '''Get item by ID'''
    item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
    if not item:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Item not found"
        )
    return item

@router.put("/{item_id}", response_model=Item)
async def update_item(
    item_id: int,
    item: ItemUpdate,
    db: Session = Depends(get_db)
):
    '''Update an item'''
    db_item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
    if not db_item:
        raise HTTPException(status_code=404, detail="Item not found")

    update_data = item.dict(exclude_unset=True)
    for field, value in update_data.items():
        setattr(db_item, field, value)

    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    logger.info(f"Updated item: {item_id}")
    return db_item

@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int, db: Session = Depends(get_db)):
    '''Delete an item'''
    item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
    if not item:
        raise HTTPException(status_code=404, detail="Item not found")

    db.delete(item)
    db.commit()
    logger.info(f"Deleted item: {item_id}")

Main Application Setup

# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging

from app.config import settings
from app.api.routes import items, users

logging.basicConfig(level=logging.INFO)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    logger = logging.getLogger(__name__)
    logger.info("Starting up")
    yield
    # Shutdown
    logger.info("Shutting down")

app = FastAPI(
    title=settings.app_name,
    version=settings.app_version,
    lifespan=lifespan
)

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.allowed_hosts,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Routes
app.include_router(items.router)
app.include_router(users.router)

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=8000,
        reload=settings.debug,
        workers=1 if settings.debug else 4
    )

Key Takeaways

  • āœ… Organize code into models, schemas, routes, and core modules
  • āœ… Use Pydantic for request/response validation
  • āœ… Implement proper error handling with meaningful messages
  • āœ… Use SQLAlchemy for database operations
  • āœ… Add logging for debugging and monitoring
  • āœ… Configure CORS for frontend integration
  • āœ… Use environment variables for configuration
  • āœ… Use multiple workers in production

Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

Ā© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn