This blog was last modified 554 days before.

Why using Response Model

There are several reasons that we should use Response Model patterns provided by FastAPI.

  • Validate Response Data

With Response Model set, FastAPI will automatically check and validate the function return object, ensuring you are returning info with right type to your client.

  • Filter Unwanted Data (Important to security)

FastAPI will only returns the data that match the Response Model, with allows you to control what info could be returned to the client.

class SomeInfo(BaseModel):
    not_important_data: str
    safety_key: str

class ReturnInfo(BaseModel):
    not_important_data: str

@app.get(response_model=ReturnInfo)
def return_info():
    # do sth
    return SomeInfo()

For example, in code above, safety_key will NOT appeared in the respone on client, since which didn't appeared in the respone model: ReturnInfo.

Set Response Model

There are two different way to set response model, we recommend using the second one, reason will be explained later at below.

Using Python Function Return Type Anno

The simplest way to set a Response Model is using the function return type anno grammer provided by Python like below:

@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item

In code above, Item has been set as the Response Model

Using response_model param in Router Decorator

Here is another approach:

@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]

In this case, we set Response Model in router decorator, and this also works as intend.

Why Second Approach is Recommend

Sometimes we may meet the situation that the return object is not exactly the type same as the response type.

For example, we want to respone a database object retrive from database, but for safety reason (explain in beginning of this passage), we create a custom class called ReturnUserInfo, and in which some sensitive field has been removed.

If now we used the first method to set the response type, then when we want to directly return the database object (which is not the type of ReturnUserInfo), then Python will complain that the return type is wrong. And the same issue will not appear if we use the second approach.

Tips About Creating Model Classes

We have already learn how to specify a response model class for FastAPI.

Here is some tips we may used when writing a model class for FastAPI.

Factory Constructor with Pydantic

Pydantic has its own overrided __init__() method, and it's NOT recommend to override the init method of a subclass of pydantic.BaseModel.

We could use Python @classmethod decorator to implement Factory Constructor pattern. Here is example:

class ServerPingInfo(BaseModel):
    """
    Response model of server ping api
    """

    # this could be float on some supported platform (based on time() comment)
    server_time: float

    @classmethod
    def from_request(cls, req: Request) -> "ServerPingInfo":
        info = cls(server_time=time())
        return info

In example above, we use @classmethod to create a factory constructor from_request(), which could create a ServerPingInfo class instance based on a req info.

Notice the trick we used in the return type anno of this function. We need to add the quote " of the return type, since the return type is this class itself.

More info about Python @classmethod and @staticmethod