FastAPI Hits 80K Stars by Swapping Synchronous Python for an Async Event Loop
Foreword
A high-performance Python framework guide that Java developers can also understand
In recent years, Python has been at its peak in the AI and data processing fields, and many Java developers have started to engage with the Python ecosystem.
Among the many web frameworks in Python, the rise of FastAPI is staggering.
On GitHub, FastAPI has already garnered 80K+ Stars, growing faster than Flask and Django, making it the fastest-growing web framework in the Python ecosystem.
Companies like Microsoft, Netflix, and Didi are using it in production environments.
Many friends ask me: "Brother San, I'm a Java developer, why should I learn FastAPI?"
My answer is simple: If you need to quickly build a high-performance API service, especially involving AI model deployment, data processing, or microservice scenarios, FastAPI might be one of the best choices right now.
In this article, I will explain FastAPI thoroughly from a Java developer's perspective, using comparison methods you are familiar with.
Illustrated with text and images, code is copyable, just follow along.
I hope it will be helpful to you.
More project practices on my tech website: susan.net.cn/project
1. What Exactly is FastAPI?
1.1 Explained in One Sentence
FastAPI is a modern Python-based web framework designed specifically for building high-performance APIs.
It was created by Spanish developer Sebastián Ramírez in 2018, with the core design goal of solving the pain points of traditional Python web frameworks in terms of performance, development efficiency, and type safety.
Its technical positioning can be summarized as "Three Highs": High Development Efficiency, High Runtime Performance, High Type Safety.
1.2 Comparison with Frameworks Familiar to Java Developers
To help Java developers quickly understand FastAPI's positioning, I'll use a table for comparison:
| Comparison Dimension | FastAPI (Python) | Spring Boot (Java) | Flask (Python) |
|---|---|---|---|
| Core Positioning | High-performance API framework | Enterprise-grade full-stack framework | Lightweight micro-framework |
| Performance | Extremely high (close to Go/Node.js) | High | Medium |
| Development Speed | Extremely fast | Slower | Fast |
| Auto Documentation | ✅ Native support | Requires integration like SpringDoc | ❌ Requires third-party |
| Type Safety | ✅ Pydantic strong validation | ✅ Java strong typing | ⚠️ Weaker |
| Async Support | ✅ Native async/await | ✅ Spring WebFlux | ⚠️ Requires extensions |
| Learning Curve | Low | Steep | Low |
| Applicable Scenarios | API services, microservices, AI deployment | Large enterprise applications | Simple web applications |
The relationship between FastAPI and Spring Boot is a bit like a sports car and an SUV—the sports car is light and agile, suitable for high-speed sprints; the SUV is stable and solid, suitable for long-distance treks and complex road conditions.
Each has its strengths, the key is what you need to do.
1.3 What Can FastAPI Actually Do?
The FastAPI official documentation summarizes its core capabilities:
High Performance: Based on Starlette (async web framework) and Pydantic (high-performance data validation), it represents the performance ceiling among Python web frameworks.
Automatic API Documentation Generation: No need to write documentation; Swagger UI (/docs) and ReDoc (/redoc) are generated automatically.
Automatic Parameter Validation Using Python Type Hints: Automatic request parameter validation, automatic response model validation, excellent IDE auto-completion experience.
Native Async Support: Fully supports async/await, suitable for high-concurrency I/O scenarios.
Dependency Injection System: Supports permission checks, token validation, DB session management, and unified behavior injection.
Easy Maintenance: Type hints + auto-completion, suitable for microservice architecture.
2. Why is FastAPI So Fast?
Some friends might ask: Also written in Python, why is FastAPI so much faster than Flask?
The answer is three letters: ASGI.
2.1 WSGI vs ASGI: A Revolution
Traditional Python web frameworks (like Flask, Django) are based on the WSGI (Web Server Gateway Interface) specification.
WSGI is synchronous—each request monopolizes a thread until processing is complete. This is like a restaurant where each waiter can only serve one table at a time, leaving other guests waiting.
FastAPI, on the other hand, is based on the ASGI (Asynchronous Server Gateway Interface) specification.
ASGI is asynchronous and non-blocking—each request is scheduled within an event loop rather than monopolizing thread resources.
This is like the same waiter being able to serve multiple tables simultaneously—taking an order for one table, and while the kitchen is cooking, going to take orders for another table, naturally increasing efficiency significantly.
This is why FastAPI can easily handle thousands of concurrent connections.
2.2 Three-Engine Drive Architecture
FastAPI's architecture can be summarized as a "star model," driven by three core engines:
① Routing System: Implements RESTful routing based on path operation decorators (@app.get, @app.post), supporting automatic parsing of path and query parameters. Its path matching algorithm uses regex optimization, achieving a performance improvement of up to 40% over Flask's Werkzeug routing when there are many path parameters.
② Dependency Injection System: Implements automatic resolution of service dependencies via the Depends keyword, especially suitable for unified management of resources like database connections. Its underlying implementation uses a function decorator pattern, preserving the original function via the __wrapped__ attribute and dynamically injecting dependencies at runtime.
③ Data Validation Engine: FastAPI's data validation is based on Pydantic's BaseModel. The validation process includes field type checking, constraint validation, nested model validation, and extra attribute checking.
2.3 Pydantic Validation Process
When a request arrives at FastAPI, the data validation process works like this:
If a non-string type username is passed in the request, the framework immediately returns a 422 error, clearly indicating which field failed and why.
This design of "stopping unqualified requests at the door" greatly reduces defensive checks in business code.
2.4 Performance Data
In the TechEmpower benchmark tests, FastAPI achieved 18,732 req/sec (sync mode) and 32,451 req/sec (async mode) in JSON serialization scenarios.
JSON serialization performance reaches 8 times that of Django, close to the level of the Go framework Gin.
For API development, FastAPI is approximately 2-3 times faster than Flask.
In I/O-intensive scenarios, this gap becomes even more pronounced.
3. Environment Setup: Get Running in 5 Minutes
3.1 Install Python Environment
It is recommended to use Python 3.9+:
# Use pyenv to manage Python versions (recommended)
brew install pyenv
pyenv install 3.11.5
pyenv global 3.11.5
# Or use the system Python directly
python3 --version
3.2 Create Project and Install Dependencies
# Create project directory
mkdir fastapi-demo
cd fastapi-demo
# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install FastAPI and Uvicorn
pip install fastapi uvicorn[standard]
Uvicorn is an ASGI server, equivalent to Tomcat or Netty in Java.
3.3 Write Your First API
Create a main.py file:
from fastapi import FastAPI
# Create an application instance
app = FastAPI(title="My First FastAPI App", version="1.0.0")
# Define routes
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello, {name}!"}
3.4 Start the Service
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Parameter explanation:
main:app—— theappinstance in themain.pyfile--reload—— auto-restart in development mode (do not use in production)--host—— listening address--port—— port number
After starting, visit the following addresses:
- API Service: http://localhost:8000
- Swagger Documentation: http://localhost:8000/docs
- ReDoc Documentation: http://localhost:8000/redoc
Visiting /docs you will see an interactive API document—you wrote nothing, and the documentation has already been generated automatically. This is one of FastAPI's most amazing features.
4. Core Concepts in Practice
4.1 Path Parameters and Query Parameters
from fastapi import FastAPI
app = FastAPI()
# Path parameters: extracted from the URL path
@app.get("/users/{user_id}")
async def get_user(user_id: int): # Automatic type conversion + validation
return {"user_id": user_id, "name": f"User_{user_id}"}
# Query parameters: extracted from the URL query string
@app.get("/items")
async def list_items(
skip: int = 0, # Default value
limit: int = 10, # Default value
category: str | None = None # Optional parameter
):
return {"skip": skip, "limit": limit, "category": category}
Code Explanation:
- The path parameter
{user_id}is extracted from the URL, anduser_id: intautomatically performs type conversion and validation—passing a non-number returns a 422 error. - Query parameters are extracted from
?skip=0&limit=10; parameters with default values are optional. - Type annotations allow the IDE to provide auto-completion and enable the framework to validate automatically.
Visit examples:
GET /users/123→{"user_id": 123, "name": "User_123"}GET /items?skip=5&limit=20&category=books
4.2 Request Body and Pydantic Models
This is one of FastAPI's core capabilities—defining request and response data structures using Pydantic models.
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from typing import Optional
from datetime import datetime
app = FastAPI()
# Define request body model
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20, description="Username")
email: EmailStr = Field(..., description="Email address")
password: str = Field(..., min_length=8, description="Password")
age: Optional[int] = Field(None, ge=0, le=150, description="Age")
tags: list[str] = []
# Define response body model
class UserResponse(BaseModel):
id: int
username: str
email: str
age: Optional[int]
created_at: datetime
@app.post("/users", response_model=UserResponse)
async def create_user(user: UserCreate):
# Business logic: create user
return UserResponse(
id=1,
username=user.username,
email=user.email,
age=user.age,
created_at=datetime.now()
)
Code Explanation:
UserCreatedefines the structure of the request body, andFieldprovides additional validation rules (minimum length, maximum length, value range, etc.).EmailStrautomatically validates the email format.response_model=UserResponsespecifies the response data structure; the framework automatically filters out fields not in the model.- If the request lacks required fields or field types are incorrect, FastAPI automatically returns a 422 error and explains why.
This is the charm of "declarative programming"—you just declare "what I want," and the framework handles "how to validate."
4.3 Dependency Injection
FastAPI's dependency injection system is very flexible, suitable for handling cross-cutting concerns like permission checks and database session management.
from fastapi import FastAPI, Depends, Header, HTTPException
app = FastAPI()
# Define a dependency: verify token
async def verify_token(authorization: str = Header(...)):
"""Extract and verify token from request header"""
if not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid authentication format")
token = authorization.replace("Bearer ", "")
if token != "valid-token":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": 1, "username": "admin"}
# Use the dependency
@app.get("/protected")
async def protected_route(user: dict = Depends(verify_token)):
return {"message": f"Welcome, {user['username']}!", "user": user}
Code Explanation:
verify_tokenis a dependency function that extractsAuthorizationfrom the request header and validates it.Depends(verify_token)injects the dependency into the route function.- If validation fails, a 401 error is automatically returned.
- Upon successful validation, the returned user information is passed as the
userparameter to the route function.
This design completely separates authentication logic from business logic, making the code clearer and more testable.
4.4 Async Support
FastAPI natively supports async/await, which is key to its high performance.
import asyncio
from fastapi import FastAPI
app = FastAPI()
# Synchronous function (suitable for CPU-bound operations)
@app.get("/sync")
def sync_endpoint():
# Synchronous operation, will block the thread
return {"result": "done"}
# Asynchronous function (suitable for I/O-bound operations)
@app.get("/async")
async def async_endpoint():
# Simulate I/O operation: database query, external API call, etc.
await asyncio.sleep(1)
# While waiting, the event loop can handle other requests
return {"result": "done after 1 second"}
# Mixed use: calling synchronous code within an async function
@app.get("/mixed")
async def mixed_endpoint():
# Use run_in_executor to run synchronous code in a thread pool
result = await asyncio.to_thread(sync_heavy_work)
return {"result": result}
def sync_heavy_work():
# CPU-bound operation
return sum(range(1000000))
Code Explanation:
- Asynchronous functions are defined with
async def, releasing thread resources while waiting for I/O. await asyncio.sleep(1)simulates I/O waiting, during which the event loop can process other requests.- For CPU-bound operations, use
asyncio.to_threadto execute them in a thread pool to avoid blocking the event loop.
5. Database Integration
5.1 Async SQLAlchemy Integration
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import declarative_base, Mapped, mapped_column
from sqlalchemy import select
# Database configuration
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/db"
engine = create_async_engine(DATABASE_URL, echo=True)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
Base = declarative_base()
# Define model
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(unique=True)
email: Mapped[str]
# Dependency: get database session
async def get_db():
async with AsyncSessionLocal() as session:
yield session
app = FastAPI()
@app.get("/users")
async def get_users(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User))
users = result.scalars().all()
return [{"id": u.id, "username": u.username, "email": u.email} for u in users]
Code Explanation:
create_async_enginecreates an asynchronous database engine.AsyncSessionLocalis an asynchronous session factory.get_dbis a dependency that creates a database session for each request and automatically closes it after the request ends.- All database operations are asynchronous and will not block the event loop.
6. Unified Response and Exception Handling
In a production environment, a unified response format and exception handling are essential.
6.1 Unified Response Model
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional
T = TypeVar("T")
class ApiResponse(BaseModel, Generic[T]):
"""Unified API response format"""
code: int = 200
msg: str = "success"
data: Optional[T] = None
app = FastAPI()
@app.get("/users/{user_id}", response_model=ApiResponse)
async def get_user(user_id: int):
if user_id <= 0:
return ApiResponse(code=400, msg="User ID must be greater than 0")
# Simulate query
user = {"id": user_id, "name": f"User_{user_id}"}
return ApiResponse(data=user)
6.2 Global Exception Handling
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"code": exc.status_code, "msg": exc.detail, "data": None}
)
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"code": 500, "msg": "Internal server error", "data": None}
)
With global exception handling, any uncaught exception is uniformly formatted into a standard response structure, so the frontend no longer has to guess "what format does this endpoint return."
7. Middleware
Middleware can perform unified processing before a request enters a route or before a response is returned.
from fastapi import FastAPI, Request
import time
app = FastAPI()
# Logging middleware: record the processing time for each request
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
# Log request information
print(f"Request received: {request.method} {request.url.path}")
# Continue processing the request
response = await call_next(request)
# Log response information
process_time = time.time() - start_time
print(f"Request completed: {process_time:.4f} seconds")
# Add processing time to response headers
response.headers["X-Process-Time"] = str(process_time)
return response
8. Automatic Documentation: Writing Code = Writing Documentation
One of FastAPI's most amazing features is automatic API documentation generation.
You don't need to write any extra documentation code. FastAPI automatically generates two sets of documentation based on your route definitions, Pydantic models, and type annotations:
- Swagger UI (
/docs): Interactive documentation, allowing online API debugging. - ReDoc (
/redoc): More aesthetically pleasing static documentation.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(
title="E-commerce API",
description="This is the API documentation for an e-commerce platform",
version="1.0.0",
contact={"name": "Tech Team", "email": "[email protected]"}
)
class Product(BaseModel):
name: str
price: float
stock: int
@app.post("/products",
summary="Create product",
description="Create a new product, requiring name, price, and stock",
response_description="Successfully created product information")
async def create_product(product: Product):
"""Create product endpoint"""
return {"id": 1, **product.model_dump()}
After starting the service, visiting /docs will show you a complete, interactive API document—parameter descriptions, request examples, and response examples are all generated automatically.
During front-end and back-end integration, the backend sends the service address to the frontend, and the frontend can see detailed information for all endpoints by opening /docs, and even test them online.
This experience is something you can never go back from once you've tried it.
9. Pros and Cons
Pros
1. Extremely High Development Efficiency: Automatically generates API documentation through Python type annotations, eliminating the need to manually write Swagger configurations. Defining a user registration endpoint reduces code volume by 60% compared to traditional frameworks. In real projects, the development cycle can be shortened from 6 weeks to 2 weeks.
2. Excellent Performance: Based on the ASGI asynchronous architecture, JSON serialization performance in TechEmpower benchmarks reaches 8 times that of Django, close to the level of the Go framework Gin.
3. Automatic Documentation Generation: Documentation is automatically generated while writing code, greatly improving front-end and back-end integration efficiency.
4. Type Safety: Automatically checks data types at runtime through Pydantic models, with error responses accurately indicating which field failed and why.
5. Native Async Support: Naturally supports async/await, making it very suitable for I/O-intensive workloads (API calls, database queries, file operations).
6. Dependency Injection System: Very flexible, supporting permission checks, token validation, DB session management, etc.
7. Production-Grade Features: Built-in middleware for CORS, GZip, HTTPS redirect, etc., and supports WebSocket real-time communication.
Cons
1. Ecosystem Not as Mature as Django: Features like ORM and Admin are not as complete as Django's.
2. Pydantic Learning Curve: Beginners need to adapt to the Model pattern.
3. High Reliance on Type Hints: Code volume is slightly more compared to Flask.
4. Some Components Require Self-Packaging: Such as global exception handling and middleware systems.
5. Relatively New Community: Although growing rapidly, compared to Django and Flask, it may lack sufficient support and resources in certain specific scenarios.
6. CPU-Intensive Scenarios Not as Good as Java: In CPU-intensive scenarios, Spring Boot (Java) offers more stable performance due to JIT optimization and thread pool advantages.
10. A Diagram to Understand Applicable Scenarios
Best Use Cases
AI Model Deployment: When machine learning models need to provide external services, FastAPI's high concurrency + auto documentation features are a perfect fit. An AI company used FastAPI to deploy a model prediction service, significantly shortening the development cycle.
Microservice Architecture: An e-commerce platform refactored its order service using FastAPI, shortening the development cycle from 6 weeks to 2 weeks, reducing error rates by 72%, and supporting 2000+ concurrent requests per second.
Data Processing APIs: Scenarios requiring external interfaces for data querying, statistics, exporting, etc.
Real-time Applications: Scenarios requiring bidirectional communication, such as WebSocket chat and real-time monitoring.
Rapid Prototyping: Scenarios requiring quick idea validation and demo presentation.
Less Suitable Scenarios
Large Enterprise-Grade Full-Stack Applications: If a powerful Admin backend, comprehensive ORM, and built-in user authentication system are needed, Django might be more suitable.
CPU-Intensive Computing: Scenarios like complex algorithms and big data processing, where Java (Spring Boot) offers more stable performance due to JIT optimization.
Teams with Existing Java Tech Stack: If the team consists entirely of Java developers, introducing Python incurs additional learning and operational costs.
11. A Real Comparison with Spring Boot
A developer conducted a real experiment: writing the same business service using both FastAPI and Spring Boot, and running them online simultaneously for six months.
Development Phase: FastAPI got the service running in just two days, while the Spring Boot side was still struggling with Maven dependencies. Automatic parameter validation and documentation generation were achieved with just a few lines of code.
Stress Testing Phase (1000 concurrent users):
- FastAPI: 50th percentile response time 45ms, throughput 2400 req/sec, memory usage 180MB.
- Spring Boot: 50th percentile response time 80ms, throughput 1800 req/sec.
FastAPI led comprehensively in development speed and initial performance.
But the final result was that the Java developer colleague won—not because of performance, but because of "non-functional requirements" like the operations system, monitoring and alerting, log aggregation, and error tracking, where Spring Boot's ecosystem is more mature.
This experiment tells us: Technology selection cannot only look at development speed and performance; it must also consider the entire team's tech stack, operational system, and long-term maintenance costs.
More project practices on my tech website: susan.net.cn/project
12. Final Words
FastAPI is not a framework meant to "replace" Spring Boot, but a tool that can help you achieve twice the result with half the effort in specific scenarios.
If you are a Java developer, consider adding FastAPI to your technical toolbox when encountering the following situations:
- Need to quickly deploy an AI model inference service.
- Need to build a high-performance data API.
- Need to quickly validate a product prototype.
- The team already has Python capabilities.
If you are a complete Python novice, FastAPI might be one of the best introductory frameworks—concise syntax, excellent documentation, an active community, and it allows you to see results quickly.
There is no silver bullet in technology selection.
FastAPI performs amazingly in scenarios like API development, microservices, and AI deployment, but in scenarios like large enterprise-grade full-stack applications and CPU-intensive computing, Spring Boot remains the more reliable choice.
My suggestion is: Spend a weekend running through FastAPI, writing a complete CRUD service. Experience the "writing code = writing documentation" feeling, and appreciate the convenience of "automatic validation via type annotations." Then, based on your actual project needs, decide whether to introduce it into a production environment.
After all, having one more handy tool means having one more way to solve a problem.
Open Source Addresses:
- FastAPI: https://github.com/tiangolo/fastapi (80K+ Stars)
- Official Documentation: https://fastapi.tiangolo.com