DEV Community

Cover image for Day 7: Response Models & Data Validation with Pydantic in FastAPI
Utkarsh Rastogi
Utkarsh Rastogi

Posted on • Edited on

Day 7: Response Models & Data Validation with Pydantic in FastAPI

Welcome back to the FastAPI Zero to Hero series!

In Day 7, we unlock one of FastAPI’s most powerful features — Response Models using Pydantic.


🧠 What is This All About?

In APIs, we often need to return data back to users. But not all data is safe or relevant to show.

For example:

  • 🔐 You never want to expose a user’s password.
  • 🧾 You may want to structure your response consistently.
  • 🔍 You may want to filter, validate, or serialize your response data.

FastAPI + Pydantic gives us all of this out of the box with response_model.


🔎 Real-Life Analogy

Imagine you're a restaurant kitchen 🧑‍🍳.

  • The kitchen has all ingredients (your raw DB model).
  • But the customer only sees a nicely plated dish (your response model).

That’s exactly what response_model does — hides the mess, and serves what’s needed. 🍽️


✅ Step-by-Step Working Example

🧱 Step 1: Simulate a Database Model

class UserInDB(BaseModel):
    id: int
    username: str
    email: str
    password: str
Enter fullscreen mode Exit fullscreen mode

🎯 Step 2: Create a Clean Response Model

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    username: str
    email: str

    class Config:
        orm_mode = True
Enter fullscreen mode Exit fullscreen mode

✅ This model will be used to filter out sensitive fields (like passwords) when returning user data.

orm_mode = True lets Pydantic work seamlessly with ORM/database objects (like SQLAlchemy).


⚙️ Step 3: Use response_model in FastAPI

@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
    return db.get_user(user_id)
Enter fullscreen mode Exit fullscreen mode

✅ The response_model=UserResponse ensures that only the fields defined in UserResponse are returned to the client — filtering out any sensitive or unnecessary data like password.


🧪 Step 4: Filter Sensitive Info Automatically

If UserInDB has password, it won't show up in the response — FastAPI automatically filters it out 🎉


🔐 Bonus: Public vs Authenticated Models

Sometimes you need to show limited user info to the public. For that, you can define a lighter model like PublicUser.

class PublicUser(BaseModel):
    id: int
    username: str

    class Config:
        orm_mode = True
Enter fullscreen mode Exit fullscreen mode

Use it in your route with response_model to automatically hide other fields like email, password, etc.

@app.get("/public/users", response_model=List[PublicUser])
def get_public_users():
    return db.get_all()
Enter fullscreen mode Exit fullscreen mode

📄 Complete Example: day7_response_models.py

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

# Simulated database model including sensitive fields
class UserInDB(BaseModel):
    id: int
    username: str
    email: str
    password: str
    is_admin: bool
    created_at: str

# Public response model (safe for public APIs)
class PublicUser(BaseModel):
    id: int
    username: str

    class Config:
        orm_mode = True

# Authenticated user response model (excludes password)
class UserResponse(BaseModel):
    id: int
    username: str
    email: str

    class Config:
        orm_mode = True

# Initialize FastAPI app
app = FastAPI()

# Fake in-memory user database
fake_users_db = {
    1: UserInDB(id=1, username="utkarsh", email="[email protected]", password="secret123", is_admin=True, created_at="2024-01-01"),
    2: UserInDB(id=2, username="john", email="[email protected]", password="topsecret", is_admin=False, created_at="2024-02-01")
}

# Authenticated route — returns full user info except sensitive fields
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
    return fake_users_db.get(user_id)

# Authenticated route — returns all users excluding sensitive fields
@app.get("/users", response_model=List[UserResponse])
def list_users():
    return list(fake_users_db.values())

# Public route — returns only public-safe fields
@app.get("/public/users", response_model=List[PublicUser])
def get_public_users():
    return list(fake_users_db.values())
Enter fullscreen mode Exit fullscreen mode

🎁 Full Code to Try

Save this file as day7_response_models.py and run it with the command below:

uvicorn day7_response_models:app --host 0.0.0.0 --reload --port 9002
Enter fullscreen mode Exit fullscreen mode

Then test it in your browser at:

👉 http://localhost:9002/docs — Swagger UI for testing your endpoints.


🧪 Test These Endpoints

Endpoint Description
/users/1 Returns full user (excluding password)
/users Returns list of users (excluding password)
/public/users Returns public-safe version (id, username only)
/docs Explore and test all endpoints via Swagger UI

Input

AllUsers

Users

Public User


🎬 Wrap-Up: Why Response Models Matter

By now, you’ve seen how response_model in FastAPI acts like a smart filter — returning only what should be seen, and hiding what shouldn’t.

🔒 Real-World Relevance

Think of situations like:

  • 👨‍⚖️ Banking App: Your backend has account number, balance, PAN, and password. But you only want to show the user's name and balance on the dashboard — no PAN or password!

  • 🏥 Healthcare System: Patient data may include sensitive diagnosis history, insurance ID, and internal notes. But when accessed by reception or an external portal, only show basic details like name, age, and appointment time.

  • 📱 Social Media App: Each user has private settings, blocked users list, and email — but when you show profiles, you only show username, bio, and follower count.

✅ With FastAPI + Pydantic:

  • You write cleaner code by separating internal and external models.
  • You build safer APIs by preventing accidental data leaks.
  • You help frontend developers by giving structured, predictable responses.
  • You use response_model as a contract between your backend and client apps.

🔁 Revisit any day in the series or explore the full repo on GitHub (coming soon!).

🔔 Don’t forget to share what you learned and follow for more!

🧠 “Good APIs don’t just work — they protect, structure, and guide.”

See you in the next lesson! 👋


🙏 Credits

Huge thanks to the FastAPI Official Documentation by Sebastián Ramírez (@tiangolo) — the best place to learn and explore everything about FastAPI.


👨‍💻 About Me

Hey there! I’m Utkarsh Rastogi, an AWS Community Builder and passionate cloud-native enthusiast who loves building scalable backend systems and sharing knowledge with the community.

🔗 Connect with me: Utkarsh Rastogi


💬 Share Your Thoughts – I'd Love Your Feedback!

If you enjoyed today's post or learned something new, I'd truly appreciate it if you leave a comment or share your thoughts 👇

Your feedback, questions, or even a quick “🔥 Loved this!” keeps me motivated to continue this journey and share more in the upcoming #FastAPIDaily posts.

What did you find most helpful?

Anything you'd like explained in the next part?

Suggestions for improvement? I’m all ears! 🙌

Let’s grow and learn together — one FastAPI day at a time 🚀


Top comments (0)