Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.


FastAPI

Python micro-framework 🐍 - fairly recent (2018)

occupies the same space as

🚧 Micro-framework doesn’t mean not usable on large projects ⚠️


FastAPI vs Flask

FastAPI is similar on the surface to Flask, but much more modern!


Why FastAPI (and not something else)

1️⃣ You all more or less know how to do Python 🐍
so we eliminate everything that’s not Python-based

2️⃣ We’ll try to teach you things used elsewhere
And the FastAPI trend indeed seems to be experiencing spectacular growth!


Graphical User Interface

But actually... why are we interested in this?
The GUI (pronounce gooey) is what bridges 🌉 between:

a calculation/data processing code/...
and a user interface


GUI - two approaches

Old school
Using graphical libraries and developing a “thick” client

New age

Using the browser


FastAPI: we already know a bit

We already vaguely know how to use it, remember, we’ve already seen

Notice how simple it is to get started 😯
this is *a pro of Flask/FastAPI over Django (which requires a more advanced setup)


Quick recap

  • Step 1️⃣:

from fastapi import FastAPI
  • Step 2️⃣

app = FastAPI()

Then we attach Python functions to URL paths
we call these functions route handlers or router functions

@app.get("/a/path/target")
def the_corresponding_function():
  // does very smart things
  return a_result    # which can be data (json) or html or whatever...

And to start the server?

From the terminal

fastapi dev my_app.py

the server in development mode

fastapi dev my_app.py --port 8080

or on another port

or also

fastapi run my_app.py

in production mode


Parameters in a GET

For GET requests, we can write slightly more sophisticated URLs:

Of course then we need to retrieve the arguments in the handler function 🤔
FastAPI has it all figured out

@app.get("/some/route/data")
def get_parameters(
        name: str,
        age: int):
    return {'name': name, 'age': age}

you just need to declare the parameters
with their type
and FastAPI does the rest
and even type conversion

🚧 No notion of type in network exchanges, everything is a string 🚧


Parametric URL

Now you can also - and more in line with best practices - define parameters within a URL itself
e.g. then your users would call URLs like /my/route/basile/42 instead

and of course you can also receive multiple parameters this way

@app.get("/my/route/{name}/{age}")
def url_parameter(name: str, age: int):
    return ...

Note: Special case for /

  • by default a parameter does not contain a slash /

  • but in a route you can declare
    @app.get"/my/route/{parameter:path}"

    to allow slashes / in the parameter itself


More kinds of parameters

in all generality, parameters can be passed in 5 different ways in an HTTP request:

  1. in the URL path
    (e.g. /my/route/basile/42)

  2. in the URL query string
    (e.g. /my/route?name=basile&age=42)

  3. in the request body (e.g. for POST requests)

  4. in the request headers

  5. in cookies

here’s an example of a request with parameters in all these different places, freely inpired from the pixels-war game


Capturing parameters in the FastAPI route handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI, Header, Cookie, Query, Body
from pydantic import BaseModel

app = FastAPI()

class PixelPayload(BaseModel):
    i: int
    j: int
    color: str  # e.g. "#ff0000"

@app.post("/api/{map}/set")
async def set_pixel(
    map: str,
    defer: bool = Query(False),
    authorization: str = Header(...),
    signature: str = Cookie(...),
    payload: PixelPayload = Body(...),
):
    ...

with this code, inside the router function, you can access the parameters as follows:

Python code

value

map

themap

defer

True

authorization

Bearer ...

signature

f4d93b...

payload.i

3


Returning data

remember: everything is text over the network
by default, FastAPI returns data in JSON format

this means that when you say e.g.

1
2
3
4
@app.get("/my/route/{name}/{age}"):
def url_parameter(name: str, age: int):
    return {'name': name,
            'age': age}

what is sent back to the client will be the text:

{
  "name": "basile",
  "age": 42
}

so remember to interpret this text as JSON on the client side


→ A random generator (exercise)


Exercise 1 - the docs


Exercise 2 - from the browser


Exercise 3 - from the terminal


Exercise 4 - error handling


Exercise solutions


HTTP verbs

Quick reminder from the 1st episode, HTTP different possible requests

These are the main types of requests but there are others, for the complete list you can check here: Hypertext Transfer Protocol


Parameters in a POST

however for POST, PATCH, DELETE requests, ...
the parameters are passed in the body of the request

Let’s look at an example


POST requests

And to start let’s look at what is sent by httpie when we do a POST

As we can see, the parameters are sent


→ On the FastAPI side

Here now is one FastAPI code that works well to handle this request

1
2
3
4
5
6
7
from fastapi import Body

@app.post("/api/seed")
# with Body() we indicate that the parameter comes from the request body
def set_seed(seed_value: int=Body(..., embed=True)):
    random.seed(seed_value)
    return {"message": f"Seed set to {seed_value}"}

What’s next

At this point you know how to implement FastAPI endpoints that handle GET and POST requests with parameters

We have many other things to see, including:

We’ll see that in the following episodes...