FastAPI Interview Questions 11-15 (Validation, Nested Models, & Responses)
Hello again! In this section, we are going to tackle data integrity. Building an API isn't just about moving data from A to B; it is about ensuring that the data is correct, safe, and structured.
FastAPI shines here because it automates the boring work of checking data types. Instead of writing dozens of if statements to check if an age is a number or an email is valid, we use Pydantic. We will also cover how to protect sensitive data (like passwords) from being sent back to the user and how to communicate properly using HTTP status codes.
11. How do you validate request data in FastAPI?
In traditional frameworks, you often have to write manual validation logic (e.g., "is this field present?", "is it an integer?"). In FastAPI, validation is declarative and automatic.
You validate data by defining a Pydantic model and declaring it as a type hint in your path operation function.
When a request arrives, FastAPI does the following automatically:
- Reads the JSON body.
- Converts types (e.g., string "5" becomes integer 5).
- Validates the data against your model rules.
- Returns a clear error (422 Unprocessable Entity) if validation fails.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
# Define validation rules here
class UserSignup(BaseModel):
username: str
email: EmailStr # Requires 'pip install pydantic[email]'
age: int
@app.post("/signup/")
def signup(user: UserSignup):
# If we reach this line, 'user' is guaranteed to be valid.
# 'user.age' is definitely an int.
return {"message": f"User {user.username} validated successfully"}
Sample Output (Invalid Request):
{
"detail": [
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}12. What are Pydantic models?
If FastAPI is the car, Pydantic is the engine. Pydantic modelsare Python classes that inherit from BaseModel. They represent the "shape" or "blueprint" of your data.
Analogy: The Cookie Cutter
Think of raw data (JSON) as dough. A Pydantic model is a cookie cutter. You press the cutter onto the dough. If the dough fits the shape, you get a perfect, structured object. If the dough is the wrong consistency (wrong data type), it doesn't work.
Key capabilities of Pydantic models:
- Type Enforcement: Ensuring an
intis anint. - Data Parsing: Parsing a JSON string into a Python object.
- Helper Methods: Exporting data back to a dictionary (
.dict()) or JSON (.json()).
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
in_stock: bool = True # Default value
# Creating an instance (Parsing)
external_data = {"name": "Smartphone", "price": "999.99"}
product = Product(**external_data)
print(product.price)
# Output: 999.99 (It automatically converted string to float)
13. How do you handle nested Pydantic models?
Real-world data is rarely flat. You might have a user who has an address, and that address has a city and zip code. FastAPI handles this effortlessly using Nested Models.
You simply define a Pydantic model for the child (e.g., Address) and use it as a type hint inside the parent model (e.g., User). FastAPI understands this hierarchy and expects the incoming JSON to match it.
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Child Model
class Image(BaseModel):
url: str
name: str
# Parent Model
class Item(BaseModel):
name: str
description: str
# Nesting: An item has a list of Images
tags: List[str] = []
images: List[Image] = []
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "image_count": len(item.images)}
This allows clients to send complex JSON structures like:
{
"name": "Keyboard",
"description": "Mechanical",
"tags": ["tech", "sale"],
"images": [
{"url": "http://img.com/1.png", "name": "Front View"},
{"url": "http://img.com/2.png", "name": "Side View"}
]
}14. Explain response models in FastAPI.
A Response Model is used to define what data should be sent back to the client. This is critical for security.
The Problem: Your database object might contain a hashed_password field. If you just return the object directly, that secret data might be sent to the frontend.
The Solution: You define a Pydantic model that includes only the fields you want to share (e.g., username, email) and exclude the secrets. You pass this model to the response_model parameter in the decorator.
class UserIn(BaseModel):
username: str
password: str
email: str
class UserOut(BaseModel):
username: str
email: str
# Notice: 'password' is missing here
@app.post("/user/", response_model=UserOut)
def create_user(user: UserIn):
# We are returning the full 'user' object (which has the password)
# BUT, FastAPI will filter it through 'UserOut' before sending.
return user
Tip: This also automatically generates the correct schema for your API documentation, letting users know exactly what fields they will receive in the response.
15. How do you add custom response status codes?
HTTP status codes tell the client what happened. By default, FastAPI returns200 OK. However, if you create a new resource, you should return201 Created. If something is missing, you should return 404 Not Found.
There are two main ways to handle this:
- 1. For Success: Use the
status_codeparameter in the decorator. - 2. For Errors: Raise an
HTTPExceptioninside the function.
from fastapi import FastAPI, status, HTTPException
app = FastAPI()
tasks = {"1": "Buy milk"}
# 1. Success: explicitly set 201 Created
@app.post("/tasks/", status_code=status.HTTP_201_CREATED)
def create_task(task_id: str, name: str):
tasks[task_id] = name
return {"task_id": task_id, "name": name}
# 2. Error: dynamically raise 404
@app.get("/tasks/{task_id}")
def read_task(task_id: str):
if task_id not in tasks:
raise HTTPException(
status_code=404,
detail="Task not found"
)
return {"task": tasks[task_id]}
Using status.HTTP_201_CREATED is preferred over just writing 201because it makes your code more readable and prevents typos.