FastAPI is a modern async framework for Python. According to Python developers survey 2020, FastAPI is the 3rd most popular web framework for python.
What makes FastAPI so popular?
- Async
- Fast
- Easy to Code and fast learning curve.
- Data validation via Pydantic
- Automatic docs
Take a look at all the FastAPI features.
Pydantic is a python library for data parsing and validation using Python type hints. It is the fastest python data validation library.
Pydantic for FastAPI
1. BaseSettings for reading environment variables
This requires python-dotenv
installed.
pip install python-dotenv
Dummy .env
file:
DB_NAME=postgres
DB_PASS=postgres
DB_URL=postgresql://postgres:postgres@127.0.0.1:5432/postgres
DEBUG=True
SEED=42
# env.py
from pydantic import BaseSettings, PostgresDsn
class Settings(BaseSettings):
db_name: str
db_pass: str
db_url: PostgresDsn
debug: bool
seed: int
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()
print(settings)
Output:
db_name='postgres' db_pass='postgres' db_url=PostgresDsn('postgresql://postgres:postgres@127.0.0.1:5432/postgres', scheme='postgresql', user='postgres', password='postgres', host='127.0.0.1', host_type='ipv4', port='5432', path='/postgres') debug=True seed=42
- The environment variables are automatically loaded by name.
- They are converted to the types specified by type hints.
By default, all variables are case-insensitive.
You can also use an alias for loading env values. For example, your env variable is DATABASE_URL
, but you need to load it as db_url
.
from pydantic import BaseSettings, Field, PostgresDsn
class Settings(BaseSettings):
db_url: PostgresDsn = Field(..., env="DATABASE_URL")
Reading the env
file is only required if the values are not in the system environment. This process is costly, especially when read for each request. So cache the values using lru_cache
.
from functools import lru_cache
# other imports
class Settings(BaseSettings):
# All fields, yadayadayada
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
@lru_cache
def get_settings() -> Settings:
return Settings()
settings = get_settings()
Every time the get_settings
is called, the cached values are returned.
2. Validators for custom data validation
Validators are applied to BaseModel to perform custom validation.
Basic BaseModel + FastAPI
usage:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
email: str # use EmailStr from pydantic
username: str
password: str
@app.post("/")
async def register(user: User):
return user
Test it out using httpie:
➜ http POST 127.0.0.1:8000 email=test@test.com username=amal password=amal
HTTP/1.1 200 OK
content-length: 61
content-type: application/json
date: Wed, 19 May 2021 12:20:06 GMT
server: uvicorn
{
"email": "test@test.com",
"password": "amal",
"username": "amal"
}
Let's use @validator
from pydantic to perform some operation on the BaseModel before the view, register
, receives it.
from pydantic import BaseModel, validator
class User(BaseModel):
email: str # use EmailStr from pydantic
username: str
password: str
@validator("password")
def hash_the_password(cls, password: str):
return f"Fast-Insecure-Hash-{password}"
➜ http POST 127.0.0.1:8000 email=test@test.com username=amal password=amal
HTTP/1.1 200 OK
content-length: 80
content-type: application/json
date: Wed, 19 May 2021 12:39:06 GMT
server: uvicorn
{
"email": "test@test.com",
"password": "Fast-Insecure-Hash-amal",
"username": "amal"
}
You can also validate a particular field with the rest of the fields.
@validator("password")
def hash_the_password(cls, password, values, **kwargs):
print(values.keys())
return password
This prints dict_keys(['email', 'username'])
to stdout. The pydantic fields are validated in sequence, and the values
dict carries the already validated fields.
In this case, since we are validating the password
field, all the above fields are available to use.
You can use Root Validator to use the entire model's data. By default, the root validator gets data after all the fields are validated(i.e., the default validation).
@root_validator
def test_root_validator(cls, values):
print(values.keys())
return values
Output:
dict_keys(['email', 'username', 'password'])
3. schema_extra for providing examples in docs
With the previous example running, if you navigate to http://127.0.0.1:8000/docs, you could see the interactive docs by FastAPI.
Instead of string
for all values, let's write some custom data for the examples.
class User(BaseModel):
email: str # use EmailStr from pydantic
username: str
password: str
class Config:
schema_extra = {
"example": {
"email": "test@test.com",
"username": "amal",
"password": "amal",
}
}
You can also use fake data libraries like Faker to generate random data.
Setting the example data can also be achieved like so:
class User(BaseModel):
email: str = Field(..., example="test@test.com")
username: str = Field(..., example="amal")
password: str = Field(..., example="amal")
Conclusion
Pydantic is an amazing tool for data validation. FastAPI uses pydantic to help build better APIs. For more amazing features of pydantic, read the official documentation.
Top comments (1)
Nice ...