ابدأ بـ بايثون فاست إي بي آي: خطوتك الأولى العملية اليوم

بايثون FastAPI: الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة تتكون من 32 درسًا.

ابدأ بـ بايثون فاست إي بي آي: خطوتك الأولى العملية اليوم

أفضل طريقة لتعلم Python FastAPI هي بالممارسة. يضعك هذا المقال على الطريق الصحيح مع مقتطفات عملية مستمدة من دورة تضم 32 درسًا — ما يكفي للحصول على نتيجة أولية اليوم.

tl;dr
  • مقدمة وتثبيت
  • المسارات والطرق
  • Pydantic والتحقق
  • حقن التبعيات
  • قاعدة البيانات
~$ cat ./parcours.md # Python FastAPI — 10 فصول
01
مقدمة وتثبيت
→ Pourquoi FastAPI ?→ Installer FastAPI et uvicorn+ 1 دروس أخرى
02
المسارات والطرق
→ Path params et query params→ POST, PUT, PATCH, DELETE+ 1 دروس أخرى
03
Pydantic والتحقق
→ BaseModel للطلبات→ Validators والقيود+ 2 دروس أخرى
04
حقن التبعيات
→ Depends - المبدأ→ التبعيات مع yield+ 1 دروس أخرى
05
قاعدة البيانات
→ SQLAlchemy مع FastAPI→ CRUD كامل+ 1 دروس أخرى
06
المصادقة JWT
→ OAuth2 + JWT→ الأدوار والأذونات+ 1 دروس أخرى
07
Async والأداء
→ async vs sync→ BackgroundTasks+ 1 دروس أخرى
08
الاختبارات
→ TestClient و pytest→ تجاوز التبعيات+ 1 دروس أخرى
🏁
المشروع النهائي (+ 2 فصول في الطريق)
→ Tu repars avec un projet concret et démontrable

لماذا FastAPI؟

NOTEالهدف — فهم ما يميز FastAPI عن أطر عمل Python الأخرى ومتى تختاره.

FastAPI في جملة واحدة

إطار عمل Python حديث لبناء واجهات برمجة تطبيقات REST سريعة، مكتوبة بالأنواع ومع توثيق تلقائي، مبني على Starlette (ASGI) وPydantic.

القوى الخارقة الأربع

القوةالتفاصيل
الأداءدعم Async أصلي عبر ASGI/uvicorn، مشابه لـ Node/Go
سلامة الأنواعتلميحات الأنواع في Python -> تحقق في وقت التشغيل عبر Pydantic
التوثيق التلقائيSwagger UI + ReDoc يُنشآن تلقائيًا
تجربة المطورإكمال تلقائي في IDE، أخطاء واضحة، تبعيات قابلة للحقن

مقارنة Flask vs FastAPI vs Django

المعيارFlaskFastAPIDjango
الأسلوبMicroMicro (تركيز على API)Full-stack
دعم Async أصليلا (إضافات)نعمجزئي (DRF لا)
تلميحات الأنواعلانعم (Pydantic)لا
التوثيق التلقائيلانعم (Swagger)عبر DRF (يدوي)
ORM مضمنلالانعم (Django ORM)
سهولة التعلمسهل جدًاسهلمتوسط
الأداء (req/s)~5k~20-30k~3k

مثال صغير: "Hello World"

output
# فلاسك
from flask import Flask
app = Flask(__name__)

@app.route("/items/<int:item_id>")
def get_item(item_id):
    return {"id": item_id}
output
# فاست إيه بي آي
from fastapi import FastAPI
app = FastAPI()

@app.get("/items/{item_id}")
def get_item(item_id: int):
    return {"id": item_id}
# + تحقق تلقائي، توثيق تلقائي، تلميحات الأنواع

متى تختار FastAPI؟

TIPنعم
  • خلفية API REST (خدمات مصغرة)
  • تقديم نموذج تعلم آلي
  • الحاجة إلى أداء (async)
  • فريق يقدر الكتابة بالأنواع
  • توثيق OpenAPI مطلوب
