Follow a request
A web framework defines application behavior. An application server accepts network connections and invokes that behavior. A reverse proxy can sit in front to handle routing, HTTPS, or other edge responsibilities.
Browser
│ HTTPS
▼
Reverse proxy or managed load balancer
│ HTTP
▼
ASGI server → Python application → Database / external service
FastAPI is a framework. Uvicorn is an ASGI application server. Nginx and Caddy can act as reverse proxies. A cloud platform may provide the proxy role without requiring a separately managed proxy installation.
What ASGI means
ASGI defines an interface between a Python server and an application. It supports asynchronous interaction and connection types such as WebSockets. WSGI defines the older synchronous request-response interface.
These interfaces operate at a different layer from the HTTP connection between a browser and a server. Using ASGI also does not make every operation in application code nonblocking.
An illustrative FastAPI application might expose a simple liveness response:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
An ASGI server imports the application object and serves requests. A development reloader restarts it after source changes; production instead needs an explicit service lifecycle and recovery policy.
A running process is one part of a service
| Responsibility | Typical owner |
|---|---|
| Routing and request validation | Web application |
| HTTP connections and application invocation | ASGI server |
| HTTPS and edge routing | Proxy or managed platform |
| Process startup and restart | Service manager or container platform |
| Release validation and rollout | Deployment pipeline |
| Durable records and job state | Database, queue, or other shared service |
Uvicorn can manage multiple worker processes. Some deployments use a separate worker manager. A particular process manager is not a universal prerequisite for serving a Python application.
Workers do not share ordinary Python memory
Consider an application that stores job status in a module-level dictionary:
Create job → Worker A → Status saved in A's memory
Read job → Worker B → Job may not be present
Each worker has its own memory. Restarting the process also discards that state. Adding replicas can expose the same problem across containers.
Use a shared persistence layer when status must survive requests, process changes, or restarts. Long-running jobs may also need a queue, retry policy, idempotency, cancellation, and durable execution records.
Starting a background task in an API process does not create a durable job system. A single worker avoids cross-worker memory differences but does not provide recovery after a crash.
Streaming adds lifecycle requirements
Server-Sent Events send events over a continuing HTTP response. A buffering proxy can delay delivery, while idle timeouts can close connections.
Check buffering, timeouts, heartbeats, reconnection, and event replay requirements. During a release, define what happens to existing streams and long-running work before terminating the old process.
Forwarded headers also need a trust boundary. An application should accept client-address and protocol information only from the proxies it is configured to trust.
Verify the behavior users need
A fixed /health response confirms that a request reached a responding application. It does not prove that the database, job processing, or streaming path works.
Distinguish basic liveness from readiness to receive traffic. Test a representative workflow, observe failures and resource use, and verify restart behavior before adding more workers.