jypi
  • Explore
ChatWays to LearnMind mapAbout

jypi

  • About Us
  • Our Mission
  • Team
  • Careers

Resources

  • Ways to Learn
  • Mind map
  • Blog
  • Help Center
  • Community Guidelines
  • Contributor Guide

Legal

  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Content Policy

Connect

  • Twitter
  • Discord
  • Instagram
  • Contact Us
jypi

© 2026 jypi. All rights reserved.

Fast API
Chapters

1Introduction to FastAPI

2Routing and Endpoints

3Request and Response Handling

Data Validation with PydanticResponse ModelsForm Data and FilesHandling JSONCustom Request HeadersResponse Status CodesError HandlingResponse ClassesStreaming ResponsesCookies and Sessions

4Dependency Injection

5Security and Authentication

6Database Integration

7Testing FastAPI Applications

8Asynchronous Programming

9Deployment Strategies

10Real-world Applications and Projects

Courses/Fast API/Request and Response Handling

Request and Response Handling

9753 views

Explore how FastAPI handles requests and responses, including data validation and serialization.

Content

3 of 10

Form Data and Files

Form & Files: The No-BS Guide
624 views
intermediate
humorous
science
gpt-5-mini
624 views

Versions:

Form & Files: The No-BS Guide

Watch & Learn

AI-discovered learning video

Sign in to watch the learning video for this topic.

Sign inSign up free

Start learning for free

Sign up to save progress, unlock study materials, and track your learning.

  • Bookmark content and pick up later
  • AI-generated study materials
  • Flashcards, timelines, and more
  • Progress tracking and certificates

Free to join · No credit card required

Form Data and Files — Because APIs Also Need to Accept Your Awful Selfie

"You can't send a file with JSON. Stop trying. Use multipart/form-data like a responsible developer." — Probably your future self

You're already comfortable creating routes and endpoints (remember that smooth Routing and Endpoints session?). You've validated JSON payloads with Pydantic and returned clean Response Models. Now it's time for the other half of real-world HTTP: form data and file uploads. This is where FastAPI stops pretending everything is JSON and starts dealing with the messy, human world of forms, photos, and PDFs named resume_final_v3_reallyfinal.pdf.


