Welcome back! In the previous lesson, we compared FastAPI to other frameworks and looked at the big picture. Now, we are going to get our hands dirty with the core building blocks.
These questions focus on the day-to-day work of a FastAPI developer. You will learn about the engine that powers data validation (Pydantic), how the magic documentation generation works, and how to handle different types of data inputs. If you can answer these five questions, you can build a functional API.
Pydantic is a standalone Python library for data validation and settings management using Python type annotations. While FastAPI uses it heavily, Pydantic can be used in any Python project.
Analogy: The Club Bouncer
Imagine your API function is an exclusive club. Pydantic is the bouncer at the door. You give the bouncer a checklist (your data model): "Must have a name (string), age must be over 18 (int), and email is optional." If the incoming data doesn't match this checklist perfectly, Pydantic rejects it before it even enters the club (your function).
How FastAPI uses it:
FastAPI uses Pydantic to:
"age": "25" (string), Pydantic converts it to 25 (int).from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 1. Define the Pydantic Model
class User(BaseModel):
username: str
email: str
age: int # Pydantic will ensure this is an integer
# 2. Use it in a route
@app.post("/users/")
def create_user(user: User):
# If code reaches here, 'user' is guaranteed to be valid
return {"message": f"User {user.username} created!"}
# Scenario: Client sends {"username": "alice", "email": "a@b.com", "age": "twenty"}
# Output: 422 Validation Error (because "twenty" is not an int)One of FastAPI's most beloved features is that it documents itself. Because you use standard Python type hints and Pydantic models, FastAPI knows exactly what data your API expects and what it returns.
It uses this knowledge to generate an OpenAPI schema(formerly known as Swagger), which is a standard JSON definition of your API. Using this schema, it provides two default documentation interfaces:
Note: You do not need to write a separate YAML or JSON file for this. As soon as you write your Python function, the documentation is updated automatically.
This question checks if you know the basic boilerplate code. Creating an app involves three distinct steps: importing, instantiating, and defining a path operation.
# main.py
# Step 1: Import FastAPI
from fastapi import FastAPI
# Step 2: Create an instance of the class
# This 'app' object is what creates the API structure
app = FastAPI()
# Step 3: Define a Path Operation
# @decorator + method("path") + function
@app.get("/")
def read_root():
return {"message": "Welcome to my API"}
# Step 4: Run the server (usually in terminal)
# uvicorn main:app --reloadIn FastAPI terminology, a Path Operationis the combination of a URL path and an HTTP method.
/users/ or /items/.When you write @app.get("/items/"), you are creating a "Path Operation Decorator". It tells FastAPI: "Whenever a user sends a GET request to the path /items/, run the function immediately below."
The function itself (e.g., def get_items():) is technically called the Path Operation Function.
This is one of the most critical concepts for API design. You must know how to differentiate between these three ways of receiving data.
| Type | Description | Example URL |
|---|---|---|
| Path Parameter | Used to identify a specific resource. It is part of the URL path. Mandatory. | /items/5 |
| Query Parameter | Used to sort or filter resources. Appears after the ?. Usually optional. | /items?skip=0&limit=10 |
| Request Body | Complex data sent inside the request (JSON). Used for creating/updating. Not visible in URL. | N/A (Hidden in payload) |
Here is how you define them in code. FastAPI distinguishes them automatically based on where you declare them.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
# 1. Path Parameter: Declared in the decorator string {item_id}
# 2. Query Parameter: Function argument NOT in path (q)
# 3. Request Body: Function argument typed as a Pydantic model (item)
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item, q: str = None):
return {
"item_id": item_id, # from Path
"item_name": item.name, # from Body
"query": q # from Query String
}
# Request: PUT /items/55?q=urgent
# Body: {"name": "Hammer", "price": 9.99}
# Output:
# {
# "item_id": 55,
# "item_name": "Hammer",
# "query": "urgent"
# }3 questions · no sign-up, nothing stored
Progress is stored in this browser only - no sign-up, nothing sent anywhere.