1. ספרייה
  2. HTTP והרשת
  3. ממשקי API ושירותי ווב

עודכן לפני חודש

כל דף אינטרנט שביקרת בו אי פעם פעל באותו אופן: הקלדת כתובת URL, הדפדפן שלך ביקש אותה, ושרת שלח בחזרה ייצוג. קישורים הובילו אותך לכתובות URL אחרות. סימניות זכרו היכן נמצאו הדברים. כפתור החזרה עבד כי כל דף היה דבר נפרד בכתובת נפרדת.

REST היא ההכרה הזו: גם API צריך לעבוד כך.

הפילוסופיה

REST (Representational State Transfer) אינו פרוטוקול. זה לא תקן שאתה מממש. זו פילוסופיה ארכיטקטונית שאומרת: הדפוסים שהפכו את הרשת למה שהיא צריכים להפוך גם את ה-API שלך למוצלח.

Roy Fielding, שעזר לתכנן את HTTP עצמו, ניסח את REST בעבודת הדוקטורט שלו משנת 2000. אבל הוא לא המציא משהו חדש — הוא תיאר את מה שכבר עבד. הרשת הגיעה למיליארדי דפים בזכות בחירות ארכיטקטוניות ספציפיות. REST אומר: קבל אותן בחירות עבור ה-API שלך.

השם אומר הכל. כאשר אתה מבקש משאב, אתה מקבל ייצוג של המצב הנוכחי שלו, מועבר אליך. זה הכל. זה REST.

משאבים: דברים עם כתובות

משאב הוא כל דבר שאתה יכול לתת לו שם. משתמש. מסמך. עגלת קניות. תוצאת חיפוש. אם אתה יכול לדבר עליו, הוא יכול להיות משאב.

לכל משאב יש כתובת — URI:

/users/123           # משתמש ספציפי
/posts/456/comments  # תגובות על פוסט ספציפי
/products            # אוסף כל המוצרים

זה נראה מובן מאליו. כמובן שלדברים יש כתובות. אבל שים לב כמה מהר מפתחים נוטשים את זה כשהם בונים APIs. הם יוצרים נקודות קצה כמו /getUserProfile או /fetchOrderDetails — פעלים המתחזים לכתובות. REST אומר לא. פרופיל משתמש נמצא ב-/users/123. אתה לא מביא אותו — אתה מבקש אותו, בדיוק כפי שהדפדפן שלך מבקש כל כתובת URL.

משאבים הם שמות עצם. תמיד שמות עצם.

מתודות HTTP: הפעלים שכבר יש לך

ל-HTTP כבר יש פעלים. השתמש בהם.

GET מאחזר משאב. הוא לעולם לא משנה כלום. אתה יכול לקרוא לו אלף פעמים ולא קורה כלום מלבד שאתה מקבל את אותו דבר בחזרה. זה נקרא בטוח ואידמפוטנטי.

GET /users/123      # אחזר משתמש 123
GET /posts          # אחזר את כל הפוסטים

POST יוצר משהו חדש. קרא לו פעמיים, אולי תקבל שני דברים חדשים. הוא לא בטוח ולא אידמפוטנטי — הוא משנה את העולם.

POST /users         # צור משתמש חדש
POST /comments      # צור תגובה חדשה

PUT מחליף משאב במלואו. שלח את הגרסה החדשה המלאה. קרא לו חמש פעמים עם אותם נתונים, אתה מקבל את אותה תוצאה — אידמפוטנטי.

PUT /users/123      # החלף את משתמש 123 בנתונים האלה

PATCH מעדכן חלק ממשאב. רק את השדות שאתה משנה.

PATCH /users/123    # עדכן שדות ספציפיים

DELETE מסיר משאב. מחיקת משהו פעמיים? הוא נמחק בכל מקרה — אידמפוטנטי.

DELETE /users/123   # הסר משתמש 123

הרשת כבר ידעה לעשות זאת. REST פשוט אמר: עשה זאת בכוונה.

קודי סטטוס: המילה הראשונה של התגובה

לפני שאתה קורא את הגוף, קוד הסטטוס אומר לך מה קרה.

2xx — עבד:

  • 200 OK: הנה מה שביקשת
  • 201 Created: יצרתי את הדבר החדש שרצית
  • 204 No Content: סיימתי, אין מה להראות לך

4xx — עשית משהו לא נכון:

  • 400 Bad Request: אני לא מבין מה שלחת
  • 401 Unauthorized: מי אתה?
  • 403 Forbidden: אני יודע מי אתה. לא.
  • 404 Not Found: אין כלום בכתובת הזו
  • 409 Conflict: אי אפשר לעשות זאת — זה סותר את המצב הנוכחי
  • 429 Too Many Requests: האט

