From Read-Only to Interactive
Until now our API could only read data.
Today we add the ability to CREATE : the C in CRUD.
The Request & Response DTOs
Request : what the client sends
class ItemCreateDTO(BaseModel):
name: str
description: Optional[str] = None
price: float
@field_validator("price")
def price_must_be_positive(cls, v):
if v <= 0:
raise ValueError("Price must be a positive number")
return v
Response : what the API returns
class ItemResponseDTO(BaseModel):
id: int
name: str
description: Optional[str]
price: float
created_at: datetime
Two separate DTOs : one for input, one for output.
That's clean API design.
The POST Endpoint
@app.post("/items", response_model=ItemResponseDTO, status_code=201)
def create_item(item: ItemCreateDTO):
db = SessionLocal()
new_item = Item(
name=item.name,
description=item.description,
price=item.price
)
db.add(new_item)
db.commit()
db.refresh(new_item)
db.close()
return new_item
Notice status_code=201 : that's the correct HTTP status
for a successful creation, not 200.
Testing in Postman
Valid Request
Missing Name — 422 Error
Negative Price — 422 Error
Lessons Learned
POST endpoints need two things done right:
validation on the way in and the correct status
code on the way out. 201 Created is not the same as 200 OK,
and that distinction matters in production systems.
Day 9 done. 21 more to go. 🔥



Top comments (0)