מבוא ל-FastAPI: למה לבחור אותו?
FastAPI הוא מסגרת פיתוח API מודרנית לבסיס Python, שנוצרה על ידי סבסטיאן רמז. היא מתמקדת ביצירת API מהירים, בטוחים וקלים לתיעוד — תוך שימוש במתקנים מובנים של Python 3.6+ כגון סוגי טיפוסים, Pydantic ואובייקטים של איחוד (Union).
מהירות יוצאת דופן
FastAPI נבנתה על בסיס Starlette (למוניטורינג ו-ASGI) ו-Pydantic (לאימות). היא אחת מהמסגרות המהירות ביותר בעולם Python — מהירה פי 2–3 מ-Flask או Django REST Framework בבדיקות עומסים.
תיעוד אוטומטי מלא
בזמן שמריצים את ה-API, FastAPI מייצרת אוטומטית שני סוגי תיעוד:
- Swagger UI בכתובת
/docs - ReDoc בכתובת
/redoc
התקנה והגדרת פרויקט ראשון
נתחיל בהתקנת FastAPI יחד עם השרת המובנה שלה — Uvicorn. אין צורך בשרת נפרד כמו Gunicorn או uWSGI בשלב ההתחלתי.
# יצירת סביבת וירטואלית (מומלץ)
python -m venv fastapi-env
source fastapi-env/bin/activate # ב-Mac/Linux
# או ב-Windows:
# fastapi-env\Scripts\activate
# התקנת החבילות
pip install fastapi uvicorn[standard]
ניצור קובץ בשם main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "ברוך הבא ל-API שלך עם FastAPI!"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
נריץ את השרת:
# מריץ את השרת באישור localhost:8000
uvicorn main:app --reload
כעת ניתן לגשת ל:
- ה-API עצמו: http://localhost:8000
- תיעוד Swagger: http://localhost:8000/docs
- תיעוד ReDoc: http://localhost:8000/redoc
עיצוב מבנה פרויקט מקצועי
כשמפתחים פרויקט אמיתי, חשוב להפריד בין הקבצים כדי לשמור על ניווט קל, התאמה לעתיד ויכולת тестирование. הנה מבנה מומלץ לפרויקט גדול:
my_fastapi_project/
├── app/
│ ├── __init__.py
│ ├── main.py # נקודת הכניסה
│ ├── core/ # הגדרות כלליות
│ │ ├── config.py # משתנים סביבתיים
│ │ └── security.py # אימות ואבטחה
│ ├── models/ # מודלים של Pydantic
│ │ ├── user.py
│ │ └── item.py
│ ├── schemas/ # סכמות לקלט/פלט (לרוב זהה ל-Models, אבל אפשר להרחיב)
│ ├── api/ # מסילות (routes)
│ │ ├── __init__.py
│ │ ├── v1/
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── items.py
│ ├── db/ # ניהול חיבור למסד נתונים
│ │ ├── base.py
│ │ └── session.py
│ └── dependencies.py # תלויות משותפות (למשל: get_db)
├── alembic/ # migraции ל-SQLAlchemy
├── tests/ # בדיקות
├── requirements.txt
└── README.md
אימות ומודלים עם Pydantic
אחד מהיתרונות הגדולים של FastAPI הוא השימוש האוטומטי ב-Pydantic לאימות ולתיעוד. כל מודל שמתאר נתוני כניסה או יציאה מוגדר כ-class שמכיל את השדות וההגבלות שלו.
# app/models/user.py
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
class UserBase(BaseModel):
email: EmailStr
full_name: Optional[str] = None
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
class UserOut(UserBase):
id: int
is_active: bool
class Config:
orm_mode = True # מאפשר להשתמש במודל עם SQLAlchemy ORM
כעת נוכל להשתמש בו ברoutes:
# app/api/v1/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.models.user import UserCreate, UserOut
from app.core.security import get_password_hash
router = APIRouter()
@router.post("/users/", response_model=UserOut, status_code=status.HTTP_201_CREATED)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
# בדיקה אם משתמש כבר קיים
existing_user = db.query(User).filter(User.email == user.email).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="משתמש עם כתובת אימייל זו כבר קיים"
)
# יצירת משתמש חדש
hashed_password = get_password_hash(user.password)
db_user = User(email=user.email, full_name=user.full_name, hashed_password=hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
המערכת תבצע אוטומטית:
- אימות טיפוסים (למשל: האם
emailתקין? האםpasswordארוך מספיק?) - ייצוא JSON עם תיאור מדויק ב-OpenAPI Schema
- תיעוד באוטומטי ב-
/docs - תגובה שגיאה אוטומטית במקרה של הפרה (כולל הודעת שגיאה מפורקת)
חיבור למסד נתונים עם SQLAlchemy
לרוב הפרויקטים נזדקקים למסד נתונים. FastAPI עובדת מצוין עם SQLAlchemy (ORM) ובאופן אסינכרוני גם עם asyncpg או aiomysql. כאן נציג גישה סינכרונית — פשוטה יותר לתחילת הדרך.
# app/db/base.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
engine = create_engine(
settings.DATABASE_URL,
pool_pre_ping=True,
echo=settings.DEBUG # רק בפיתוח
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# app/db/session.py
from app.db.base import SessionLocal, Base
from app.db.base import engine
def init_db():
Base.metadata.create_all(bind=engine)
# תלות לשימוש בכל route
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
הגדרת משתנים סביבתיים
ניצור קובץ .env בשרש הפרויקט:
DATABASE_URL=postgresql://user:password@localhost:5432/fastapi_db
DEBUG=True
SECRET_KEY=your-super-secret-key-change-in-production
והגדרת config.py:
# app/core/config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
DATABASE_URL: str
DEBUG: bool = False
SECRET_KEY: str
class Config:
env_file = ".env"
settings = Settings()
אבטחה ואימות משתמשים
אבטחה היא קריטית בכל API. FastAPI מספקת תמיכה מובנית באימות באמצעות JWT (JSON Web Tokens) ו-OAuth2PasswordBearer.
# app/core/security.py
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from app.core.config import settings
from app.db.session import get_db
from app.models.user import UserOut
from sqlalchemy.orm import Session
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(hours=24)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm="HS256")
return encoded_jwt
ועכשיו נוסיף נקודת כניסה לאימות:
# ב-main.py או ב-api/v1/auth.py
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.core.security import authenticate_user, create_access_token
from app.models.user import UserOut
router = APIRouter()
@router.post("/token", response_model=dict)
def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db)
):
user = authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="שם משתמש או סיסמה שגויים",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=30)
access_token = create_access_token(
data={"sub": user.email}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
OAuth2PasswordRequestForm ולא בקבלת username ו-password כ-JSON. זה מתאים לסטנדרט OAuth2, מתועד אוטומטית, ומאפשר ללקוחות להשתמש בכלי כמו Postman או Swagger ללא בעיות.
תהליך triểnת פרודקשן: Docker + NGINX + Uvicorn
לפני שה-API עולה לפרודקשן, יש לוודא שהיא מוגנת, מסונכרנת, ויכולה להתמודד עם עומסים. הנה תהליך triểnת מלא ומוכח.
1. קובץ Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0:8000", "--port", "8000", "--workers", "4", "--reload"]
2. קובץ docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/fastapi_db
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
restart: unless-stopped
db:
image: postgres:15
environment:
- POSTGRES_DB=fastapi_db
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data/
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- web
volumes:
postgres_data:
3. קובץ nginx.conf פשוט
events {
worker_connections 1024;
}
http {
upstream fastapi_app {
server web:8000;
}
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://fastapi_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /docs {
proxy_pass http://fastapi_app/docs;
}
location /redoc {
proxy_pass http://fastapi_app/redoc;
}
}
}
השוואה: סביבת פיתוח לעומת פרודקשן
| תכונה | סביבת פיתוח | סביבת פרודקשן |
|---|---|---|
| שרת | Uvicorn עם --reload |
Uvicorn עם מספר workers, ללא reload |
| בסיס נתונים | SQLite או PostgreSQL מקומי | PostgreSQL עם עותקים, backup, connection pooling |
| אבטחה | HTTPS לא נדרש, סיסמאות פשוטות | HTTPS חובה, JWT עם זמן תפוגה קצר, rate limiting |
| תיעוד | פעיל ב-/docs ו-/redoc |
מושבת או מוגבל לגישה פנימית בלבד |
| ניטור | אין או Consul/Loguru בסיסי | Prometheus + Grafana, Sentry, ELK Stack |
/docs לציבור הרחב. ניתן להשבית אותו לחלוטין על ידי הגדרת docs_url=None ב-FastAPI(), או לשלוט בגישה באמצעות NGINX או middlware. זה מפחית סיכונים של סריקות אוטומטיות וחשיפות מבנה הפנים של ה-API.