FastAPI, a modern and fast web framework for building APIs with Python, is becoming increasingly popular. One of its strengths is to validate request parameters. However, how the equal sign (=) is used during validation also causes many programmers to be confused. In this blog, we explain how FastAPI uses equal sign (=) in function parameters and alternative ways to avoid it.

1. Traditional Python Equal Sign (=)

In vanilla Python, the equal sign (=) in function parameters assigns a default value. For instance:

def greet(name:str = "World"):
    return f"Hello, {name}!"

If you don’t provide value for the parameter – name, it defaults to “World”.

2. FastAPI’s Equal Sign (=) Twist

2.1 Example 1 – parameter: int = Path()

In FastAPI, however, this familiar symbol takes on a new role. Consider this example:

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int = Path(..., title="Item ID", ge=0)):
    return {"item_id": item_id}

Here, the equal sign (=) doesn’t assign a default value to item_id. Instead, it’s used to connect the item_id parameter with additional validation and metadata provided by FastAPI’s Path function.

What’s Path()?

We’ll need to take a deep look at the FastAPI source code. Path() is a function defined in .\fastapi\param_functions.py. It returns a Path() class defined in .\fastapi\params.py. The class Path() eventually inherits from the class Representation() defined in the Pydantic library.

class diagram of Path
Class Diagram of Path()

What’s with the … (Ellipsis)?

The ... (called an ellipsis) indicates that the parameter is required. By pairing it with Path(), you’re saying: “This parameter is mandatory; when provided, it should adhere to the constraints defined in Path().”

In the above example, item_id is required (...), must be an integer (due to the type hint int), and should be greater than or equal to 0 (ge=0).

What is Path() Returning Value?

Not in the way you might think. The function Path(), and other similar functions in FastAPI like File(), are not “returning” values for the parameters directly. Instead, they return special internal objects, a class also called Path(), that FastAPI uses to parse and validate the parameter.

2.2 Example 2 – file: bytes = File()

Even for more complex operations like file uploads, it provides a seamless integration using the same principle. Let’s explore UploadFile.

from fastapi import FastAPI, UploadFile, File

app = FastAPI()

@app.post("/upload/") 
async def upload_file(file: bytes = File()):    
    return {"filename": file.filename}

In the above snippet, we define an endpoint for file uploads. Let’s break it down:

  • Bytes is a type hint. It tells FastAPI that the incoming parameter – file’s type should be bytes.
  • File() tells FastAPI to expect a file from the client’s request. It is to be read as bytes, and treated as a file.

3. The Ugly Practice and its Remedy

In my opinion, this equal sign (=) twist is so ugly and not intuitive. It should not have been designed like this in the first place. There are a few ways to not use the equal sign.

3.1 Use Annotated

After Python 3.7, typing extensions offers Annotated. It can achieve exactly what we discussed using the equal sign (=), but in a more intuitive way that is easier to understand.

from fastapi import FastAPI, File
from typing_extensions import Annotated

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]): 
    return {"file_size": len(file)}

3.2 Use a Feature-rich Type

UploadFile is a class defined in FastAPI. It eliminates the need to use File() to annotate the parameter, provides the file’s metadata, and utilizes a ‘spooled’ file. Please refer to the FastAPI official document for more details.

from fastapi import FastAPI, UploadFile

app = FastAPI()

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

4. Recap

FastAPI has repurposed the equal sign (=) in function parameters:

  • In traditional Python, it assigns default values.
  • In FastAPI, it links parameters to validation, parsing, and documentation utilities, such as File(), Path() , etc.

Although this approach allows developers to leverage the familiar Python syntax while benefiting from FastAPI’s powerful features, it confuses many programmers. When you write new code, we shall avoid using it as much as possible.

Feel free to add a comment below. You may find more blogs about Python on our website.

[Credit: Image by rawpixel.com on Freepik]

Leave a Reply

Your email address will not be published. Required fields are marked *