Response Handling

Loading concept...

🎁 FastAPI Response Handling: The Gift-Wrapping Factory

Imagine you’re running a gift-wrapping factory. People send you items, and your job is to wrap them beautifully before sending them back. That’s exactly what Response Handling does in FastAPI!

Your API receives requests, does some work, and then wraps the answer in a nice package before sending it back. Let’s learn how to be the best gift-wrapper in town!


🎯 What You’ll Learn

Topic What It Does
Response Models The “box” your gift goes in
Response Model Config Rules for how to wrap
HTTP Status Codes Labels saying “Success!” or “Oops!”
Custom Response Types Different wrapping paper styles
Cookies & Headers Little notes attached to the gift
JSON Compatible Encoder Making sure everything fits in the box

📦 Response Models: The Perfect Box

Think of a Response Model as the box you put your gift in. It decides:

  • What can go inside
  • What shape it should be
  • What people will see when they open it

The Problem Without a Box

Without a response model, you might accidentally send too much:

# Oops! Sending everything,
# including the password!
@app.get("/user")
def get_user():
    return {
        "name": "Alice",
        "email": "alice@mail.com",
        "password": "secret123"  # 😱
    }

The Solution: Use a Response Model!

from pydantic import BaseModel

class UserOut(BaseModel):
    name: str
    email: str
    # No password field = safe!

@app.get("/user", response_model=UserOut)
def get_user():
    return {
        "name": "Alice",
        "email": "alice@mail.com",
        "password": "secret123"
    }
# Only name & email go out! ✅

Magic! FastAPI automatically removes the password because UserOut doesn’t include it.

graph TD A[Your Data] --> B{Response Model Filter} B --> C[Safe Data Sent] B --> D[Sensitive Data Blocked] D --> E[🚫 Never Leaves Server]

⚙️ Response Model Configuration: Custom Wrapping Rules

Sometimes you need special rules for wrapping. FastAPI gives you configuration options!

Exclude Unset Values

Only send values that were actually set:

@app.get(
    "/item",
    response_model=Item,
    response_model_exclude_unset=True
)
def get_item():
    return Item(name="Toy")
# Only sends: {"name": "Toy"}
# Skips fields with default values!

Include or Exclude Specific Fields

# Only send these fields
@app.get(
    "/item",
    response_model=Item,
    response_model_include={"name", "price"}
)

# Send everything EXCEPT these
@app.get(
    "/item",
    response_model=Item,
    response_model_exclude={"secret_code"}
)

All Config Options at a Glance

Option What It Does
response_model_exclude_unset Skip fields user didn’t set
response_model_exclude_defaults Skip fields with default values
response_model_exclude_none Skip fields that are None
response_model_include Only include these fields
response_model_exclude Remove these fields

🚦 HTTP Status Codes: The Label on Your Package

When you send a package, you put a label:

  • ✅ “Delivered Successfully!”
  • ❌ “Address Not Found”
  • ⚠️ “Package Too Heavy”

HTTP Status Codes are those labels for your API!

Common Status Codes (Like Traffic Lights!)

Code Meaning Like…
200 OK - All good! 🟢 Green light
201 Created - New thing made! 🎂 Birthday!
204 No Content - Done, nothing to say 👍 Thumbs up
400 Bad Request - You asked wrong 🤷 “Huh?”
404 Not Found - Doesn’t exist 👻 Ghost
500 Server Error - We messed up 💥 Boom

Setting Status Codes

from fastapi import status

@app.post(
    "/item",
    status_code=status.HTTP_201_CREATED
)
def create_item(item: Item):
    # Save item...
    return item
# Returns with code 201!

Different Codes for Different Situations

from fastapi import HTTPException

@app.get("/item/{id}")
def get_item(id: int):
    if id not in items:
        raise HTTPException(
            status_code=404,
            detail="Item not found"
        )
    return items[id]
graph TD A[Request Arrives] --> B{Item Exists?} B -->|Yes| C[200 OK + Data] B -->|No| D[404 Not Found]

🎨 Custom Response Types: Different Wrapping Paper

Not everything needs to be wrapped in JSON! Sometimes you want:

  • 📄 HTML pages
  • 📁 Files to download
  • 📝 Plain text
  • 🔄 Streaming data

JSONResponse (Default Wrapping Paper)

from fastapi.responses import JSONResponse

@app.get("/data")
def get_data():
    return JSONResponse(
        content={"message": "Hello!"},
        status_code=200
    )

HTMLResponse (For Web Pages)

from fastapi.responses import HTMLResponse

@app.get("/page")
def get_page():
    html = "<h1>Welcome!</h1>"
    return HTMLResponse(content=html)

PlainTextResponse (Just Words)

from fastapi.responses import PlainTextResponse

@app.get("/hello")
def say_hello():
    return PlainTextResponse("Hello there!")

FileResponse (Send a File)

from fastapi.responses import FileResponse

@app.get("/download")
def download_file():
    return FileResponse(
        path="report.pdf",
        filename="my_report.pdf"
    )

StreamingResponse (Big Data, Piece by Piece)

from fastapi.responses import StreamingResponse

def generate_data():
    for i in range(10):
        yield f"Chunk {i}\n"

