1.1 fastapi + postman הרצאה
פיתוח api¶
בבניית api עלינו להכיר מספר מושגים בסייסים
- הrouteים: הגדרה של כל הפניות האפשריות שניתן לבצע לapi שלנו- לדוגמה,
בקשת post ל api/comment/
בקשת get ל api/products/
- הschmaות: סכמות הן בדרך כלל מחלקות שמגדירות בדיוק איזה קלט המשתמש צריך להעביר לroute מסוים כדי שהroute יוכל לפעול כראוי. (נניח איזה סוג מידע route אמור לקבל בpost כדי לפעול כראוי)
- הcontrollerים: הקוד שמבצע מאחורי הקלעים פעולות אל מול מסד הנתונים- בדרך כלל נראה שהקוד של הrouteים פונה לקוד של הcontrollerים כדי לבצע פעולות אל מול מסדי הנתונים.
- הmodelים: קוד שמגדיר את כל סוגי האובייקטים/טבלאות שנמצאות במסד הנתונים, המודלים מאפשרים לנו בצורה נכונה לגשת למסד הנתונים.

פירוט של התהליך של בקשת API עד לתשובה מפורק לשלבים (הסתכלו על התמונה)
נניח שהבקשה היא בקשת GET /api/hotels ומטרתה להציג למשתמש את כל המלונות שבמערכת.
1. הAPI מקבל בקשת http מהלקוח GET /api/hotels ומעביר אותה ישירות לroute שאחראי לטפל בסוג בקשה זו.
2. הroute בודק שתוכן הבקשה תקין על פי הschma שהוגדרה לו (במקרה הזה מדובר בבקשת GET בלי תוכן, אז אין מה לבדוק)
3. הroute מבקש מהcontroller שאחראי על המלונות להביא רשימה של כל המלונות.
4. הcontroller ניגש לmodel של מלון במסד הנתונים, ועושה שליפה על כל המלונות דרך המודל.
5. הmodel מדבר עם מסד הנתונים ומביא את כל המידע.
6. הmodel מחזיר את המידע לcontroller שמחזיר לroute שמחזיר למשתמש בתשובת http תוכן json עם רשימה של כל המלונות.
כדי לבנות api אפשר להשתמש בכל שפת תכנות שנרצה, אלמד בהרצאה הבאה כיצד לפתח api עם שפת פייתון באמצעות ספריית fastapi.
בניית API עם FastAPI¶
כדי להתחיל, נתקין 3 סיפריות.
- הספרייה fastapi - ספרייה שמאפשרת לנו לבנות api
- הספרייה uvicorn - ספרייה שמאפשרת לנו להריץ שרת http עם fastapi
- הספרייה pydantic - ספרייה שמאפשרת לנו להגדיר modelים וschemaות בצורה נוחה
יצירת הקובץ הראשי¶
ניצור קובץ בשם main.py ונכתוב בו את הקוד הבסיסי הבא:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def home():
return {"message": "ברוך הבא ל-API הראשון שלך!"}
הקוד הזה יוצר אפליקציית FastAPI ומגדיר route אחד – הבקשה GET / מחזירה הודעת JSON.
הרצת השרת¶
נריץ את הפקודה:
זה ירים לנו את שרת הbackend על בהאזנה על פורט 8000 על המחשב שלנו.
לאחר מכן נוכל לגשת לדפדפן ולכתוב:
אם תשימו לב, אנחנו ניגשים לכתובת אייפי 127.0.0.1, זה placeholder לכתובת של המחשב שלנו עצמו.
ובגלל שהשרת רץ על המחשב שלנו, אז כדי לפנות אילו עם הדפדפן שלנו נצטרך לפנות לכתובת הזו.
ונראה את התגובה:
תיעוד אוטומטי (Swagger)¶
חלק מההיתרונות של FastAPI היא היצירה האוטומטית של ממשק גרפי שמציג את כל ה־Routes שלכם בצורה אינטראקטיבית.
כדי לראות את זה, ניגש לכתובת:
נראה דף תיעוד אינטראקטיבי שמאפשר להריץ בקשות ישירות מהדפדפן!
יצירת route נוסף¶
נוסיף עוד route שמחזיר מידע על משתמש לפי מזהה (ID):
@app.get("/user/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id, "name": "Amit", "role": "admin"}
אם נגלוש לכתובת:
נקבל:
ולעומת זאת אם נגלוש ל-
נקבל:
שליחת נתונים עם POST¶
עם בקשות post נוכל לקבל מידע מהלקוח (למשל טופס יצירת משתמש).
נשתמש במודול BaseModel של pydantic כדי להגדיר את הסכמות (הקלט שאנחנו נקבל מהמשתמש לroute):
from pydantic import BaseModel
class User(BaseModel):
name: str
email: str
age: int
@app.post("/user")
def create_user(user: User):
return {"message": "משתמש נוצר בהצלחה", "user": user}
כאשר User היא הסכמה שיצרנו. כעט הroute שלנו (/user) יודע לקבל רק מידע מוגדר לפי הסכמה.
נוכל לבצע את בקשת הpost באמצעות הכלי postman.
https://www.postman.com/downloads/
אחרי שתסיימו להתקין, פשוט תשימו את הip של השרת (במקרה הזה זה localhost כי אנחנו מריצים את השרת על המחשב שלנו ולא על שרת מרוחק) והפורט שבחרנו (במקרה הזה 8000), אחרכך נוכל לבחור את הנתיב (במקרה הזה /user) ואז בגוף הבקשה, תוכלו לשים את הjson של הuser שנרצה לשלוח לapi.
אם נשלח בקשה מסוג POST לכתובת /user עם גוף הבקשה:
נקבל:
{
"message": "משתמש נוצר בהצלחה",
"user": {
"name": "Shir",
"email": "shir@example.com",
"age": 21
}
}
באמצעות המחלקה user שיצרנו, קבענו סכמה שנשתמש בה כדי לוודא שאנחנו מקבלים קלט תקין מהמשתמש בapi שלנו.
חיבור FastAPI למסד נתונים¶
נשתמש ב־SQLAlchemy כדי לעבוד מול מסד נתונים בצורה נוחה, בלי לכתוב SQL ידנית. (עבודה עם orm (כמו שלמדנו בקורס תכנות בסיסי))
נתחיל מהתקנת הספרייה:
יצירת קובץ חיבור למסד הנתונים¶
ניצור תיקייה בשם database/ ובתוכה קובץ בשם connection.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
כעת יצרנו מנגנון שמתחבר למסד נתונים מקומי בשם
test.db. מסוג SQLite
יצירת מודל (Model)¶
ניצור תיקייה בשם models/ ובתוכה קובץ בשם user_model.py:
from sqlalchemy import Column, Integer, String
from database.connection import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
email = Column(String)
age = Column(Integer)
זהו מודל שמייצג טבלת משתמשים עם עמודות: מזהה, שם, אימייל וגיל.
בעתיד נוכל להשתמש במודל הזה כדי לגשת למידע ממסד הנתונים בטבלה של Users
מבנה מקצועי של פרויקט FastAPI¶
נבנה את המבנה הבא:
project/
├── main.py
├── database/
│ └── connection.py
├── models/
│ └── user_model.py
├── controllers/
│ └── users_controller.py
└── routes/
└── users.py
controller – הלוגיקה של המשתמשים¶
בcontroller אנחנו נשתמש בmodel שבנינו כדי לממש 2 פונקציות, פונקציה שאחראית לשלוף על כל הuser-ים בטבלה, ופונקציה שאחראית להוסיף user לטבלה.
controllers/users_controller.py:
from sqlalchemy.orm import Session
from models.user_model import User
def get_users(db: Session):
return db.query(User).all()
def create_user(db: Session, name: str, email: str, age: int):
user = User(name=name, email=email, age=age)
db.add(user)
db.commit()
db.refresh(user)
return user
למעשה כתבנו controller שיודע לבצע פעולות במסד הנתונים באמצעות הmodel-ים בהנחה והוא מקבל Session (אובייקט שמהווה חיבור לDB).
הrouteים של הapi¶
לפני שאנחנו כותבים את הRoute-ים אנחנו צריכים ליצור חיבור למסד הנתונים ונעשה זאת ככה:
עלינו לוודא שבכל route שלנו, הקוד שלנו באמת יוצר חיבור עם מסד הנתונים (שלא תהיה שגיאה), בfastapi יש פונקציה מובנית בשם 'depends' שמטפלת בדיוק בזה.
כפרמטר לroute-ים נוכל להוסיף שהroute תלוי (באמצעות depends) בתוצאה של פונקציה מסוימת.
ככה אנחנו יכולים לוודא שבroute יהיה לנו גישה לאובייקט db, ושבלי תוצאה מget_db הפונקציה תקרוס.
הנה הקובץ route.
routes/users.py:
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel
from database.connection import SessionLocal
from controllers.users_controller import get_users, create_user
router = APIRouter()
class UserSchema(BaseModel):
name: str
email: str
age: int
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.get("/users")
def read_users(db: Session = Depends(get_db)):
return get_users(db)
@router.post("/users")
def add_user(user: UserSchema, db: Session = Depends(get_db)):
existing = db.query(create_user.__annotations__['db'].query(UserSchema)).filter_by(email=user.email).first()
if existing:
raise HTTPException(status_code=400, detail="משתמש עם אימייל זה כבר קיים")
return create_user(db, user.name, user.email, user.age)
למעשה הקוד בסוף רק מגדיר את המשתנה router, שיש לו את כל הroute-ים שלנו בפרויקט.
main.py¶
from fastapi import FastAPI
from database.connection import Base, engine
from models.user_model import User
from routes import users
Base.metadata.create_all(bind=engine)
app = FastAPI()
app.include_router(users.router)
בקובץ main אנחנו קוראים ל
Base.metadata.create_all(bind=engine) כדי ליצור את כל הטבלאות אם הם לא נוצרו עדייןואנחנו מאתחלים אובייקט של
FastAPI ומביאים לו את הrouter שבנינו עם כל הroute-ים.
ככה בנינו פרויקט, שבו אנחנו מאתחלים מסד נתונים + כל הroute-ים של הapi.
וכאשר כל ניתוב מבצע פעולות שמוגדרות בcontroller-ים
כאשר הפונקציות של הcontroller-ים, משתמשות במודולים כדי לגשת למסד נתונים.
זה העיצוב הנהוג לכתיבת api מודרני.
טיפול בשגיאות (Error Handling)¶
ב־FastAPI אפשר לטפל בשגיאות בצורה נוחה בעזרת HTTPException.
דוגמה:
from fastapi import HTTPException
@app.get("/product/{id}")
def get_product(id: int):
product = None # נניח שהמוצר לא נמצא
if not product:
raise HTTPException(status_code=404, detail="המוצר לא נמצא")
return product
כמו כן, ניתן להגדיר global error handler – שיתפוס כל שגיאה במערכת ויחזיר תשובה יפה למשתמש: