Unit 06.02: Raising the right status, with a body
The status code and the headers carry information the body cannot.
201 with a Location, 204 with nothing
A create returning 201 and a location header, and a delete returning 204 with an empty body.
The code exercises both.
from fastapi import FastAPI, HTTPException, Response
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
STORE = {}
class Item(BaseModel):
name: str
@app.post("/items", status_code=201)
def create(body: Item, response: Response) -> dict:
item_id = f"item-{len(STORE) + 1}"
STORE[item_id] = body.name
response.headers["Location"] = f"/items/{item_id}"
return {"id": item_id}
@app.delete("/items/{item_id}", status_code=204)
def delete(item_id: str):
STORE.pop(item_id, None)
return None
client = TestClient(app)
r = client.post("/items", json={"name": "widget"})
print(f"POST -> {r.status_code}, Location: {r.headers.get('Location')}")
r = client.delete("/items/item-1")
print(f"DELETE -> {r.status_code}, body length {len(r.content)}")
print("\n201 with a Location header tells the client where the thing now is.")
201 plus Location tells the client where the created thing now lives, so they can fetch or update it without constructing the URL themselves. A 200 with an id in the body makes them do that construction, and they will do it slightly wrong.
204 means "succeeded, and there is deliberately nothing to return", which is different from a 200 with an empty object - a client can tell the difference and act on it.
The mistake this prevents
The mistake is returning 200 for everything. It works, and it discards the channel that tells the client what happened - which is exactly what they need when they did not write the code that called you.
Takeaway
Use 201 with a Location header for creation and 204 for a deliberate empty response. The status and headers carry meaning the body cannot.