WARNINGلا
  • تطبيق full-stack مع قوالب HTML ثقيلة (Django)
  • سكريبت صغير لمرة واحدة (Flask يكفي)
  • فريق منتج بالفعل بإطار عمل آخر

من يستخدم FastAPI؟

النظام البيئي

المكونالدور
Starletteإطار ASGI الأساسي
Pydanticالتحقق والتسلسل
uvicornخادم ASGI (للتطوير)
gunicornمدير العمليات (للإنتاج)
SQLAlchemyORM (شائع)
Alembicترحيلات قاعدة البيانات

الملخص

NOTEللتذكر
  • FastAPI = أداء + سلامة الأنواع + توثيق تلقائي
  • مبني على Starlette (ASGI) وPydantic
  • مثالي لواجهات API والخدمات المصغرة الحديثة
  • دعم Async أصلي، أسرع بحوالي 5 مرات من Flask

الخطوة التالية: الجزء 2 — تثبيت FastAPI

النشر بـ Docker والاختبارات

المشروع النهائي • Docker • الاختبارات • CI/CD

NOTEالهدف — إنهاء المشروع: اختبارات pytest، Dockerfile، docker-compose، CI GitHub Actions.

اختبارات التكامل الرئيسية

output
# tests/test_posts.py
import pytest

def test_create_post_requires_auth(client):
    r = client.post("/posts", json={"title": "Test",
                                  "content": "Hello world"})
    assert r.status_code == 401

def test_create_post(client, auth_headers):
    r = client.post("/posts", headers=auth_headers, json={
        "title": "Mon premier post",
        "content": "Contenu interessant ici",
        "tags": ["python", "fastapi"],
        "published": True
    })
    assert r.status_code == 201
    data = r.json()
    assert data["slug"] == "mon-premier-post"
    assert len(data["tags"]) == 2

def test_list_posts_only_published(client, auth_headers):
    client.post("/posts", headers=auth_headers,
                json={"title": "Draft", "content": "...", "published": False})
    client.post("/posts", headers=auth_headers,
                json={"title": "Published", "content": "...", "published": True})
    
    r = client.get("/posts")
    assert len(r.json()["items"]) == 1

def test_only_author_can_delete(client):
    h1 = make_user_and_login(client, "alice", "alice@x.com")
    h2 = make_user_and_login(client, "bob", "bob@x.com")
    
    r = client.post("/posts", headers=h1,
                     json={"title": "Mien", "content": "...", "published": True})
    slug = r.json()["slug"]
    
    # يحاول Bob حذف منشور Alice
    r = client.delete(f"/posts/{slug}", headers=h2)
    assert r.status_code == 403
    
    # يمكن لـ Alice
    r = client.delete(f"/posts/{slug}", headers=h1)
    assert r.status_code == 204

conftest.py النهائي

output
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.db import Base, get_db

TestEngine = create_engine("sqlite:///./test.db",
                            connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=TestEngine)

def override_db():
    db = TestSession()
    try:
        yield db
    finally:
        db.close()

app.dependency_overrides[get_db] = override_db

@pytest.fixture(autouse=True)
def reset_db():
    Base.metadata.create_all(bind=TestEngine)
    yield
    Base.metadata.drop_all(bind=TestEngine)

@pytest.fixture
def client():
    return TestClient(app)

@pytest.fixture
def auth_headers(client):
    client.post("/auth/register", json={
        "email": "test@x.com", "username": "test",
        "password": "secret123"
    })
    r = client.post("/auth/login", data={
        "username": "test@x.com", "password": "secret123"
    })
    return {"Authorization": f'Bearer {r.json()["access_token"]}'}

Dockerfile

output
FROM python:3.11-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.11-slim
RUN useradd -m app
USER app
WORKDIR /home/app
ENV PATH=/home/app/.local/bin:$PATH

COPY --from=builder /root/.local /home/app/.local
COPY --chown=app:app . .

EXPOSE 8000
HEALTHCHECK CMD curl -fsS http://localhost:8000/health || exit 1

ENTRYPOINT ["./entrypoint.sh"]
CMD ["gunicorn", "-c", "gunicorn.conf.py", "app.main:app"]

docker-compose.yml

