FastAPI Request Data Handling: Forms, Files & Metadata
The Restaurant Kitchen Analogy 🍽️
Imagine you’re running a busy restaurant kitchen. Every order that comes in has different parts:
- Form Data = Written notes from the waiter (customer name, table number)
- File Uploads = Actual photos of special requests or allergy cards
- Cookies = Loyalty cards customers bring back each visit
- Headers = Special instructions written on the order ticket envelope
FastAPI is like your super-organized kitchen manager who sorts ALL of this automatically!
1. Form Data Handling
What is Form Data?
When you fill out a form on a website (like your name and email), that information travels to the server as “form data.” It’s like filling out a paper form and handing it to someone.
Simple Example
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login")
async def login(
username: str = Form(),
password: str = Form()
):
return {"username": username}
What’s happening here?
- Someone fills in username and password
- FastAPI catches them with
Form() - You can use them right away!
Making Fields Optional
@app.post("/profile")
async def update_profile(
name: str = Form(),
bio: str = Form(default="")
):
return {"name": name, "bio": bio}
The default="" means “bio” can be empty. Like a form where some boxes are optional!
graph TD A[User Fills Form] --> B[Browser Sends Data] B --> C[FastAPI Catches with Form] C --> D[You Use the Data]
2. File Uploads
What is a File Upload?
When you attach a photo to send, that’s a file upload. FastAPI handles this beautifully!
Simple File Upload
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/upload")
async def upload_file(
file: UploadFile
):
return {"filename": file.filename}
Understanding UploadFile
UploadFile gives you these superpowers:
file.filename→ Name of the filefile.content_type→ Type (image/png, etc.)await file.read()→ Get file contents
Reading and Saving Files
@app.post("/upload")
async def upload_file(file: UploadFile):
contents = await file.read()
# Save to disk
with open(f"uploads/{file.filename}", "wb") as f:
f.write(contents)
return {"saved": file.filename}
Think of it like:
- Someone hands you an envelope (file)
- You open it (
await file.read()) - You put it in your filing cabinet (save to disk)
3. Multiple File Uploads
Uploading Many Files at Once
What if someone wants to upload 5 photos? Easy!
from fastapi import FastAPI, UploadFile
from typing import List
app = FastAPI()
@app.post("/upload-many")
async def upload_many(
files: List[UploadFile]
):
filenames = []
for file in files:
filenames.append(file.filename)
return {"uploaded": filenames}
Processing Each File
@app.post("/upload-many")
async def upload_many(files: List[UploadFile]):
results = []
for file in files:
contents = await file.read()
size = len(contents)
results.append({
"name": file.filename,
"size": size
})
return {"files": results}
graph TD A[User Selects 3 Files] --> B[Browser Sends All] B --> C[FastAPI Gets List] C --> D[Loop Through Each] D --> E[Process One by One]
4. Working with Cookies
What are Cookies?
Cookies are tiny notes your browser remembers. Like when a shop gives you a stamp card - next time you visit, you show the card!
Reading Cookies
from fastapi import FastAPI, Cookie
app = FastAPI()
@app.get("/me")
async def get_user(
user_id: str = Cookie(default=None)
):
if user_id:
return {"user": user_id}
return {"message": "No cookie found"}
How Cookies Work
graph TD A[First Visit] --> B[Server Sets Cookie] B --> C[Browser Saves It] C --> D[Next Visit] D --> E[Browser Sends Cookie] E --> F[Server Reads It]
Optional vs Required Cookies
# Optional cookie (might not exist)
session: str = Cookie(default=None)
# Required cookie (must exist)
session: str = Cookie()
5. Working with Headers
What are Headers?
Headers are like the envelope of a letter. Before you even open the letter, the envelope tells you who sent it, when, and how to handle it.
Reading Headers
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/info")
async def get_info(
user_agent: str = Header(default=None)
):
return {"browser": user_agent}
Common Headers You’ll Use
| Header | What It Tells You |
|---|---|
user-agent |
What browser/device |
authorization |
Who they are (login token) |
content-type |
Format of data sent |
accept |
What format they want back |
Custom Headers
@app.get("/secure")
async def secure_route(
x_token: str = Header()
):
if x_token != "secret123":
return {"error": "Bad token"}
return {"message": "Welcome!"}
Note: Header names with hyphens (like x-token) become underscores in Python (x_token).
Combining Everything! 🎯
Real apps use ALL of these together:
from fastapi import (
FastAPI, Form, UploadFile,
Cookie, Header
)
app = FastAPI()
@app.post("/complete")
async def complete_request(
# Form data
title: str = Form(),
# File upload
image: UploadFile = None,
# Cookie
session: str = Cookie(default=None),
# Header
user_agent: str = Header(default=None)
):
return {
"title": title,
"has_image": image is not None,
"logged_in": session is not None,
"browser": user_agent
}
Quick Reference Table
| What | Import | How to Use |
|---|---|---|
| Form Data | Form |
name: str = Form() |
| Single File | UploadFile |
file: UploadFile |
| Many Files | List[UploadFile] |
files: List[UploadFile] |
| Cookies | Cookie |
token: str = Cookie() |
| Headers | Header |
agent: str = Header() |
The Journey Complete! 🚀
You’ve just learned how FastAPI handles:
âś… Form Data - Like reading filled paper forms âś… File Uploads - Like receiving photos and documents âś… Multiple Files - Like handling a whole folder at once âś… Cookies - Like reading loyalty stamp cards âś… Headers - Like reading the envelope before opening
FastAPI makes all of this feel natural. Each piece of data has its own special helper (Form, UploadFile, Cookie, Header), and they all work together beautifully.
You’re now ready to build real-world APIs that handle anything users throw at them!