CRUD Operations
CRUD - Create, Read, Update, Delete - is the backbone of most APIs. This chapter assembles the pieces you already know (models, path/query parameters, response models, status codes) into a complete resource API with consistent conventions.
Models First
from pydantic import BaseModel, Field
class ItemCreate(BaseModel):
name: str
price: float = Field(gt=0)
tags: list[str] = []
class ItemOut(ItemCreate):
id: int
class ItemUpdate(BaseModel):
name: str | None = None
price: float | None = Field(default=None, gt=0)
An In-Memory Store
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
items_db: dict[int, ItemOut] = {}
next_id = 1
@app.post("/items/", response_model=ItemOut, status_code=status.HTTP_201_CREATED)
def create_item(item: ItemCreate):
global next_id
out = ItemOut(id=next_id, **item.model_dump())
items_db[next_id] = out
next_id += 1
return out
@app.get("/items/", response_model=list[ItemOut])
def list_items(skip: int = 0, limit: int = 100):
return list(items_db.values())[skip:skip + limit]
@app.get("/items/{item_id}", response_model=ItemOut)
def read_item(item_id: int):
item = items_db.get(item_id)
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item
@app.patch("/items/{item_id}", response_model=ItemOut)
def update_item(item_id: int, item: ItemUpdate):
current = items_db.get(item_id)
if current is None:
raise HTTPException(status_code=404, detail="Item not found")
data = current.model_dump()
data.update({k: v for k, v in item.model_dump().items() if v is not None})
updated = ItemOut(**data)
items_db[item_id] = updated
return updated
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
del items_db[item_id]
return None
Conventions in the Code
- POST returns 201 with the created resource; DELETE returns 204 with no body.
- PATCH is a partial update: None fields mean "unchanged".
- Missing resources are 404, not 500 - raise HTTPException explicitly.
- List endpoints accept skip/limit for pagination from day one.
- Separate Create/Update/Out models keep input and output contracts independent.
Summary
A CRUD API is a set of conventions applied to the tools you already have: Pydantic models for in/out, path and query parameters, 201/204/404 status codes, and a store (in-memory here, a database later). Follow the same shape for every resource and the API stays predictable as it grows.