Python micro-framework 🐍 - fairly recent (2018);
occupies the same space as
Flask - developed since 2010 - lightweight and extensible
Django - developed since 2003 - perceived as more complete but also heavier
🚧 Micro-framework doesn’t mean not usable on large projects ⚠️
FastAPI¶
similar on the surface to Flask, but much more modern!
encourages a more structured approach
leverages type information (type annotations / pydantic)
especially for data validation / conversion
you can define separate models for creation, reading, updating, etc.
useful for example for password hashing (not exposed)
in particular, automatically generates interactive documentation
has native support for asynchronous programming
as well as for websockets
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 is what bridges 🌉 between:
a calculation/data processing code/...
and a graphical interface
so very relevant for the “Computer Science Projects” at the end of S2
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
how to install FastAPI
and
how to make a minimal server with FastAPI
notice how simple it is to get started 😯
this is an advantage of Flask/FastAPI compared to Django
which requires a more advanced setup to start a project
Quick recap¶
Step 1️⃣:
from fastapi import FastAPIStep 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 or html or ...And to start the server?¶
from the terminal
fastapi dev my_app.pythe server in development mode
fastapi dev my_app.py --port 8080or on another port
or also
fastapi run my_app.pyin production mode
Parameters in a GET¶
We can write slightly more sophisticated URLs:
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¶
Possibility offered by Flask to define parameters within a URL itself
Special case for /
by default a parameter does not contain a slash
/but in a route you can declare
"/my/route/{parameter:path}"to allow slashes
\in the parameter
@app.get("/my/route/{parameter}")
def url_parameter(parameter: int):
return {"square": parameter**2}and of course you can also receive multiple parameters this way
A random generator (exercise)¶
in python/random-generator.py
read the code
start the server
Random number generation API
/api/integer: generates integers/api/float: generates floats
from the browser - or the terminal with httpie - query the endpoint /api/integer
# don't hesitate to also see what it gives with the -v option
# which will ALSO show you the request sent
http :8000/api/integerExercise solutions¶
Solution to Exercise 1
in the browser:
http://localhost:8000/api/float?min=10&max=50&count=4it’s important to understand how
httpworks well
with httpie, it’s simpler:1 2 3 4 5 6 7 8 9 10 11 12# long version - watch out for quotes! # because of the & which is a special character in bash http ":8000/api/float?min=10&max=50&count=4" # short version, to pass parameters with GET # you must use == http :8000/api/float min==10 max==50 count==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/float min=10 max=50 count=4
Solution to Exercise 2
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
random_floatsfunction to verify thatmin < maxand if not we raise an HTTP 400 (Bad Request) exceptionor 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 6from fastapi import HTTPException def 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
GET: requests to obtain a resource from the server (html/css/js file, image, video, data, ...)POST: requests to send data to the server for processing (adding a user to a database, ...)PATCH: requests to partially modify a server resource (updating a user’s email address in the database)DELETE: requests to delete a server resource (deleting a comment on an article, ... )
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¶
seen above: GET parameters are in the URL
like /my/route?param1=val1¶m2=val2
and for info the HTTP protocol doesn’t provide for putting parameters in the body of a GET request, if you do it anyway the behavior is undefined
however for POST, PATCH, DELETE requests, ...
the parameters are passed in the body of the request
Let’s look at an example
the POST request¶
And to start let’s look at what is sent by httpie when we do a POST
the body of a POST request
Here it is
1 2 3 4 5 6 7 8 9 10 11 12 13❯ http -v :8000/api/seed seed_value:=42 POST /api/seed HTTP/1.1 Accept: application/json, */*;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 18 Content-Type: application/json Host: localhost:8000 User-Agent: HTTPie/3.2.4 { "seed_value": 42 }
As we can see, the parameters are sent in JSON format
in the Body of the request - i.e. after the headers
Remember this well, it’s important!
This is the process we’ll need to use when we want to send data to the server (and especially when it’s the frontend sending the request via JS)
on the FastAPI side¶
Here now is the FastAPI code that works well to handle this request
1 2 3 4 5 6 7from 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}"}
it’s simpler with Pydantic
we’ll see this later, but if we use a Pydantic model to define the parameters, it’s even simpler...
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:
how FastAPI leverages type annotations to do automatic validation
how to return HTML rather than simple data
and a few other tips & tricks
We’ll see that in the following episodes...