5xx — השרת קרס:

  • 500 Internal Server Error: משהו השתבש אצלנו
  • 502 Bad Gateway: משהו שמאחורינו קרס
  • 503 Service Unavailable: אנחנו עמוסים מדי או בתחזוקה

שימוש בקוד הסטטוס הנכון אינו פדנטיות. זו תקשורת. לקוח שמקבל 401 יודע לאמת מחדש. אחד שמקבל 429 יודע להאט. אחד שמקבל 404 יודע לא לנסות שוב.

האילוצים שגורמים לזה לעבוד

REST אינו רק "השתמש ב-HTTP בצורה נכונה." זו קבוצה של אילוצים ארכיטקטוניים שמאפשרים קנה מידה, אמינות ואבולוציה.

חוסר מצב (Statelessness): כל בקשה מכילה כל מה שהשרת צריך לעבד אותה. אין "סשן" על השרת שזוכר מי אתה בין קריאות. זה נשמע לא נוח — אתה צריך לשלוח את האישורים שלך בכל פעם. אבל זה מה שמאפשר לכל שרת לטפל בכל בקשה. אין צורך ב-sticky sessions. אין סיוטי סנכרון. ניתן להרחיב לרוחב ללא הגבלה.

הפרדת לקוח-שרת: הלקוח והשרת מתפתחים באופן עצמאי. כל עוד הממשק יציב, אתה יכול לכתוב מחדש כל צד לחלוטין. האפליקציה שלך ל-iOS, לאנדרואיד ולרשת כולן מדברות עם אותו API. השרת לא מתעניין איזה סוג לקוח אתה.

יכולת שמירה במטמון (Cacheability): תגובות מציינות אם ניתן לשמור אותן במטמון. כאשר ניתן, גורמים מתווכים (CDNs, מטמוני דפדפן, שרתי proxy) יכולים לשרת אותן מבלי לגעת בשרת שלך. כך הרשת מטפלת במיליארדי בקשות — רובן לעולם לא מגיעות למקור.

מערכת שכבות (Layered System): לקוחות לא יודעים אם הם מדברים עם השרת הסופי או עם גורם מתווך. זה מאפשר לך להוסיף מאזני עומסים, מטמונים, שכבות אבטחה ו-API gateways מבלי לשנות קוד לקוח.

ממשק אחיד (Uniform Interface): כולם מתקשרים עם משאבים באותה דרך. אתה לא צריך ידע מיוחד כדי להשתמש ב-REST API חדש — אתה כבר מכיר את הפעלים, אתה מבין URIs, אתה יכול לקרוא קודי סטטוס. הדפוסים ניתנים להעברה.

אוספים ויחידים

URIs מבחינים בין אוספים (קבוצות של משאבים) ומשאבים בודדים.

GET /users          # אוסף המשתמשים
GET /users/123      # משתמש ספציפי

POST /users         # הוסף לאוסף (צור משתמש)
DELETE /users/123   # הסר מהאוסף (מחק משתמש)

משאבים מקוננים מראים קשרים:

GET /users/123/posts      # פוסטים של משתמש 123
GET /posts/456/comments   # תגובות על פוסט 456

אבל אל תקנן עמוק מדי. /users/123/posts/456/comments/789/likes הוא מסורבל. אם משאב יכול לעמוד בפני עצמו, תן לו כתובת ברמה עליונה.

פרמטרי שאילתה: עידון אוספים

פרמטרי שאילתה מסננים, ממיינים ומחלקים לדפים אוספים מבלי ליצור נקודות קצה מיוחדות אינסופיות.

GET /users?role=admin              # סנן לפי תפקיד
GET /posts?sort=-created_at        # מיין בסדר יורד לפי תאריך
GET /users?page=2&per_page=20      # עימוד
GET /users?fields=id,name,email    # בחר שדות ספציפיים

משאב האוסף נשאר אותו דבר. פרמטרי השאילתה מדייקים מה שאתה מבקש.

מבנה הבקשה והתגובה

תקשורת REST טיפוסית:

בקשה:

POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer token123

{
  "name": "Jane Smith",
  "email": "jane@example.com"
}

תגובה:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/789

