[Python] Developing an API with FastAPI

Hey, everyone! This is the first post of the series "API frameworks for Python". We are starting with the framework FastAPI. FastAPI is fast and really easy to use. Let's build a simple API to exemplify its use.

1. Install the library in your environment

pip install fastapi

We are going to need an ASGI to run it:

pip install uvicorn[standard]

2. Create the first endpoint

main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello_world():
    return {"message": "Hello world"}

3. Start the ASGI server

project-root$ uvicorn main:app --reload

If everything goes well, you can now access your app on http://localhost:8000 and see our hello world message. You can access its documentation (swagger) on http://localhost:8000/docs

You have a second option of documentation and you can access it on http://localhost:8000/redoc

FastAPI gets a bonus point for having Swagger and ReDoc configured by default.

4. Create the other verbs

We are going to create, update, find and list our resource. Another good thing about FastAPI is that it supports type hints, unlike Flask, that we have to write schemas for validation. So the model we are using here is going to be written with type hints and with Pydantic to perform the data validation.

model.py

from pydantic import BaseModel
from typing import Optional


class School(BaseModel):
    id: Optional[int]
    name: str

4.1 Passing the request body

We pass the request body as argument to the function, for example:

@app.post('/schools')
def save(school: School):
    # method implementation
    school.id = 1
    return school

Here we receive a school object.

Let's test this example:

curl -X POST -H "Content-Type: application/json" \
    -d '{"name": "new school"}' \
    http://localhost:8000/schools

>> {"id":1,"name":"new school"}

If we don't pass a required parameter or if we pass it as the wrong type, we receive a validation message:

curl -X POST -H "Content-Type: application/json"  \
-d '{"namef": "new school"}' \
http://localhost:8000/schools

>> {"detail":[{"loc":["body","name"],"msg":"field required","type":"value_error.missing"}]}r

4.2 Passing path params

We pass the path params between {} in the url and we pass it as an argument to our function using the same name:

@app.get('/schools/{school_id}')
def find_by_id(school_id: int):
    # method implementation
    return {}

4.3 Passing query params `

We denote the query param by using the keyword Query. The first parameter is the parameter's standard value.

@app.get('/schools')
def search(name: Optional[str] = Query('standard value', max_length=50)):
    # method implementation
    return {}

4.4 Full example

Here is how our full class would be like:

main.py

from fastapi import FastAPI, Query
from model import School
from typing import Optional


app = FastAPI()


@app.post('/schools')
def save(school: School):
    # method implementation
    school.id = 1
    return school


@app.get('/schools')
def search(name: Optional[str] = Query(None, max_length=50)):
    # method implementation
    return {}


@app.get('/schools/{school_id}')
def find_by_id(school_id: int):
    # method implementation
    return {}


@app.patch('/schools/{school_id}/name')
def partial_update(name: str, school_id: int):
    # method implementation
    return {"message": "School updated"}


@app.put('/schools/{school_id}')
def update(school: School, school_id: int):
    # method implementation
    return {"message": "School updated"}


@app.delete("/schools/{school_id}")
def delete(school_id: int):
    # method implementation
    return {"message": "School deleted"}

5. Conclusion

FastAPI is very easy to get running and it comes with a lot of useful things by default, like built-in documentation. It has issues like any other framework, but it is being updated and improved, so it seems like a good choice if you need to write an API in Python.