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

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


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.

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

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

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

which needs to be interpreted as JSON on the client side


A random generator (exercise)

in python/random-generator/


Exercise 1 - the docs


Exercise 2 - from the browser


Exercise 3 - from the terminal


Exercise 4 - error handling


Exercise solutions

Solution to Exercise 2

in the browser, you need to type the full URL, i.e.

1
http://localhost:8000/api/integer?min=100&max=200&count=10
Solution to Exercise 3
  • first naive approach:
    with http, you can use the same URL as in the browser
    but you need to quote it because of the & characters, like this:

    1
    2
    3
    4
    # long version - watch out for quotes!
    # you can do this, but it's a little awkward
    # because & is a special character in bash
    http "http://localhost:8000/api/integer?min=100&max=200&count=10"
  • now, http makes it a little simpler / less awkward
    for one thing, you can skip the localhost part and just use :8000
    and also, you can pass parameters directly as arguments to http, like this:

    1
    2
    3
    # short version, to pass parameters with GET
    # however you MUST use ==
    http :8000/api/integer min==100 max==200 count==10
  • NOTE using just = would NOT WORK

    1
    2
    3
    4
    # WARNING: the simple = is for POST requests!
    # if we use = it doesn't do what we want!
    # DON'T DO IT LIKE THIS!
    # http GET :8000/api/integer min=100 max=200 count=10
Solution to Exercise 4

as it stands, there is no control on the parameters
so the server calls the random.uniform function with invalid parameters, and that generates a 500 error

to address this, several choices are possible:

  • either we add a check in the some_random_floats function to verify that min < max and if not we raise an HTTP 400 (Bad Request) exception

  • or we use Pydantic’s validation features; but for now that’s premature since we haven’t seen Pydantic yet 😉

so for now we’ll settle for the 1st solution

1
2
3
4
5
6
from fastapi import HTTPException

def some_random_floats(min: float, max: float) -> float:
    if min >= max:
        raise HTTPException(status_code=400, detail="Invalid range")
    return random.uniform(min, max)

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...