output
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://blog:pwd@db/blogdb
      SECRET_KEY: change-me-in-prod
      REFRESH_SECRET_KEY: change-me-too
    depends_on:
      db: {condition: service_healthy}
    restart: unless-stopped
  
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: blog
      POSTGRES_PASSWORD: pwd
      POSTGRES_DB: blogdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U blog"]
      interval: 5s
      retries: 5

volumes:
  pgdata:

CI/CD GitHub Actions

output
# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: {python-version: "3.11"}
      - run: pip install -r requirements.txt
      - run: pip install pytest pytest-cov httpx
      - run: pytest --cov=app --cov-report=xml
      - uses: codecov/codecov-action@v4
  
  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == "refs/heads/main"
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:latest

التشغيل في الإنتاج

output
# محلي كامل
docker-compose up -d
docker-compose logs -f api
docker-compose exec api alembic upgrade head

# زيارة
#   http://localhost:8000/docs
#   http://localhost:8000/redoc

WebSockets مع FastAPI

NOTEالهدف — بناء دردشة في الوقت الفعلي باستخدام WebSocket الأصلي في FastAPI.

Hello WebSocket

output
from fastapi import FastAPI, WebSocket, WebSocketDisconnect

app = FastAPI()

@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
    await ws.accept()
    try:
        while True:
            data = await ws.receive_text()
            await ws.send_text(f"Echo: {data}")
    except WebSocketDisconnect:
        print("Client deconnecte")

اختبار سريع باستخدام wscat

output
npm install -g wscat
wscat -c ws://localhost:8000/ws
> Hello
< Echo: Hello

Connection Manager

output
class ConnectionManager:
    def __init__(self):
        self.active: list[WebSocket] = []
    
    async def connect(self, ws: WebSocket):
        await ws.accept()
        self.active.append(ws)
    
    def disconnect(self, ws: WebSocket):
        self.active.remove(ws)
    
    async def broadcast(self, message: str):
        for connection in self.active:
            try:
                await connection.send_text(message)
            except:
                pass

manager = ConnectionManager()

بث الدردشة

output
@app.websocket("/chat/{username}")
async def chat(ws: WebSocket, username: str):
    await manager.connect(ws)
    await manager.broadcast(f">> {username} a rejoint")
    
    try:
        while True:
            msg = await ws.receive_text()
            await manager.broadcast(f"[{username}] {msg}")
    except WebSocketDisconnect:
        manager.disconnect(ws)
        await manager.broadcast(f"<< {username} a quitte")

إرسال JSON

output
@app.websocket("/json")
async def json_ws(ws: WebSocket):
    await ws.accept()
    try:
        while True:
            data = await ws.receive_json()
            await ws.send_json({"echo": data, "server_time": str(datetime.now())})
    except WebSocketDisconnect:
        pass

مصادقة WebSocket

output
from fastapi import WebSocketException, status

@app.websocket("/ws")
async def ws(ws: WebSocket, token: str):
    try:
        payload = decode_token(token)
        user_id = payload["sub"]
    except:
        raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
    
    await ws.accept()
    await ws.send_text(f"Connecte en tant que {user_id}")
    ...

# العميل: ws://localhost:8000/ws?token=eyJ...
va-plus-loin

يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة Python FastAPI (10 فصول، 32 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.

./acceder-au-cours-complet cours gratuit : Vibe Coding

الأسئلة الشائعة

كم من الوقت يستغرق تعلم Python FastAPI؟
مع تقدم منظم (10 فصول، 32 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في بضعة أسابيع بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو ممارسة كل مفهوم فورًا.
هل هناك متطلبات مسبقة؟
تكفي أساسيات الحاسوب. إذا كنت تعرف استخدام الطرفية وقراءة كود بسيط، فأنت جاهز.
من أين أبدأ عمليًا؟
أعد تنفيذ الأوامر في هذا المقال، ثم تابع الدورة الكاملة Python FastAPI: فهي تربط الـ 32 درسًا بالترتيب، مع تمارين ومشروع نهائي.

📬 هل تريد تلقي هذا النوع من الأدلة كل أسبوع؟ اشترك مجانًا — كود حقيقي، بدون كلام زائد.