Returning JSON
In the previous examples, we returned Python dictionaries directly from our endpoints:
@app.get("/projects")
def list_projects() -> list[dict]:
return [{"id": 1, "name": "Example"}]
FastAPI automatically takes those Python dictionaries, serializes them into JSON strings, and sets the appropriate Content-Type: application/json HTTP headers.
Explicit Response Models
Because our mock_database is built using Pydantic models, our endpoints are currently returning ProjectRead instances directly. FastAPI can automatically serialize these Pydantic models just like it serializes dictionaries.
However, we should still explicitly declare our response_model in the route decorator.
Update your get_project function in main.py to specify the response_model argument inside the @app.get decorator. The other code in the file (the FastAPI import, the ProjectRead model, the app instance, the mock_database, and the other routes) stays the same.
from fastapi import FastAPI
from pydantic import BaseModel
# ... (ProjectRead, app, mock_database, and other routes unchanged) ...
@app.get("/projects/{project_id}", response_model=ProjectRead)
def get_project(project_id: int):
return mock_database.get(project_id)
Why use response_model?
FastAPI does several things when response_model=ProjectRead is specified:
-
Filtering:
If your internal object has a secret field (like a password hash), but your
ProjectReadmodel doesn't include it, FastAPI will completely strip the secret field out before sending the JSON to the user. -
Validation:
It ensures the data you are sending back strictly conforms to the expected types. If our mock database had a string
"one"for the ID instead of an integer1, Pydantic would catch it. -
Documentation:
It uses the model to automatically generate accurate OpenAPI documentation for the endpoint's response shape.
Heads-up: missing IDs now error
With response_model=ProjectRead, requesting a project that doesn't exist (like /projects/999) will now fail with a 500 ResponseValidationError. That's because mock_database.get(project_id) returns None when the key isn't found, and None doesn't satisfy the ProjectRead schema. We'll fix this in the exercise at the end of the chapter by raising an HTTPException with a 404 status code.