How to define schemas for FastAPI when a image is involved

I know the concept of schemas and how we use it when designing FastAPI applications. For example
from pydantic import BaseModel
class User(BaseModel):
username: str
email: str
age: int
is_active: bool = True
from fastapi import FastAPI
from typing import List
app = FastAPI()
@app.post("/users/")
def create_user(user: User):
return {"message": f"User {user.username} created"}
which is fine. My question is how (or do we do it at all?) does this work when we want to include an image in the request? I know that we could convert the image to a base64 string and pass it like any other string, but what about when we want to pass it like a binary?
To be clear I am not asking how to pass an image in the request (although I would be happy if any answer includes that) but how the concept of "Schema" applies in this case. And if not, how do we design the I/F for this case
Answer
FastAPI’s use of Pydantic models is primarily for structured JSON data. When you’re handling binary files like images, they don’t fit into that schema model because JSON can’t naturally represent binary data. Instead, FastAPI provides dedicated tools for file uploads using the File
and UploadFile
types.
Below are some key points:
Separation of Concerns
Pydantic Schemas for JSON:
Pydantic models are ideal for validating and serializing structured data (e.g., strings, integers, booleans) sent as JSON. They work well when the client sends data in theapplication/json
format.Binary Files with UploadFile/File:
Binary data, such as images, isn’t JSON serializable. For these, FastAPI uses theUploadFile
type (along with the helper functionFile()
) to accept file uploads. This tells FastAPI to expect multipart form data, where files and form fields can be sent together.
Designing the Interface
When you need both a JSON payload and a binary file in the same request, you typically have two options:
Separate Endpoints:
Use one endpoint for JSON data and another for file uploads. This keeps concerns separate, but may not be ideal if you need both in one operation.Combined Multipart Request:
Send the JSON data as form fields along with the file upload. This means you won’t get the automatic parsing of a Pydantic model from a JSON body. Instead, you extract individual fields from the form data and then create a Pydantic model instance manually if needed.
Example: Combined Multipart Request
from fastapi import FastAPI, UploadFile, File, Form
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
username: str
email: str
age: int
@app.post("/users/")
async def create_user(
username: str = Form(...),
email: str = Form(...),
age: int = Form(...),
image: UploadFile = File(...),
):
# Construct the Pydantic model from form data.
user = User(username=username, email=email, age=age)
# Process the file (e.g., save it or manipulate it)
image_content = await image.read()
# For illustration, we just return the filename and user info.
return {
"message": f"User {user.username} created",
"filename": image.filename,
"content_size": len(image_content)
}
What This Means for Schemas
JSON Data:
The Pydantic model (like yourUser
class) continues to serve its purpose for the structured parts of your request.Binary Data:
The binary file is handled separately viaUploadFile
andFile()
. This is not part of the JSON schema but is instead managed by FastAPI’s file upload mechanisms.Base64 Alternative:
Although you could encode a binary file as a base64 string and include it in a JSON body, this is generally not recommended because it increases payload size and requires additional decoding logic on both client and server. It’s better to use file upload techniques designed for binary data.
Conclusion
The concept of a schema in FastAPI is best reserved for structured JSON data. For binary files like images, you’ll typically design your interface using FastAPI’s file upload features. This way, you maintain a clean separation: using Pydantic for data validation of JSON fields, and dedicated parameters (with File
/UploadFile
) for binary content.
Enjoyed this article?
Check out more content on our blog or follow us on social media.
Browse more articles