{
  "id": "789",
  "name": "Jane Smith",
  "email": "jane@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}

שים לב: 201 Created (לא 200). כותרת Location אומרת לך היכן נמצא המשאב החדש. התגובה כוללת את המשאב שנוצר עם ה-ID שהשרת הקצה לו. הכל מתקשר.

למה אידמפוטנטיות חשובה

במערכות מבוזרות, בקשות נכשלות. רשתות מפילות מנות. לקוחות מנסים שוב. מה קורה כאשר הניסיון החוזר מצליח אבל המקורי גם הצליח?

עם פעולות אידמפוטנטיות, כלום רע לא קורה:

  • GET את אותו משאב פעמיים? אתה מקבל את אותו דבר.
  • DELETE את אותו משאב פעמיים? הוא נמחק (או כבר היה).
  • PUT את אותם נתונים פעמיים? אותה תוצאה.

עם POST, אתה עלול ליצור כפילויות. לכן תכנון API זהיר משתמש במפתחות אידמפוטנטיים — מזהה ייחודי שהלקוח שולח כדי שהשרת יוכל לזהות ניסיונות חוזרים.

הבנת אידמפוטנטיות אינה עיון תיאורטי. כך בונים מערכות אמינות.

מה הולך לא נכון

GET עם תופעות לוואי. לעולם לא. GET חייב להיות בטוח. אם הדפדפן של מישהו מבצע prefetch לקישורים, אם סורק מאנדקס את ה-API שלך, אם מטמון מגיש תגובה ישנה — GET לא יכול לשנות דבר.

200 OK עם שגיאה בגוף. קוד הסטטוס הוא עבור מכונות. אם אתה מחזיר 200 עם {"error": "User not found"}, לקוחות שבודקים קודי סטטוס חושבים שהכל עבד.

פעלים בכתובות URL. /createUser, /deletePost, /updateOrder — יצרת מחדש RPC עם שלבים נוספים. תן למתודות HTTP להיות הפעלים שלך.

חוסר עקביות במוסכמות. snake_case כאן, camelCase שם. /user יחיד, /products רבים. בחר מוסכמות ודבוק בהן.

שינויים שוברים ללא גרסאות. כאשר אתה משנה מה נקודת קצה מחזירה, אתה שובר כל לקוח. תן גרסאות ל-API שלך מהיום הראשון.

מלכודת הפשטות

REST נראה פשוט. משאבים, פעלים, קודי סטטוס — כל אחד יכול להבין זאת בשעות ספורות.

המלכודת היא לחשוב שהבנה פירושה עקיבה. מפתחים ממציאים מחדש RPC שוב ושוב, יוצרים נקודות קצה כמו /api/performAction כי זה נראה ישיר יותר מאשר מודלינג של משאבים. הם מתעלמים מקודי סטטוס כי בדיקתם היא עבודה נוספת. הם מדלגים על גרסאות כי ה-API הוא "פנימי".

האילוצים של REST קלים להבנה וקשים לעקיבה. המשמעת היא הנקודה. הרשת הגיעה למיליארדים כי האילוצים האלה יוצרים מערכות שמתחברות, נשמרות במטמון ומתפתחות. ה-API שלך יכול לרשת את המורשת הזו — אם אתה אכן עוקב אחר האילוצים.

שאלות נפוצות על REST APIs

מה ההבדל בין REST ל-RESTful?

REST הוא הסגנון הארכיטקטוני. "RESTful" מתאר API שעוקב אחר עקרונות REST. בפועל, מעט APIs הם RESTful לחלוטין — רובם מממשים את החלקים שהם צריכים ומתעלמים מאילוצים כמו HATEOAS. המונח "REST API" הפך לקיצור של "HTTP API שמשתמש ב-JSON", בין אם הוא עוקב אחר עקרונות REST בקפדנות ובין אם לאו.

מתי להשתמש ב-PUT לעומת PATCH?

PUT מחליף את המשאב כולו. שלח את הגרסה החדשה המלאה — שדות שאינך כולל עלולים להימחק או לאפס לערכי ברירת המחדל. PATCH מעדכן רק את השדות שאתה מציין. השתמש ב-PUT כאשר ללקוחות יש תמיד את המשאב המלא. השתמש ב-PATCH כאשר אתה מעדכן כמה שדות בלבד במשאב גדול.

כיצד לטפל באימות ב-REST?

חוסר מצב אומר שכל בקשה כוללת אישורים. בדרך כלל זה אומר כותרת Authorization עם Bearer token (JWT או OAuth access token). השרת מאמת את האסימון בכל בקשה מבלי לאחסן מצב סשן. מפתחות API בכותרות או בפרמטרי שאילתה הם גישה נוספת, אם כי פחות מאובטחת.

האם להשתמש בשמות עצם ביחיד או ברבים עבור משאבים?

רבים. /users עבור האוסף, /users/123 עבור יחיד. יחיד יוצר אי-עקביות מביכה — האם /user הוא האוסף או משתמש ספציפי? רבים ברור יותר ונפוץ יותר.

כיצד לתת גרסאות ל-REST API שלי?

גרסאות URL (/v1/users, /v2/users) הן הנפוצות ביותר והנראות ביותר. גרסאות כותרת (Accept: application/vnd.api.v2+json) שומרות על כתובות URL נקיות אבל קשות יותר לבדיקה בדפדפן. גרסאות פרמטר שאילתה (/users?version=2) עובדות אבל מזהמות את כתובת המשאב. בחר אחת והיה עקבי.

האם דף זה היה מועיל?

😔
🤨
😃
יסודות REST API • ספרייה • Connected