APIModel

Source module: fastapi_utils.api_model


One of the most common nuisances when developing a python web API is that python style typically involves snake_case attributes, whereas typical JSON style is to use camelCase field names.

Fortunately, pydantic has built-in functionality to make it easy to have snake_case names for BaseModel attributes, and use snake_case attribute names when initializing model instances in your own code, but accept camelCase attributes from external requests.

Another BaseModel config setting commonly used with FastAPI is orm_mode, which allows your models to be read directly from ORM objects (such as those used by SQLAlchemy).

You can use fastapi_utils.api_model.APIModel to easily enable all of these frequently desirable settings.

Create a model

To make use of APIModel, just use it instead of pydantic.BaseModel as the base class of your pydantic models:

from dataclasses import dataclass
from typing import NewType
from uuid import UUID

from fastapi import FastAPI

from fastapi_utils.api_model import APIModel

UserID = NewType("UserID", UUID)


class User(APIModel):
    user_id: UserID
    email_address: str


@dataclass
class UserORM:
    """
    You can pretend this class is a SQLAlchemy model
    """

    user_id: UserID
    email_address: str


app = FastAPI()


@app.post("/users", response_model=User)
async def create_user(user: User) -> UserORM:
    return UserORM(user.user_id, user.email_address)

Info

You can use typing.NewType as above to (statically) ensure that you don’t accidentally misuse an ID associated with one type of resource somewhere that an ID of another type of resource is expected.

This is useful since it can be difficult, for example, to immediately recognize that you’ve passed a user ID where a product ID was supposed to go just by looking at its value. Using typing.NewType ensures mypy can check this for you.

For a more detailed explanation and example, see this GitHub issue comment

Now, you can make requests to endpoints expecting User as the body using either snake case:

{
  "user_id": "00000000-0000-0000-0000-000000000000",
  "email_address": "user@email.com"
}

or camel case:

{
  "userId": "00000000-0000-0000-0000-000000000000",
  "emailAddress": "user@email.com"
}

and both will work.

In addition, if you set the response_model argument to the endpoint decorator and return an object that can’t be converted to a dict, but has appropriately named fields, FastAPI will use pydantic’s orm_mode to automatically serialize it.

from dataclasses import dataclass
from typing import NewType
from uuid import UUID

from fastapi import FastAPI

from fastapi_utils.api_model import APIModel

UserID = NewType("UserID", UUID)


class User(APIModel):
    user_id: UserID
    email_address: str


@dataclass
class UserORM:
    """
    You can pretend this class is a SQLAlchemy model
    """

    user_id: UserID
    email_address: str


app = FastAPI()


@app.post("/users", response_model=User)
async def create_user(user: User) -> UserORM:
    return UserORM(user.user_id, user.email_address)