مقدمة في FastAPI — لماذا هو إطار الويب بايثون من الجيل التالي
إذا كانت Flask سكينيناً سويسرياً متعدد الاستخدامات، وDjango سيارة دفع رباعي مجهزة بالكامل، فإن FastAPI سيارة رياضية كهربائية — تسرع بسرعة، وتوفّر الطاقة، وتأتي مع لوحة قيادة مُولَّدة تلقائياً.
1. ما ستتعلمه
- الفرق الأساسي بين ASGI وWSGI: لماذا التزامن هو المستقبل
- FastAPI مقابل Flask مقابل Django REST Framework: مقارنة الأداء وتجربة التطوير
- كيف تقود تلميحات الأنواع التحقق التلقائي وتوليد التوثيق
- نظرة معمّقة على بنية Starlette وPydantic الأساسية
- نظرة عامة على مشروع PriceTracker: ما تخطط Alice لبنائه
2. القصة الحقيقية لـ Alice
(1) نقطة الألم: إطار التزامن لا يستطيع التعامل مع ملايين الطلبات
Alice مهندسة واجهة خلفية تبني PriceTracker — واجهة برمجة تطبيقات SaaS لتتبع الأسعار لمنصة تجارة إلكترونية — تحتاج إلى التعامل مع استعلامات فورية لملايين أسعار المنتجات. بنت النموذج الأولي باستخدام Flask، لكن عندما تجاوزت الطلبات المتزامنة 1,000 QPS، تسبب نموذج WSGI المتزامن في اصطفاف كل طلب، وقفز زمن الاستجابة P99 إلى 3,000 مللي ثانية. اشتكى Bob (مهندس الواجهة الأمامية) من بطء تحميل الصفحة، وقال Charlie (DevOps) أن تكلفة التوسع الأفقي مرتفعة جداً.
(2) حل FastAPI
FastAPI مبني على بروتوكول ASGI غير المتزامن ويمكنه التعامل مع آلاف الاتصالات المتزامنة بعملية واحدة. تولّد تلميحات الأنواع تلقائياً توثيق OpenAPI وتتحقق من بيانات الطلب، لذلك لا تحتاج Alice لكتابة كود تحقق أو توثيق يدوياً.
from fastapi import FastAPI
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(product_id: int):
# تلميح النوع يتحقق ويولّد التوثيق تلقائياً
return {"product_id": product_id, "name": "Widget"}
(3) العائد
بعد الانتقال إلى FastAPI، ارتفع QPS لعقدة PriceTracker الواحدة من 500 إلى أكثر من 4,000، وانخفض زمن الاستجابة P99 إلى 50 مللي ثانية، وتحمّلت واجهة Bob الأمامية أسرع بـ 5 مرات، وانخفضت تكاليف خوادم Charlie بنسبة 60%.
3. ASGI وWSGI: التزامن هو المستقبل
(1) اختناقات WSGI المتزامنة
WSGI (واجهة بوابة خادم الويب) هو المعيار التقليدي لتطبيقات الويب بايثون؛ كل طلب يشغل خيطاً، وينتظر الخيط محجوباً عند حدوث عملية إدخال/إخراج (مثل استعلام قاعدة بيانات أو طلب شبكة).
flowchart LR
Client1[العميل 1] -->|طلب| WSGI[خادم WSGI]
Client2[العميل 2] -->|طلب| WSGI
Client3[العميل 3] -->|طلب| WSGI
WSGI -->|الخيط 1| DB1[(قاعدة البيانات)]
WSGI -->|الخيط 2| DB1
WSGI -->|الخيط 3 - محجوب| DB1
| البُعد | WSGI | ASGI |
|---|---|---|
| نموذج الاتصال | طلب واحد، خيط واحد | كوروتينات غير متزامنة، خيط واحد مع اتصالات متعددة |
| حد التزامن | محدود بتجمع الخيوط (عادة 10-100) | شبه غير محدود (الكوروتينات خفيفة) |
| انتظار الإدخال/الإخراج | حجب الخيط | غير محجوب، يتحول إلى كوروتين آخر |
| WebSocket | غير مدعوم | دعم أصلي |
| الخوادم النموذجية | Gunicorn + Flask | Uvicorn + FastAPI |
(2) مزايا ASGI غير المتزامنة
ASGI (واجهة بوابة الخادم غير المتزامنة) هو امتداد غير متزامن لـ WSGI يدعم بناء async/await، مما يسمح لعملية واحدة بالتعامل مع آلاف الاتصالات المتزامنة.
import asyncio
import time
# نمط WSGI - يحجب الخيط
def sync_handler():
time.sleep(1) # الخيط محجوب لثانية واحدة
return "done"
# نمط ASGI - غير محجوب
async def async_handler():
await asyncio.sleep(1) # حلقة الأحداث تتحول إلى مهام أخرى
return "done"
(1) ▶ مثال: مقارنة التزامن مقابل التزامنية العالية غير المتزامنة
import asyncio
import time
async def fetch_price(product_id: int) -> dict:
# محاكاة زمن إدخال/إخراج لقاعدة البيانات
await asyncio.sleep(0.1)
return {"product_id": product_id, "price": 9.99}
async def main():
start = time.perf_counter()
# 100 طلب متزامن - التزامنية تنتهي في ~0.1 ثانية
results = await asyncio.gather(*[fetch_price(i) for i in range(100)])
elapsed = time.perf_counter() - start
print(f"Async: {len(results)} items in {elapsed:.2f}s")
asyncio.run(main())
الناتج:
Execution Successful
Output:
Async: 100 items in 0.10s
4. FastAPI مقابل Flask مقابل Django DRF
(1) التموضع ضمن منظومة الأطر
flowchart LR
FastAPI[FastAPI] --> Starlette[Starlette ASGI]
Starlette --> Uvicorn[خادم Uvicorn]
Uvicorn --> ASGI_Protocol[بروتوكول ASGI]
FastAPI --> Pydantic[Pydantic V2]
Flask2[Flask] --> Werkzeug[Werkzeug WSGI]
Werkzeug --> Gunicorn[Gunicorn]
Django2[Django DRF] --> Django_Core[نواة Django]
| البُعد | FastAPI | Flask | Django DRF |
|---|---|---|---|
| الأداء (TechEmpower RPS) | ~40,000 | ~1,200 | ~800 |
| دعم التزامنية | async/await أصلي | يتطلب إضافة | دعم محدود |
| التوثيق التلقائي | توليد تلقائي لـ OpenAPI | يتطلب Flask-RESTX | يتطلب drf-spectacular |
| التحقق من الأنواع | تحقق تلقائي بـ Pydantic | تحقق يدوي | تعريف Serializer يدوي |
| منحنى التعلم | منخفض (تلميحات الأنواع تعمل كتوثيق) | منخفض | مرتفع |
| حجم المشروع | خدمات API صغيرة إلى متوسطة | خدمات صغيرة | مشاريع كاملة كبيرة |
(2) لماذا FastAPI أنسب لـ PriceTracker
| متطلبات PriceTracker | مزايا FastAPI | عيوب Flask |
|---|---|---|
| ملايين الاستعلامات في الثانية (QPS) | تزامن عالي مع كوروتينات غير متزامنة | تزامن منخفض مع حجب متزامن |
| بث أسعار فوري عبر WebSocket | دعم أصلي | غير مدعوم |
| توثيق API تلقائي لـ Bob | توليد تلقائي لـ OpenAPI | يتطلب إعداداً إضافياً |
| التحقق من بيانات الطلب | تلقائي بـ Pydantic | مصحّح تحقق مخصص |
| مصادقة JWT | أدوات OAuth2 مدمجة | تتطلب مكتبات طرف ثالث |
(1) ▶ مثال: مقارنة ثلاث طرق لكتابة نفس API
# === FastAPI: تلميحات الأنواع = تحقق تلقائي + توثيق ===
from fastapi import FastAPI
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
app = FastAPI()
@app.post("/products")
async def create_product(product: Product):
return product # تحقق تلقائي، توثيق تلقائي
الناتج:
# تم تعريف الدالة بنجاح
# === Flask: تحقق يدوي، لا توثيق تلقائي ===
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/products", methods=["POST"])
def create_product():
data = request.get_json()
if not data or "name" not in data or "price" not in data:
return jsonify({"error": "Invalid data"}), 400
if not isinstance(data["price"], (int, float)):
return jsonify({"error": "Price must be number"}), 400
return jsonify(data)
5. تلميحات الأنواع تقود كل شيء
(1) القيمة الثلاثية لتلميحات الأنواع
يستخدم FastAPI تلميحات أنواع بايثون لإنجاز ثلاثة أمور دفعة واحدة: التحقق من البيانات، وال serialization/deserialization، وتوليد توثيق OpenAPI.
| تلميح الأنواع | الأطر التقليدية | FastAPI |
|---|---|---|
| التحقق من البيانات | if/else مكتوب يدوياً | تلقائي بـ Pydantic |
| serialization لـ JSON | json.dumps يدوي |
تلقائي عبر model_dump() |
| توثيق API | Swagger YAML مكتوب يدوياً | توليد تلقائي لـ OpenAPI |
| الإكمال التلقائي في IDE | لا يوجد | استنتاج كامل للأنواع |
(1) ▶ مثال: التحقق التلقائي بتلميحات الأنواع
from fastapi import FastAPI, Query
from typing import Optional
app = FastAPI()
@app.get("/prices")
async def search_prices(
min_price: float = Query(0.0, ge=0, description="الحد الأدنى للسعر بالدولار"),
max_price: float = Query(999999.0, le=999999, description="الحد الأقصى للسعر بالدولار"),
category: Optional[str] = Query(None, max_length=50),
):
return {"min_price": min_price, "max_price": max_price, "category": category}
الناتج:
# تم تعريف الدالة بنجاح
(2) ▶ مثال: توثيق OpenAPI المُولَّد تلقائياً
# بعد تعريف نقطة النهاية أعلاه، قم بزيارة:
# http://localhost:8000/docs -> Swagger UI
# http://localhost:8000/redoc -> ReDoc
# http://localhost:8000/openapi.json -> مخطط OpenAPI الخام
Output (مقتطف من
/openapi.json):
{
"paths": {
"/prices": {
"get": {
"summary": "Search Prices",
"parameters": [
{"name": "min_price", "in": "query", "schema": {"type": "number", "minimum": 0.0}}
]
}
}
}
}
(2) دور Pydantic
Pydantic V2 هو محرك تحقق من البيانات لـ FastAPI؛ أُعيدت كتابة نواته بـ Rust، مما يجعله أسرع بـ 5 إلى 50 مرة من V1.
| الميزة | Pydantic V1 | Pydantic V2 |
|---|---|---|
| المحرك الأساسي | Python | Rust (pydantic-core) |
| سرعة التحقق | المعيار المرجعي | أسرع بـ 5-50 مرة |
| المُحقِّق | @validator |
@field_validator/@model_validator |
| الإعداد | class Config |
model_config = ConfigDict(...) |
| serialization | .dict() |
.model_dump() |
6. البنية الأساسية: Starlette + Pydantic
(1) بنية FastAPI ثلاثية الطبقات
FastAPI نفسه غلاف رقيق؛ قدراته الأساسية تأتي من Starlette (إطار ASGI) وPydantic (التحقق من البيانات).
| الطبقة | المكون | المسؤوليات |
|---|---|---|
| طبقة التطبيق | FastAPI | تسجيل المسارات، حقن التبعيات، توليد OpenAPI |
| طبقة ASGI | Starlette | الوسائط، معالجة الطلب/الاستجابة، WebSocket |
| طبقة التحقق | Pydantic | التحقق من البيانات، serialization، وتحويل الأنواع |
| طبقة الخدمة | Uvicorn | خادم ASGI، إدارة حلقة الأحداث |
(1) ▶ مثال: استخدام ميزات Starlette مباشرة في FastAPI
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse
app = FastAPI()
# وسيط Starlette يعمل بسلاسة مع FastAPI
app.add_middleware(CORSMiddleware, allow_origins=["*"])
# فئات استجابة Starlette تعمل أيضاً
@app.get("/health")
async def health_check():
return JSONResponse({"status": "healthy"})
الناتج:
# تم تعريف الدالة بنجاح
(2) ▶ مثال: استخدام نماذج Pydantic في FastAPI
from fastapi import FastAPI
from pydantic import BaseModel, Field
class PriceCreate(BaseModel):
product_id: int = Field(gt=0)
price: float = Field(gt=0, description="السعر بالدولار")
currency: str = Field(default="USD", max_length=3)
app = FastAPI()
@app.post("/prices")
async def create_price(data: PriceCreate):
# تم التحقق من البيانات وتحليلها بالفعل بواسطة Pydantic
validated = data.model_dump()
return {"status": "created", "data": validated}
الناتج:
# تم تعريف الدالة بنجاح
7. نظرة عامة على مشروع PriceTracker
(1) ماذا تحاول Alice بناءه؟
PriceTracker هو واجهة برمجة تطبيقات SaaS لتتبع الأسعار. تشمل ميزاته الأساسية:
| وحدة الوظيفة | نقطة نهاية API | الوصف |
|---|---|---|
| إدارة المنتجات | CRUD في /products |
ملايين سجلات المنتجات |
| تتبع الأسعار | CRUD في /prices |
استعلامات وإشعارات أسعار فورية |
| مصادقة المستخدمين | /auth/login، /auth/register |
مصادقة JWT برمزين |
| خطط الاشتراك | Free/Pro/Enterprise | صلاحيات SaaS متعددة المستأجرين |
| الاستيراد المجمع | /import/csv |
استيراد غير متزامن لملفات CSV من 1,000 صف |
| إشعارات فورية | WebSocket /ws/prices |
تنبيهات فورية لتغيير الأسعار |
flowchart LR
Bob[Bob - الواجهة الأمامية] -->|HTTP/WebSocket| API[PriceTracker API]
Charlie[Charlie - DevOps] -->|مراقبة| API
API -->|استعلام| DB[(PostgreSQL)]
API -->|تخزين مؤقت| Redis[(Redis)]
API -->|مهمة| Celery[عامل Celery]
Celery -->|جلب| External[مواقع خارجية]
(1) ▶ مثال: PriceTracker — نسخة عمل مصغّرة
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Optional
app = FastAPI(title="PriceTracker API", version="0.1.0")
class ProductCreate(BaseModel):
name: str = Field(max_length=200)
category: str = Field(max_length=100)
base_price: float = Field(gt=0, description="السعر الأساسي بالدولار")
class ProductResponse(BaseModel):
id: int
name: str
category: str
base_price: float
PRODUCTS_DB: dict[int, dict] = {}
_counter = 0
@app.post("/products", response_model=ProductResponse)
async def create_product(product: ProductCreate):
global _counter
_counter += 1
record = {"id": _counter, **product.model_dump()}
PRODUCTS_DB[_counter] = record
return record
@app.get("/products/{product_id}", response_model=ProductResponse)
async def get_product(product_id: int):
if product_id not in PRODUCTS_DB:
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Product not found")
return PRODUCTS_DB[product_id]
الناتج:
# تم تعريف الدالة بنجاح
8. مثال شامل
تكمن قوة FastAPI الأساسية في التحقق التلقائي وتوليد التوثيق المدفوع بتلميحات الأنواع. يوضح ما يلي نقطة نهاية كاملة لاستعلام الأسعار تدمج معاملات المسار ونماذج Pydantic ونماذج الاستجابة.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="PriceTracker Demo")
class PriceResponse(BaseModel):
product_id: int = Field(gt=0)
product_name: str
price: float = Field(gt=0)
currency: str = "USD"
PRICES_DB: dict[int, dict] = {
1: {"product_id": 1, "product_name": "Widget", "price": 9.99},
2: {"product_id": 2, "product_name": "Gadget", "price": 24.50},
}
@app.get("/products/{product_id}", response_model=PriceResponse)
async def get_price(product_id: int):
if product_id not in PRICES_DB:
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Product not found")
return PRICES_DB[product_id]
الناتج:
GET /products/1 → {"product_id":1,"product_name":"Widget","price":9.99,"currency":"USD"}
GET /products/99 → 404 Not Found
❓أسئلة شائعة
async/await؟async يقدم أداءً أفضل في السيناريوهات الكثيفة الإدخال/الإخراج (مثل قواعد البيانات وطلبات الشبكة).📖ملخص
- FastAPI مبني على بروتوكول ASGI غير المتزامن؛ عملية واحدة يمكنها التعامل مع آلاف الطلبات المتزامنة، بأداء يفوق أطر WSGI بكثير.
- تقود تلميحات الأنواع في الوقت نفسه التحقق من البيانات وserialization وتوليد توثيق OpenAPI، مما يقلل الكود المتكرر بنسبة 70%
- FastAPI غلاف خفيف حول Starlette (إطار ASGI) وPydantic (التحقق من البيانات)، وكل طبقة يمكن استخدامها بشكل مستقل.
- Pydantic V2 أعاد كتابة نواته بـ Rust، مما يجعله أسرع بـ 5 إلى 50 مرة من V1، وهو مفتاح أداء FastAPI
- مشروع PriceTracker يمتد عبر 25 درساً، يأخذك من البناء من الصفر إلى النشر في الإنتاج، ويغطي سيناريوهات SaaS لملايين المستخدمين.
📝تمارين
- تمرين أساسي (الصعوبة: ⭐): ثبّت FastAPI وUvicorn، أنشئ نقطة نهاية GET تُرجع
{"message": "Hello PriceTracker"}، وشغّلها باستخدامuvicorn. تلميح:pip install fastapi uvicorn - مسألة متقدمة (الصعوبة ⭐⭐): أضف معامل المسار
nameإلى نقطة النهاية، أرجع{"message": "Hello, {name}"}، وزُر/docsفي متصفحك لعرض التوثيق المُولَّد تلقائياً. تلميح:@app.get("/hello/{name}") - تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ نموذج Pydantic باسم
PriceInputيتضمنproduct_name: strوprice: float(يجب أن يكون > 0)، يستخدم نقطة نهاية POST لاستقبال البيانات، ويرجع نتيجة التحقق. تلميح: ورّث منBaseModelواستخدمField(gt=0)
---|