@app.get("/stream")
def stream_data():
    return StreamingResponse(
        generate_data(),
        media_type="text/plain"
    )

Quick Reference: Response Types

Type Best For
JSONResponse API data (default)
HTMLResponse Web pages
PlainTextResponse Simple text
FileResponse Downloadable files
StreamingResponse Large/continuous data
RedirectResponse Send user elsewhere

🍪 Response Cookies & Headers: Little Notes on the Gift

What Are Cookies?

Cookies are tiny notes your server leaves with the user’s browser. Like leaving a sticky note saying “Remember, this person likes chocolate!”

from fastapi import Response

@app.get("/login")
def login(response: Response):
    response.set_cookie(
        key="user_id",
        value="abc123",
        max_age=3600,  # 1 hour
        httponly=True  # JS can't read it
    )
    return {"message": "Logged in!"}

Cookie Options Explained

Option What It Does
key Name of the cookie
value What to remember
max_age How long to keep it (seconds)
httponly Hide from JavaScript (safer!)
secure Only send over HTTPS
samesite Prevent cross-site attacks

What Are Headers?

Headers are metadata about your response. Like the label on a shipping package showing weight, origin, and handling instructions.

@app.get("/data")
def get_data(response: Response):
    response.headers["X-Custom-Info"] = "Hello!"
    response.headers["Cache-Control"] = "max-age=60"
    return {"data": "Here it is!"}

Common Headers

Header Purpose
Content-Type What kind of data (json, html)
Cache-Control How long to cache
X-Request-ID Track this specific request
Access-Control-* CORS permissions

Deleting Cookies

@app.get("/logout")
def logout(response: Response):
    response.delete_cookie("user_id")
    return {"message": "Logged out!"}
graph TD A[Server Response] --> B[Response Body] A --> C[Headers] A --> D[Cookies] C --> E[Metadata about response] D --> F[Data stored in browser]

🔄 JSON Compatible Encoder: Making Everything Fit

Sometimes your data doesn’t fit nicely into JSON. Imagine trying to put a round peg in a square hole.

The Problem

from datetime import datetime

# This WILL cause problems:
data = {
    "name": "Event",
    "date": datetime.now()  # 😱 JSON doesn't know dates!
}

The Solution: jsonable_encoder

from fastapi.encoders import jsonable_encoder
from datetime import datetime

event = {
    "name": "Party",
    "date": datetime.now()
}

safe_data = jsonable_encoder(event)
# Now date is a string: "2024-01-15T10:30:00"

What It Converts

Python Type Becomes JSON
datetime ISO string
UUID String
Pydantic Model Dictionary
Decimal Float
bytes String (base64)
set List

Real Example: Saving to Database

from fastapi.encoders import jsonable_encoder

class Item(BaseModel):
    name: str
    price: float
    created: datetime

@app.post("/item")
def create_item(item: Item):
    # Convert to JSON-safe dict
    item_data = jsonable_encoder(item)

    # Now safe to save to database
    # or send anywhere!
    db.save(item_data)
    return item_data

When to Use It

  • ✅ Before saving to a database
  • ✅ Before caching data
  • ✅ When returning non-standard types
  • ✅ When building custom responses

🎯 Putting It All Together

Here’s a complete example using everything we learned:

from fastapi import FastAPI, Response, status
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from datetime import datetime

app = FastAPI()

class ItemIn(BaseModel):
    name: str
    price: float
    secret_code: str

class ItemOut(BaseModel):
    name: str
    price: float
    created_at: datetime

@app.post(
    "/items",
    response_model=ItemOut,
    status_code=status.HTTP_201_CREATED,
    response_model_exclude_none=True
)
def create_item(
    item: ItemIn,
    response: Response
):
    # Create new item with timestamp
    new_item = {
        **item.dict(),
        "created_at": datetime.now()
    }

    # Make it JSON-safe
    safe_item = jsonable_encoder(new_item)

    # Add custom header
    response.headers["X-Item-Created"] = "true"

    # Set a cookie
    response.set_cookie(
        key="last_item",
        value=new_item["name"]
    )

    return safe_item

🌟 Key Takeaways

  1. Response Models = Your safety filter (like a bouncer)
  2. Config Options = Fine-tune what gets sent
  3. Status Codes = Tell users what happened (success/error)
  4. Custom Responses = Different formats for different needs
  5. Cookies & Headers = Extra info attached to responses
  6. jsonable_encoder = Convert weird Python types to JSON

🎁 Remember the Gift-Wrapping Analogy!

Concept Gift-Wrapping Analogy
Response Model The box shape
Config Wrapping rules
Status Code The label (“Fragile!”, “Priority!”)
Response Type The wrapping paper style
Cookies Sticky notes for later
Headers Shipping label info
JSON Encoder Making odd items fit the box

You’re now a Response Handling expert! 🎉

Go wrap some beautiful API responses!

Loading story...

No Story Available

This concept doesn't have a story yet.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

Interactive Preview

Interactive - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Interactive Content

This concept doesn't have interactive content yet.

Cheatsheet Preview

Cheatsheet - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Cheatsheet Available

This concept doesn't have a cheatsheet yet.

Quiz Preview

Quiz - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Quiz Available

This concept doesn't have a quiz yet.