Why this matters (and when you'll cry without it)

  • HTML forms submit as application/x-www-form-urlencoded or multipart/form-data (the latter when files are involved). Browsers and many clients use these; if your endpoint expects JSON, browsers will angrily give you 415 Unsupported Media Type.
  • Uploading images, CSVs, or letting users fill a quick form is extremely common. You need to accept fields and files reliably and safely.

This topic builds on: Data Validation with Pydantic (useful for validating form fields) and Response Models (you'll still want to return structured data). But forms and files behave a bit differently, so read on.


The basics: Form() and File() — the dynamic duo

FastAPI provides two special dependency helpers: Form (for form fields) and File together with UploadFile (for files). Use them in your endpoint signatures like you'd use Query() or Body().

Simple form fields

from fastapi import FastAPI, Form

app = FastAPI()

@app.post('/login')
async def login(username: str = Form(...), password: str = Form(...)):
    return {"username": username}
  • This expects either x-www-form-urlencoded or multipart/form-data (no file needed).
  • Note: You can't pass a Pydantic model here directly from a form body — more on that below.

Single file upload

from fastapi import File, UploadFile

@app.post('/upload-file')
async def upload_file(file: UploadFile = File(...)):
    contents = await file.read()  # bytes — careful with big files
    return {"filename": file.filename, "content_type": file.content_type}
  • Use UploadFile for async-friendly file handling. It exposes .filename, .content_type, and an async .read().
  • For small files, .read() is fine. For large files, stream to disk.

Multiple files

from typing import List

@app.post('/upload-multiple')
async def multi_upload(files: List[UploadFile] = File(...)):
    return {"filenames": [f.filename for f in files]}

Combining form fields with files (the classic resume upload)

@app.post('/submit')
async def submit(name: str = Form(...), age: int = Form(...), resume: UploadFile = File(...)):
    # validate age with Pydantic later or here
    # store resume
    return {"name": name, "age": age, "resume_filename": resume.filename}

Client must send multipart/form-data for this to work.


Pydantic + Forms: a little glue job

Pydantic models don't automatically read from form fields. But you can convert form fields into a Pydantic model via a dependency function.

from pydantic import BaseModel
from fastapi import Depends

class User(BaseModel):
    name: str
    age: int

async def user_form(name: str = Form(...), age: int = Form(...)) -> User:
    return User(name=name, age=age)

@app.post('/users/')
async def create_user(user: User = Depends(user_form)):
    return user

This keeps validation benefits of Pydantic while accepting form-encoded data.


Saving files safely + streaming large uploads

  • Don't trust client filenames. A file named "../../../etc/passwd" is not a cute joke. Sanitize or generate server-side names.
  • For big files, avoid reading entire content into memory.

Example streaming to disk (synchronous copy):

import shutil
from pathlib import Path

UPLOAD_DIR = Path('/tmp/uploads')
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

@app.post('/upload-stream')
async def upload_stream(file: UploadFile = File(...)):
    dest = UPLOAD_DIR / file.filename  # sanitize in production
    with dest.open('wb') as buffer:
        shutil.copyfileobj(file.file, buffer)
    return {"filename": file.filename}

Note: file.file is a real file-like object which may be a SpooledTemporaryFile — good for streaming.


Security checklist (because you will be hacked if you don't)

  • Validate file types (check content_type AND inspect bytes/signatures if critical).
  • Enforce size limits (proxy or webserver-level, and also check file size in app if needed).
  • Sanitize filenames or generate UUIDs for storage.
  • Store uploads outside web root or serve them using secure mechanisms (like presigned URLs).

Responses: returning files to clients

You can return files using FileResponse or StreamingResponse.

from fastapi.responses import FileResponse

@app.get('/download/{filename}')
def download(filename: str):
    path = UPLOAD_DIR / filename
    return FileResponse(path, media_type='application/octet-stream', filename=filename)

You can't use response_model for raw file responses — response_model is for structured JSON-like responses.


Quick testing cheatsheet

  • curl upload file:
    curl -F "file=@/path/to/file.jpg" http://localhost:8000/upload-file
  • curl form + file:
    curl -F "name=Alice" -F "age=30" -F "resume=@resume.pdf" http://localhost:8000/submit
  • Simple HTML form to test in-browser:
<form action="/submit" method="post" enctype="multipart/form-data">
  <input name="name">
  <input name="age" type="number">
  <input type="file" name="resume">
  <button type="submit">Send</button>
</form>

Common pitfalls & FAQs

  • Q: "Why is my Pydantic model not populating from Form?"
    • A: Forms are not JSON. Use a dependency that accepts Form() fields and returns a Pydantic model.
  • Q: "Should I use bytes or UploadFile?"
    • A: Use UploadFile for non-trivial file handling (async-friendly, streams). Use bytes (.read()) for tiny quick stuff.
  • Q: "Can I validate file size/type with Pydantic?"
    • A: Not directly. Validate manually in the endpoint or wrapper dependency.

TL;DR — Cheat-sheet Takeaways

  • Use Form(...) for regular form fields; File(...)/UploadFile for files.
  • Multipart/form-data is required when files are present; otherwise x-www-form-urlencoded is used for simple forms.
  • For Pydantic validation, wrap form fields in a dependency that constructs a model.
  • Stream to disk for large files; don't .read() megabytes into memory.
  • Secure filenames, validate types, and set size limits.

Final thought: FastAPI treats forms and files like first-class citizens — but you still have to be a responsible human. Treat files like wild animals: don't let them roam uncontrolled, and definitely don't name them resume_final_v3.pdf.

Build on your routing knowledge by adding endpoints that take forms and files, then wire Pydantic validation via dependencies. You're now ready to accept that user's headshot, tax form, and heartfelt message — all in one multipart/form-data bouquet.

Flashcards
Mind Map
Speed Challenge

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!

Ready to practice?

Sign up now to study with flashcards, practice questions, and more — and track your progress on this topic.

Study with flashcards, timelines, and more
Earn certificates for completed courses
Bookmark content for later reference
Track your progress across all topics