When designing a photo and video sharing platform like Instagram, your backend needs to serve thousands of requests per second: uploading posts, querying feeds, checking follower status, and verifying JWT sessions.
To achieve this level of performance without complex multi-threading bugs, modern Python relies on a specialized server stack: FastAPI, Starlette, ASGI, and Uvicorn.
In this tutorial, we will demystify how these four technologies fit together like a set of building blocks, and how they power our Instagram clone backend.
1. The Big Picture: How They Fit Together
A common point of confusion for developers is telling these four technologies apart. Here is the architectural hierarchy:
+-----------------------------------------------------------+
| CLIENT |
| (React Web App, Mobile App, Postman) |
+-----------------------------+-----------------------------+
| HTTP / WebSocket Request
v
+-----------------------------------------------------------+
| SERVER RUNTIME: Uvicorn |
| (ASGI Web Server / Network Listener) |
+-----------------------------+-----------------------------+
| ASGI Protocol (scope, receive, send)
v
+-----------------------------------------------------------+
| CORE WEB TOOLKIT: Starlette (ASGI App) |
| (Routing, Middleware, Exception Handling, Cookies) |
+-----------------------------+-----------------------------+
| Extends & Enhances
v
+-----------------------------------------------------------+
| HIGH-LEVEL FRAMEWORK: FastAPI |
| (Pydantic Validation, Dependency Injection, Docs) |
+-----------------------------------------------------------+
Think of it like a restaurant:
- ASGI: The communication standard (the universal language the kitchen and waiters speak).
- Uvicorn: The fast delivery runner bringing raw orders from customers into the kitchen.
- Starlette: The prep station handling essential tools (plates, sauces, tickets).
- FastAPI: The master chef crafting gourmet recipes, verifying ingredients, and printing menus.
2. What is ASGI? (Asynchronous Server Gateway Interface)
Before ASGI, the Python standard was WSGI (Web Server Gateway Interface), used by frameworks like Flask and Django.
- WSGI is synchronous: A request enters, the server process/thread handles it from start to finish, blocking until done. It cannot natively handle WebSockets or async long-polling.
- ASGI is asynchronous: It is a standard specification designed to allow multiple asynchronous Python web frameworks to talk to asynchronous web servers.
An ASGI application is simply an async function with three parameters:
async def simple_asgi_app(scope, receive, send):
"""A minimal ASGI application."""
if 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"Welcome to Instagram Clone API!",
})
While writing raw ASGI apps is possible, handling routing, cookies, headers, and file uploads manually would be tedious. That is where Starlette comes in.
3. Starlette: The Lightweight ASGI Engine
Starlette is a lightweight, high-performance ASGI framework/toolkit. It provides all the core web machinery:
- High-performance URL routing.
- Custom middleware support (CORS, security headers, request ID tracking).
- Session, cookie, and static file management.
- Native WebSocket support for real-time notifications and direct messages.
FastAPI is actually built directly on top of Starlette! Every FastAPI app is a subclassed, feature-rich Starlette application:
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request):
return JSONResponse({"status": "Instagram API alive"})
app = Starlette(routes=[Route("/", homepage)])
4. FastAPI: The Modern Developer Superpower
If Starlette handles raw web plumbing, FastAPI adds the developer magic:
1. Automatic Request Validation (via Pydantic)
FastAPI validates request bodies, headers, and query parameters before your business logic even runs. If a client sends invalid data, FastAPI automatically replies with a detailed 422 Unprocessable Entity error:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="Instagram Clone API")
class CreatePostSchema(BaseModel):
caption: str = Field(..., max_length=2200)
image_url: str
@app.post("/posts")
async def create_post(post_data: CreatePostSchema):
# post_data is guaranteed to be valid and type-safe!
return {
"message": "Post created successfully",
"caption": post_data.caption
}
2. Powerful Dependency Injection System (Depends)
FastAPI has an elegant dependency injection engine. In our Instagram backend, we use it for database sessions and authentication:
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
async def get_current_user(token: str = Depends(oauth2_scheme)):
# Decodes and validates JWT token
user = verify_token_and_fetch_user(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token"
)
return user
@app.get("/users/me/feed")
async def get_my_feed(current_user = Depends(get_current_user)):
# Only authenticated users reach this code
return {"feed_owner": current_user.username, "posts": []}
3. Interactive API Documentation Out-of-the-Box
FastAPI inspects your code and auto-generates interactive OpenAPI documentation:
- Swagger UI at
/docs(allows testing endpoints directly in the browser). - ReDoc at
/redoc(clean, readable API specification).
5. Uvicorn: The High-Speed ASGI Web Server
FastAPI and Starlette define what happens when a request arrives, but they cannot listen on network ports by themselves. That is Uvicorn's job.
Uvicorn is a lightning-fast ASGI server implementation using:
-
uvloop: An ultra-fast drop-in replacement for the default Python event loop, written in C and built on libuv (the same event loop powering Node.js). -
httptools: A fast Python binding for the NodeJS HTTP parser.
Running the Server in Development vs. Production
In development, we run Uvicorn with auto-reload:
uvicorn main:app --reload --host 0.0.0.0 --port 8000
-
main: Refers tomain.py. -
app: Refers to theapp = FastAPI()object defined insidemain.py. -
--reload: Automatically reloads the server whenever code changes are detected.
In a production environment, you typically run multiple Uvicorn worker processes managed by a process manager:
uvicorn main:app --workers 4 --host 0.0.0.0 --port 8000
6. How It All Connects in Our Instagram Project
Here is a look at how this stack comes together in our real backend:
-
Incoming Request: A client sends a
GET /feedrequest containing a Bearer token. -
Uvicorn: Accepts the TCP connection, parses HTTP bytes using
httptools, and constructs the ASGI scope. - Starlette: Runs custom middleware (e.g., logging response times, setting CORS headers for our React frontend).
-
FastAPI:
- Matches the route
/feed. - Runs
Depends(get_current_user)to authenticate the session. - Executes the asynchronous database query (
await db.execute(...)). - Serializes the output using Pydantic schemas.
- Matches the route
- Response: Uvicorn sends the formatted JSON back across the wire to the user.
Key Takeaways
| Technology | Role | What It Does In Our Project |
|---|---|---|
| ASGI | Specification | Standardizes async communication between Python servers and apps. |
| Uvicorn | Web Server | Listens on ports, parses raw network packets, runs the event loop. |
| Starlette | Core Toolkit | Provides routing, middleware, cookies, and low-level HTTP primitives. |
| FastAPI | Web Framework | Provides input validation, JWT dependencies, OpenAPI docs, and clean endpoints. |
In the next post, we will look at Database Integration & Migrations: PostgreSQL, SQLAlchemy (Asyncio), and Alembic!
Top comments (0)