How to define schemas for FastAPI when a image is involved

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 the application/json format.

  • Binary Files with UploadFile/File:
    Binary data, such as images, isn’t JSON serializable. For these, FastAPI uses the UploadFile type (along with the helper function File()) 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:

  1. 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.

  2. 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 your User class) continues to serve its purpose for the structured parts of your request.

  • Binary Data:
    The binary file is handled separately via UploadFile and File(). 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