If you have worked with FastAPI, you have probably run something like this:
uvicorn main:app
The command looks simple, but an important piece of modern Python web architecture sits behind it.
Uvicorn is not FastAPI. FastAPI is not the server either. Between them is a contract called ASGI.
ASGI stands for Asynchronous Server Gateway Interface. It is a specification that defines how a server receiving network connections communicates with a Python application.
A useful mental model is:
client
↓ HTTP / WebSocket
ASGI server
↓ ASGI
Python framework / application
↓
your code
For example:
browser
↓
Uvicorn
↓ ASGI
FastAPI
↓
Python endpoint
That small diagram explains a distinction that is easy to miss when starting with FastAPI: ASGI is neither a framework nor a server. It is the interface that lets both sides understand each other.
The problem ASGI is designed to solve
Before ASGI, the dominant standard for Python web applications was — and remains highly important — WSGI, the Web Server Gateway Interface.
WSGI was enormously useful because it decoupled servers from frameworks. A WSGI-compatible server could run a WSGI-compatible application without both products having to be designed specifically for each other.
That model fits the classic web request cycle very well:
request
↓
process
↓
response
But the web stopped being only a sequence of short requests followed by one final response.
Modern applications increasingly need things such as:
- WebSockets;
- long-lived connections;
- streaming;
- progressively delivered events;
- large amounts of concurrent I/O;
- applications that receive and send multiple events during one connection.
The ASGI specification itself explains that WSGI is inherently tied to the traditional HTTP request/response cycle, while ASGI was created to represent protocols and connections that can produce multiple events over time.
That is the most important conceptual difference.
ASGI turns a connection into events
A modern ASGI application can be reduced conceptually to an asynchronous function with this shape:
async def application(scope, receive, send):
...
Those three parameters capture the core of the protocol.
scope
scope describes the connection.
It is a dictionary containing information such as the protocol type, path, and other metadata associated with the connection.
For example:
scope["type"]
might be:
http
or:
websocket
receive
receive is an awaitable callable that lets the application receive events.
The body of an HTTP request, for example, arrives through events such as:
http.request
A WebSocket can produce events such as:
websocket.connect
websocket.receive
websocket.disconnect
send
send lets the application send events back to the server.
For an HTTP response, an application may first send:
http.response.start
and then:
http.response.body
For a WebSocket it might send:
websocket.send
This is why ASGI naturally fits bidirectional and long-lived connections better than a purely synchronous request/response model.
A minimal ASGI application
You normally do not write ASGI applications directly, but doing it once makes the architecture much easier to understand:
async def app(scope, receive, send):
assert scope["type"] == "http"
await send({
"type": "http.response.start",
"status": 200,
"headers": [(b"content-type", b"text/plain")],
})
await send({
"type": "http.response.body",
"body": b"Hello from ASGI",
})
There is no FastAPI here.
There is no Django.
There is not even a Request or Response abstraction.
There is only the ASGI contract.
In a real application, the framework translates these low-level events into a much more convenient programming model.
FastAPI is an ASGI application
With FastAPI, we normally write something like:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def home():
return {"message": "Hello"}
Then we run:
uvicorn main:app
That command roughly means:
uvicorn
imports main.py
finds the app object
treats it as an ASGI application
opens network sockets
translates network traffic into ASGI events
hands those events to FastAPI
FastAPI matches the route, runs our code, and builds the response; Uvicorn handles the network-facing server work.
ASGI is the shared boundary between them.
FastAPI’s own documentation states this directly: FastAPI is an ASGI web framework, and serving it requires an ASGI server program such as Uvicorn or an alternative such as Hypercorn.
So what exactly is Uvicorn?
Uvicorn is an ASGI server.
Its responsibility lives closer to this layer:
socket / HTTP
↓
Uvicorn
↓
ASGI
FastAPI lives on the other side:
ASGI
↓
FastAPI
↓
application logic
That separation is valuable because components can be replaced independently.
Conceptually, an ASGI application can run on another compatible server without being rewritten around one specific server implementation.
Django can speak ASGI too
ASGI is not exclusive to FastAPI.
Django supports both WSGI and ASGI. A modern Django project can expose an application object through its ASGI configuration and run it with compatible servers such as Uvicorn, Daphne, Hypercorn, or Granian.
That allows Django to participate in asynchronous architectures, but there is an important warning: running behind ASGI does not automatically make all your code non-blocking.
If an async handler calls a synchronous library that blocks the thread, the event loop cannot magically remove that blocking work.
async does not mean “faster at everything”
This is one of the most common misconceptions.
ASGI is especially useful for workloads dominated by I/O:
waiting for an API
waiting for a database
waiting for Redis
waiting for a remote file
waiting for socket messages
While one coroutine is waiting, the event loop can advance other tasks.
Imagine 1,000 connections spending much of their lifetime waiting on external systems. An asynchronous model can make very effective use of those idle periods.
But if the workload is instead:
encoding video
compressing large files
running heavy CPU inference
processing millions of numbers
the problem is different.
CPU-bound work still consumes CPU. Those workloads may require separate processes, worker pools, task queues, or other forms of parallelism.
ASGI improves the concurrency model for specific kinds of workloads; it does not repeal the laws of computation.
WSGI vs ASGI
A simplified comparison:
| Capability | WSGI | ASGI |
|---|---|---|
| Traditional HTTP | Yes | Yes |
| Synchronous model | Native | Supported |
async/await | Not as the native model | Yes |
| WebSockets | Not part of the standard | Yes |
| Long-lived connections | Awkward | Natural |
| Multiple events per connection | Not the central model | Yes |
| Typical frameworks | Classic Flask, Django WSGI | FastAPI, Starlette, Django ASGI |
That does not mean WSGI is “dead.”
For many traditional CRUD applications, a synchronous architecture remains entirely reasonable. ASGI was also designed with interoperability for existing WSGI applications in mind.
The right choice depends on the workload and architecture, not simply on which acronym is newer.
Why WebSockets make the difference obvious
A classic HTTP endpoint usually has a short lifetime:
client → request → response → done
A WebSocket looks more like this:
client connects
↓
server accepts
↓
client sends message
↓
server responds
↓
client sends another message
↓
server may push events
↓
...
↓
connection closes
The application must react to multiple events throughout the same connection.
ASGI’s scope + receive + send model was designed precisely to represent this style of interaction.
A useful way to remember it
If you remember only one thing, make it this:
ASGI = contract
Uvicorn = server
FastAPI / Starlette / Django = framework or application
async/await = execution model that ASGI can take advantage of
Or visually:
Internet
↓
Uvicorn / Hypercorn / Daphne
↓
========== ASGI ==========
↓
FastAPI / Starlette / Django
↓
Your business logic
ASGI is not the application the user sees.
It is not the server opening the network port.
It is not the framework where you define @app.get().
It is the common language between those layers.
Once that boundary is clear, much of Python’s asynchronous web ecosystem stops looking like a collection of unrelated names and starts looking like a coherent architecture.