Understanding FastAPI: The Basics
This post lives in:
I’ve been working with FastAPI for a while now, and I decided to start digging a little deeper on it’s internals.
Let’s start from the beginning:
What is FastAPI?
As the docs says:
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
So here we have a good idea about what is FastAPI: A web framework for building APIs.
But it’s not a framework built from scratch, it’s a framework that was built on top of another framework: Starlette.
And what is Starlette?
If we go to starlette’s website, we’ll find that:
Starlette is a lightweight ASGI framework/toolkit, which is ideal for building async web services in Python.
And what is ASGI?
ASGI, or Asynchronous Server Gateway Interface is a specification that proposes an interface between web servers and python applications.
When we are running our fastapi application, we’re using an ASGI server that will forward the request to our app.
Some of the most well-known asgi servers are:
Let’s recap
FastAPI is a modern webframework written in python that is built on top Starlette, which in turn is a lightweight ASGI framework that needs an ASGI server to run.
Let’s create a basic ASGI application
So if we want to understand how FastAPI works, we must start from the beginning: a simple asgi application:
async def app(scope, receive, send):
await send({
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
],
})
await send({
"type": "http.response.body",
"body": b"Hello, World!",
})
Can you imagine that all features that FastAPI offers you, all middlewares and error handling, OpenAPI docs, etc., starts from this?
That’s how ASGI is specified: a single asynchronous callable that takes a dict and two asynchronous callables as parameters.
And it works, without any webframework installed! Now let’s check if it’s really working:
First, install an ASGI server:
pip install uvicorn
And create a python file called app with the code above.
Let’s run it and see if it’s working:
uvicorn app:app
Now enter http://localhost:8000
in your browser:
INFO: Started server process [4808]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:64045 - "GET / HTTP/1.1" 200 OK
Let’s change to hypercorn to see if we are not tied to uvicorn somehow:
pip install hypercorn
And we can see it still works:
hypercorn app:app
[2024-06-22 19:00:55 -0300] [13008] [WARNING] ASGI Framework Lifespan error, continuing without Lifespan support
[2024-06-22 19:00:55 -0300] [13008] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
Creating the simplest FastAPI clone ever
Now that we know what’s beneath FastAPI, we can start playing a little bit and create the smallest, simpler ASGI framework ever:
class SimplestFrameworkEver:
async def __call__(self, scope, receive, send):
await send({
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
],
})
await send({
"type": "http.response.body",
"body": b"Hello, World!",
})
app = SimplestFrameworkEver()
And you can still run it like before, with:
hypercorn app:app
or
uvicorn app:app
Next steps
Now that we can understand the basics on which FastAPI is made, we can move on to see:
- how Starllete works
- how FastAPI extends Starllete
Stay tuned for the next posts ;)