על המגבלות של REST – בשירות מערכות ווב בנות-זמננו

כבר כתבתי על REST בבלוג: ב 2011 (בהרבה התלהבות) וגם ב 2016 (במתינות) – מה ששיקף תהליך אישי של התלהבות רבה מ REST, והתפכחות לאורך הזמן.

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

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

הובילו אותי לפוסט הנוכחי, ולהציג היבטים נוספים של תקשורת ו REST – והאופן שאני מציג אותם כיום.

על אלו מגבלות אנחנו מדברים? הרי REST הוא כמעט-מושלם, לא?

למי שלא זוכר / לא היה בתעשייה בשנות ה-2000 אזכיר בקצרה את ההיסטוריה:
בתחילת שנות ה-2000, כאשר התברר לתעשיית התוכנה שמערכות תוכנה הולכות ונעשות מורכבות יותר, ויותר – ולכן רבות מהן הופכות למערכות מבוזרות (יותר משרת אחד) – ומכאן צמח רעיון ה Service-Oriented-Architecture (בקיצור: SOA) לארגון של מערכות מבוזרות שכאלו. מייקרוסופט הציעה את פרוטוקול ה SOAP (לשעבר XML-RPC) כבסיס לתקשורת בסוג הארכיטקטורה הזו, מה שהתקבל במהרה ע"י יבמ, ושאר חברותיה.

החברות שהובילו אז את התעשייה (IBM, HPE, Microsoft, וכו') קידמו את רעיונות ה-SOA לכדי מציאות, ועבדו על Suite של פרוטוקולים בשם *-WS שיפתרו את כל הבעיות התקשורת והאינטגרציה של מערכות Enterprise (מה שהיה מעניין באותה תקופה).

סט הפרוטוקולים המרכיבים את *-WS

בחבילת הפרוטוקולים של *-WS היה כמעט הכל, לקחו בחשבון כמעט כל צורך של Enterprise Software, מלבד אחד חשוב מאוד: פשטות.

לא כולם אהבו את SOAP ו *-WS, אחד מהם היה רוי פילדינג אחד ממגדירי התקן של HTTP. בעיניו – HTTP היה "הפרוטוקול של האינטרנט" ועל אף ש HTTP נועד להגשת "דפי ווב", הוא חשב שפרוטוקול אפליקטיבי צריך להשתלב בצורה הרמונית (אולי "טבעית") עם HTTP וה Web Standards הקיימים, ולא להתעלם ולהחליף אותם. זו הייתה מן גישה מוקדמת של "קיומיות וובית" – שהייתה חדשה ומרעננת באותה התקופה. היום קוראים לזה "Web-native".

פילדינג פרסם באותה תקופה עבודת דוקטורט, שבה פרק מס' 5 (המפורסם!) הציג סגנון ארכיטקטוני בשם Representational State Transfer (בקיצור: REST) למערכות ווב, ארכיטקטורה שמשתלבת באופן מאוד טבעי עם ה World Wide Web ו HTTP.

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

תוצר לוואי של "ארכיטקטורת ה REST", כפי שהוגדרה בעבודת הדוקטורט של פילדינג, היה צורת התקשורת בין חלקי המערכת, מה שנקרא כיום RESTful APIs. כלומר: צורת תקשורת של העברת הודעות JSON (במקור: XML) על גבי HTTP תוך השתלבות הרמונית באופיו והתנהגותו של HTTP והווב. ל HTTP יש מנגנונים כגון Caches, ו resource naming (להלן ה URL) הנכונים גם לתקשורת אפליקטיבית – שאם משתמשים ב HTTP, לא צריך לפתח מחדש. העולם מלא בכלים (למשל: דפדפן, Firewalls, Postman, swagger) שעובדים כבר עם HTTP, והמבנים של HTTP (כלומר: URL, Status Codes, Methods, Headers, Cookies) – מוכרים היטב* בתעשייה, ומקלים על אימוץ הטכנולוגיה.


* האמירה ש"כולם מכירים HTTP" היא סופר-מקובלת בתעשייה, אבל פה אני צריך להשתעל קצת – ולהתוודות שהיא לא לגמרי נכונה. גם כיום, בעידן הידע, ה Geektime, ושנים לאחר ש RESTful APIs הפכו לסטנדרט הלא-מעורער של התעשייה – אני נתקל בחוסרים גדולים של מהנדסי תוכנה בהבנה בסיסית של פרוטוקול ה HTTP. נכון יותר לומר ש HTTP מתועד היטב ויש עליו אינסוף חומר ו References – ולכן הוא נגיש יותר מפרוטוקולים propritary אחרים.

חזרה לתחילת שנות ה-2000: חברות האינטרנט שאז היו עוד צעירות: אמזון, יאהו, גוגל ועוד – אימצו בהתלהבות את REST (או ליתר דיוק: את ה RESTFul APIs) ושינו את מהלך ההיסטוריה. SOA, עם כל התכנון ההנדסי העמוק שנעשה מסביבו, ולמרות הגיבוי של חברות הענק – נקבר בעמודי ההיסטוריה, ו REST הפך לדרך התקשורת הנפוצה והמקובלת במערכות ווב בעשור וחצי האחרונים.

להיכן ממשיכים מכאן?

REST איננו תקן (אלא פרק בעבודת דוקטורט) והפרשנות ל RESTFul APIs אף יותר מזה – היא פרשנות, שכל אחד יכול לקחת למקום שונה. עם הזמן צצה הביקורת על "גנבי-הדעת" שפשוט מעבירים XML על גבי HTTP וקוראים למה שעשו "RESTful API" – מבלי באמת לכבד את פרוטוקול ה HTTP ו/או להשתלב בעקרונות הווב המקובלים (resources, openess, linking). מודל הביקורת שתפס יותר מכל להערכת "יישום RESTful APIs" הוא Richardson's Maturity Model (בקיצור RMM):

הפסים שאתם רואים בשכבה הנמוכה הם בוץ, רפש – שדובקים בכל מי שמסתפק ב "XML/JSON over HTTP" ועוד קורא לזה REST.
גם אני, כשהייתי עוד REST Evangelist – כעסתי על כל מי שנוהג כך. מה, אין כבוד?

ע"פ מודל ה RMM, לשלוח XML או JSON על גבי HTTP הוא נקודות האפס הבסיסית ביותר (והמוערכת הכי פחות). משם אפשר להתקדם לעיצוב "נכון" של URLs, שימוש "נכון" ב HTTP Verbs, ועד הגביע הקדוש של Hypermedia transformations. השתלבות המערכת עם "טבע ה HTTP והווב".

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

שאלה דומה היא שאלת ההשתלבות ב HTTP/Web ומידת ההצמדות לרעיון ה REST: יישום של RESTful APIs ברמה 3 לפי ה RMM אולי תעניק לכם חיים בעולם הבא בקרב "קהילת נאמני ה REST" – אך כנראה גם תתברר כעבודה הנדסית בינונית עד איומה, ברוב המקרים. למשל: רמה 3 טוענת שכל קריאה לשרת צריכה לבוא מתוך "גלישה טבעית בווב". באתר ווב אני מתחיל מאתר הבית – ובסדרה של קישורים מגיע לדף בו אני מעוניין. באופן דומה, הטענה היא ששרתים שמדברים זה מזה – צריכים להתקדם למטרה בצעדים. לא לשאול: "כמה כסף יש בחשבון של לקוח X?" אלא תמיד יש להתחיל בשאלה "מי אתה? אלו לקוחות יש לך? איזה מידע יש לכל לקוח? האא… ורק אז לשאול: אז כמה כסף יש בחשבון של לקוח X?".

אם יש לנו שרתים "קרובים" (אותה מערכת) שמתקשרים הרבה, ההבדל בין 4 קריאות (3 "נימוסים" + 1 תכל'ס) על כל בקשה מול קריאה בודדת (רק תכל'ס) – היא משמעותית. רק במקום עבודה אחד בחיים ראיתי יישום של Hypermedia transformations – ואולי עשינו HATEOAS, אבל סיבכנו מאוד את המערכת. ממש ברמת העוול / הטיפשות.

מכאן עולה השאלה המתבקשת: "כמה RESTFulness" כדאי לעשות? ואיך יודעים לאמוד מתי ההצמדות ל RESTfulness היא תורמת, ומתי היא כבר מזיקה?

אז מהן המגבלות העיקריות של RESTFul APIs?

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

Hypermedia Transformations / HATEOAS – ברוב המקרים, מבחינה הנדסית – זה פשוט טירוף לשמו. יש להימנע – וחבל להמשיך ולעסוק בעניין.

HTTP Verbs הם שפה מגוונת ועשירה למערכות פורומים / Wikis – אך דלה ומוגבלת עבור מערכות ווב מודרניות ומורכבות. הנה ההגדרות מתוך וויקיפדיה:

והנה כמה מגבלות יום-יומיות ,שכנראה שכולנו נתקלים בהן:

  • קריאת GET איננה כוללת Body. כאשר אנחנו רוצים לעשות קריאה שהיא safe (לא משנה את state המערכת), אך רוצים להעביר פרטים מגוונים של הבקשה – אנחנו חסומים לשימוש ב URL לבדו. זו נשמעת מגבלה קטנה, כי על URL אפשר גם לשים 6,000 ויותר תווים בדפדפנים של ימנו – אבל זה לא כ"כ מעשי:
    • Query String הוא נחמד, וקל להשתמש בו בהפשטה של מילון (<Map<String, String) – אל זו עדיין צורה מגבילה להעברת מידע. אין היררכיה, וקשה לתאר מבנים מורכבים יותר. בעיה.
    • על זה נוסיף את הצורך ב URL encoding, כי יש תווים שאסור להציב על ה URL. למשל: רווח (להלן ה %20 שאתם רואים ב URLs). מהר מאוד ה URL הופך ללא קריא, ולכן פחות שימושי.
    • אפשר לקודד JSON (מבנה גמיש והיררכי) על ה URL – אבל זה מקרה קיצוני יותר מהאייטם הקודם. זה כבר לא פשוט, לא שקוף, ולא Web native. פספסנו לגמרי את הרעיון של שימוש ב HTTP בגלל "קריאות ופשטות". כבר כבר יותר RPC.
    • אותה בעיה – קיימת גם עבור פעולות Delete (לכאורה Body הוא optional – אבל כלים רבים יחסמו אותו).
    • הפתרון הנפוץ הוא לבצע פעולות POST עבור קריאות שהיו אמורות להיות GET. ב POST אפשר להעביר body – אבל בעצם השימוש ב POST התרחקנו מההקפדה על הסמנטיקה של HTTP – שזה כל העניין. לא ניתן להבחין כבר בין POST שהוא safe לכזה שלא, ללא מידע נוסף. ראיתי מגוון גישות להתמודד עם החריגה הזו (POST as GET) – כל גישה, וה tradeoffs שלה.
  • חוסר בהירות בין POST ל PUT
    • פעולת PUT ב HTTP משמשת להחלפה של משאב שנמצא ב URL בתוכן הנשלח ב Body, וזו פעולה Idempotent – ניתן לבצע את אותה הפעולה מספר פעמים, מבלי שהתוצאה תהיה שונה מביצוע יחיד של הפעולה.
    • פעולת POST ב HTTP מעדכנת / שולחת מידע למשאב שנמצא ב URL – שזו הגדרה כללית הרבה יותר. הפעולה בהגדרה איננה Idempotent (כלומר: חלק מהפעמים היא עשויה להיות – אך אסור להסתמך על זה).
    • מכאן ועד תרגום של ה Verbs של PUT ו POST ל Create/Update/פעולה אחרת – יש מגוון של פרשנויות, לפעמים פרשנויות סותרות המתבססות על דקויות נכונות מתוך התקן של HTTP. במערכת Wiki, קביעת גבולות ברורים בין PUT ל POST היא דבר קל – במערכת מוצר מורכבת ומסועפת, תקן ה HTTP משאיר אותנו "חשופים" לדילמות, פרשנויות, והתלבטויות.
    • אם אתם לא מכירים את הדיונים הרבים, הנה מאמר תמציתי אחד שמסביר מדוע POST ו PUT הם לא פשוט Create ו Update.
  • בלי להכביר במילים, חוסר בהירות דומה קיימת גם בין PUT ו PATCH. הנה מאמר של המחבר הנ"ל לגבי הדילמה הזו. יש גם מאמר על PUT vs. PATCH vs. JSON-PATCH, ועוד אחד, ועוד אחד, ועוד אחד – כל אחד עם טענה מעט שונה.
הנה פרשנות אחת (ומקובלת) מתי להשתמש ב PUT ומתי ב POST ליצירת אובייקט. יש עוד כמה פרשנויות סבירות והגיוניות.
  • אולי המגבלה הכי קשה באימוץ "REST על מלא-מלא" בהיבט ה Verbs הוא הדלות ואי-העדכניות של ה Verbs המקוריים של HTTP לצרכים ועושר-הפעולות של מערכות תוכנה מודרניות. REST גרס שלא צריך להמציא "שפה חדש" – ואפשר להשתמש ב HTTP, אבל בשפה הזו יש רק 9 פעלים (עוד כ 20 נרשמו כהצעות) – אך אנו זקוקים ל 300 פעלים בכדי לקיים שיחה אינטליגנטית בדומיין העסקי שלנו? דומיין שהוא הרבה יותר מ CRUD?
    • אולי עבור מערכת וויקי, 9 פעלים מספיקים: עמוד יכול להיווצר / להיכתב מחדש (PUT), אפשר לכתוב עליו הערות (POST), ניתן לעשות בו תיקונים קטנים (PATCH) ואפשר למחוק אותו (DELETE) – הנה הסתדרנו!
    • במערכת של חברת ביטוח (למשל), בה על פוליסת ביטוח ניתן לבצע מגוון של פעולות:
      • לחתום על הפוליסה
      • להתחיל את הפוליסה
      • להקפיא / להפשיר את הפוליסה.
      • להוסיף מוטבים.
      • להוריד עותק של המסמך
      • לשלוח את המסמך לצד שלישי
      • לשלוח אישורים על המסמך לצד שלישי
      • לעדכן פרטים של בעל הפוליסה
      • לעדכן פרטים לגבי תוכן הפוליסה
      • לשנות כיסויים בפוליסה
      • לבדוק את הכיסויים בפוליסה
      • לבדוק סימולציה של מקרה – האם מכוסה ע"י הפוליסה
      • לבצע ביקורת על הפוליסה (Audit)
      • ועוד פעולות רבות…
      • הפוליסה מתוארת בצורה RESTFul למהדרין, כ URL כמו customers/{customer_id}/policies/{policy_id}/ אבל אין לי סיכוי לתאר את מגוון הפעולות הנ"ל בעזרת POST/PUT/PATCH בלבד. כל הרעיון ש "נראה את ה verb בלוגים השונים – ונבין מה התרחש" – מאבד ערך אם עבור כל הפעולות הנ"ל אני משתמש ב POST verb (פתרון מקובל אחד). יתרה מכך: אני צריך להשלים את הסמנטיקה שמבדילה בין הפעולות השונות איפשהו (על ה URL? על ה body? כ Header? כ Query Param) – אין מקום מוגדר וברור לעשות זאת. מגוון אפשרויות, עם tradeoffs וחריגות שונות מתקן ה HTTP והכוונות מאחוריו.
  • גם ברמת ה URI, אנו נתקל במגבלות: תיאור של URL היררכי לכל אובייקט במערכת – הוא לא דבר חד-משמעי ופשוט כפי שהיה במערכות פשוטות פעם (שוב: וויקי/מערכת פורומים). אקצר מאוד כדי לא להאריך את הפוסט אבל בעיקר:
    • לא תמיד יש היררכיה בין אובייקטים. לפעמים יש אובייקטים שהם שכנים "Siblings" – ואין דרך טובה לתאר את הקשר בהיררכיה (על ה Path).
    • לפעמים פרטי הזהות של אובייקט הם מורכבים / ארוכים מדי בכדי להיכנס ל URL.
    • ב REST/HTTP אין סמנטיקה לתאר Bulk operation – פעולה על מגוון אובייקטים. ישנן workarounds אך הם שונים ומגוונים, ולכל אחד – המגבלות שלו.
  • ב REST/HTTP אין דרך מוסכמת לתאר גרסאות ועתיות של APIs. באופן אישי אני משוכנע שהשתתפתי בעשרות שעות של דיונים בנושאים הללו, בארגונים שונים ועם אנשים שונים – כי אין תשובה פשוטה של REST שניתן לגשת ולהציג אותה. הנה סדרה של גישות ודיונים בנושא, למי שרוצה לצלול… הנה גם תקציר על 4 גישות מקובלות.
מישהו ריכז כאן דוגמאות וגישות ל versioning ב REST (והיד עוד נטויה). רק להדגים שהנושא אינו "סגור בטון" ומקובל על כולם באותו האופן.
  • עוד "שפה" שהייתה טובה / עשירה בזמנה – אך היום היא מוגבלת מדי היא ה HTTP Status codes. קודים כגון OK 200 או 401 (הקוד נקרא "Unauthorized" אך הכוונה היא בעצם ל Unauthenticated) – טובים ומתאימים לשכבת התשתית, אך מזמן כבר דלים מכדי לתאר את שכבת האפליקציה. להזכיר: HTTP הוגדר עבור ניהול דפי וויב ("documents" או "resources") ולא לתיאור של אפליקציות עסקיות מורכבות. למשל:
    • האם 404 מתאר endpoint שלא קיים, אובייקט שלא נמצא, או פריט מידע לפעולה שלא נמצא? איך מבדילים בין המקרים אם חזר רק הקוד "404"? דיון. לא פעם מפתח אחר מתכוון לדבר אחד, ומפתח אחר – מבין דבר אחר.
    • עוד דוגמה קלאסית היא המשמעות של 400 (הקרוי Bad Request).
      • ע"פ התקן – השרת לא יכול או לא יטפל בבקשה מכיוון שלא הוגשה בקשה נכונה מצד הלקוח (פורמט שגוי, שליחה ליעד הלא נכון)
      • השימוש הנפוץ הוא הרחבה של הקונספט "משהו לא טוב בבקשה" (ולא רק בפורמט/יעד) למשל: נשלח id שלא קיים של אובייקט. אבל…
        • אם נשלחו בבקשה כמה Ids – אז איזה מהם?
        • או אולי, אי אפשר לבצע את הפעולה מכיוון שעבר התאריך.
        • או אולי, לא ניתן לעשות פעולה בסכום נכון, כי החשבון מוקפא, או כי אלו פרטים אחרים אינם תואמים?
      • בקיצור: שום קוד סביר לא בצד הלקוח לא יכול להסתמך על קוד HTTP 400 להחלטה משמעותית של שינוי ב Flow ללא פרטים נוספים. נדרשים קודים מדויקים למגוון גדול של תקלות אפשריות, אז מה החשיבות להחזיר גם קוד HTTP 400? ראיתי שנים אנשים שהקפידו ותיקנו שוב ושוב קוד שהחזיר HTTP 500 להחזיר HTTP 400 – אך מעולם לא נתקלתי בערך שצץ מהפעולה הזו… פשוט עבודה לחינם.
  • עוד נושאים שאפשר לציין ש REST/HTTP לא נותנים להם מענה:
    • תקשורת אסינכרונית
    • Notifications – התקשורת של HTTP היא בקשה/תשובה. אם השרת המרוחק רוצה להודיע לי משהו ביוזמתו – כיצד הוא יעשה זאת?

אלו צרכים בסיסיים, שכנראה רוב מערכות הווב של ימנו זקוקות להם.
חשוב לציין ש HTTP/2 הוא כבר פרוטוקול בינארי, עם יכולות notifications, ואסינכרוניות (Pipeline/streams) – אך עדיין לא נתקלתי ב RESTful APIs שמשתלב ביכולות הללו בצורה הרמונית וטבעית.

סיכום

חזרנו והתבוננו ב REST בראייה מפוכחת, לא רק "REST זה טוב, SOA זה רע" – פזמון הפאנק של אמצע שנות ה-2000.

אני לא ממליץ לאף אחד לחזור ל *-WS או SOAP חלילה, הם כבר עתיקים ולא רלוונטיים. ייתכן ו RESTFul APIs זו הגדרה מספיק טובה לסט מצומצם של Public APIs של חברה (והרי ב Public API יש יתרון גדול בשימוש בתמות מוכרות), אך דיי ברור שלצורכי התקשורת הפנימיים של מערכת מורכבת / מיקרו-שירותים, RESTFul APIs היא גישה מוגבלת מדי ודלה בסמנטיקה – שחייבת הרחבה.

REST מעולם לא היה תקן, אף אחד לא מבאר אותו ומשפר אותו, ולא מתאים אותו לרוח התקופה. הוא הוצג בתחילת שנות ה 2000 – ועולם התוכנה השתנה מאז, ומאוד. אין תלונות.

לא ענינו על התשובה לשאלה: "כמה RESTFulness כדאי לעשות? ואיך יודעים לאמוד מתי ההצמדות ל RESTfulness היא תורמת, ומתי היא כבר מזיקה?" – אל אני מניח שכל ארגון תוכנה בימנו ש"מתבסס על REST" צריך לשאול את עצמו את השאלה הזו – ולהמציא תשובות.

יש ניסיונות כנים ומשמעותיים לנסות ו"לתקן" את REST (בצד התקשורת; צד הארכיטקטורה הוא מאוד ספציפי ומתאים רק למיעוט שבמיעוט שבמערכות) למשל JSON API או להיצמד ל Guidelines של מישהו שעשה עבודה משמעותית, למשל ה Microsoft RESTFul API Guidelines. פעם מישהו הציע לי לאמץ בארגון את JSON API ושללתי את זה על הסף ("כל ארגון יגדיר את מה שמתאים לו"). היום הייתי מתייחס לאופציה הזו ברצינות גדולה יותר. לא כ "תורה מסיני שתפתור הכל", אלא כדי לא להתחיל את תהליך ההגדרה הארגוני מאפס.

לא נכון לכעוס או להתאכזב מ RESTFul APIs כרעיון כללי כ"כ, לא מוגדר היטב, ופשטני. הפשטות הזו – פתרה בעיה גדולה לארגונים בשלב הצמיחה (גוגל, יאהו, ועשרות-אלפי סטאראט-אפים נוספים) ושירתה אותם היטב. הכלליות – התאימה ל"כולם" (כל אחד מצא שם משהו שדיבר אליו, בלי משהו גדול להתנגד אליו) ויצרה "רעיון" שמשך אחריו את כלל התעשייה – גם אם לא היה מדובר בתקן מדויק. רעיונות ה Web-Native חלחלו עמוק, והיום מהנדסים רבים כל-כך חותרים להשתלב עם הווב, במקום "להחליף את הקיים".

תנועות כגון Forget HTTP + הרצאה או HTTP Hell-no אולי מביעות הרבה תסכול מה"אכזבה" של REST בפועל – אך מנסות להציע גם חלופות ותיקון.

טכניקות כגון Polling, Long-Polling ו Webhooks הן דוגמאות פשוטות להרחבות טכניות פשוטות – המאפשרות עוד הרבה מרחב תמרון במקומות ש HTTP/REST מוגבל בהם ("client קורא ל server", וזהו). יש לי הסברים מפורטים על הטכניקות הללו – אניח לרגע שכל הקוראים מבינים אותן. כן גם משמרות את האופי של HTTP/REST כמעט ולחלוטין – זו רק הרחבה.

הפתיחות לאמץ אלמנטים של RPC בצורת העבודה של הארגון (להתגמש ב Resource modeling, להוסיף קודים של הארגון לכל תשובה, ו/או מבנה סטנדרטי של בקשה/תשובה לכלל המערכת) עשויים לשרת אתכם היטב, גם אם "קהילת נאמני ה REST" – ידיחו אתכם מחברות בארגון / חיי נצח / ההבטחה ל 70 מהנדסות תוכנה (טובות!) בעולם הבא.

(אם מישהו ישאל, JSON-RPC הוא פשטני ונאיבי מדי לטעמי, וגם אותו תאלצו מהר מאוד להרחיב ולהגדיר בעצמכם. RCP בינארי כמו Thrift או gRPC הוא אטקרטיבי לשיפור ביצועים / אמינות התקשורת – אך מהלך כבד מדי אם כל מה שאתם מחפשים הוא סמנטיקה וכללי עבודה טובים לבעיות מידול ה Endpoints במערכת שאינה very high scale, הפשטות הרי חשובה).

בקיצור: אני חושב שנכון להודות על מה שיש (RESTFul APIS, HTTP, או מה השאתם משתמשים) – אבל לא להניח שזה מספיק כדי ליצור תשתית תקשורת מספיקה לצורכי המערכת והארגון – בעיקר ככל שהמערכת מתפתחת והארגון גדל. הניחו שיהיה עליכם להגדיר שפה וכללים משלכם – שישרתו אתכם, וזה בסדר.

אם אתם עובדים ברמה של HTTP בצורה ניכרת (או ב RESTFul APIs או ב RPC כלשהו, למשל home-made) – חשוב ללמוד את התקן הפשוט והחשוב הזה, ולהבין אותו. אני רוצה להאמין שזה מאמץ קטן ומשתלם. יש את הפוסטים שלי (HTTP Basics + מבנה ה URL) אלו ואלו, יש את הרפרנס הנחמד הזה של for-get-HTTP – HTTP References שמצאתי במהלך כתיבת הפוסט – ונראה לי שאשתמש בו עוד בעתיד.

מחשבות, הערות, תוספות – יתקבלו בברכה!

12 תגובות בנושא “על המגבלות של REST – בשירות מערכות ווב בנות-זמננו

  1. תודה ליאור! לקחת נושא מורכב והפכת אותו לפשוט. נהדר!

    הייתי רוצה להתייחס לכמה נקודות ולהאיר אותן מהזווית שלי:
    1. נושא ה-Verbs: ארבע הפעולות הבסיסיות GET, PUT, DELETE, POST מתמפות לפעולות ה-CRUD אותן הדגמת היטב בדוגמא שנתת של דפי ווב (או Web Resources) ובנוסף גם לבסיסי נתונים מהסוג הנפוץ לתקופתו – רלציוניים. היכולת לראות רשומה בודדת בבסיס הנתונים כפי שרואים Web Resource (או פשוט Resource) היא הכללה מעניינת.
    2. HATEOAS: אחת ההגדרות שנתן פילדינג היא שכל הודעה (request/response) מתארת את עצמה (Self Descriptive) ושפרט אליה אין עוד צורך במידע אחר. כך לדוגמא, כאשר ביקשתי יישות מסויימית שמקורה בבסיס הנתונים ואני מעוניין שתיוצג באמצעות JSON, לרוב תתקבל תשובה שאחד השדות בה הוא id. ה-id יכול להיות מספרי – id: 12345 או מחרוזת: id: 'ab12-cd34-ef56-gh78', אבל על פי פילדינג הוא צריך לחזור כ-URI שלם שמצביע אל הישות (לדוגמא בשדה מסוג self). הרעיון הזה מייצר כתובות סטטיות, אשר מאפשרות לצרכן לשמור את הישות (או קישור אליה) ולחזור אליה במידת הצורך.
    קישורים אל יישויות אחרות נעשים באותה צורה, מסורבלת ככל שתהיה.
    3. APIs שונים: דוגמת החתימה על מסמך שנתת היא דוגמא מצויינת בה ניתן להשתמש ב-REST, אבל צריך להפוך בשבילה את העולם עד כדי חוסר בהירות וסרבול מיותרים. כלומר, אם ה-State מורכב לייצוג, לא הכרחי להשתמש ב-REST ואפשר גם להפעיל טכניקה אחרת, מבוססת על HTTP או שלא.

    כמוך, אני מסכים שהשימוש ב-REST מאתגר מערכות מודרניות, ולכן אני משתמש בו כשניתן ונוטה לגוון כשצריך.

    אני מוסיף קישורים ל-API של GitHub שתומכים גם ב-REST וגם ב-GraphQL:
    https://docs.github.com/en/rest
    https://docs.github.com/en/graphql

    1. תודה אופיר על התגובה העמוקה והמנומקת!

      לגבי נקודה מספר 2 – נשמע הגיוני, אכן כל פעולה כוללת לינקים לפעולה הבאה – ומעולם לא חשבתי אם "מותר" לשמור אותם לשימוש חוזר. אני זוכר שבפעם שעבדתי עם HATEOAS (לכאורה), היינו עושים קריאת GET על המשאב (אנחנו פנינו אליו ישירות, בלי לשמור היסטוריה) – לפני פעולת POST. זו הייתה לכאורה הגנה בפני CSRF – אבל ההשראה והדיבור היה "לעשות REST נכון יותר". אני זוכר שההסבר שקיבלתי היה שזה כמו שמישהו גולש לדף באתר – ולעולם לא יוכל לעשות POST ללא שעשה GET קודם לכן. חיקוי התנהגות עמודי ווב – ב API.
      מעולם ירדתי לשורש העניין / בדקתי – האם באמת יש משהו יותר REST-י ברצף כזה (GET לפני כל POST). מניסיוני כבר למדתי שדברים רבים שאני "זוכר" – התבדו ברגע שצללתי לפרטים, ובדקתי.

      לגבי נקודה מספר 3 – מסכים שחתימה על מסמך יכולה להיות מורכבת ב REST. לא זיהיתי את המורכבות בעת שכתבתי את הפוסט (פשוט "זרקתי" רשימה ארוכה של פעולות) – אז תודה!

      למדתי דברים חדשים – אז תודה לך, שוב!

  2. מימוש ב rest תמיד מתחיל פשוט וקל וטבעי, אבל (מניסיוני) נגמר בצורה מורכבת ומסורבלת.
    בעיה אחת היא קינון או מבנה מורכב של של resources (רמה אחת מעבר לייצוג לקוחות של פוליסה, או פוליסות של לקוח).
    בעיה שניה היא קליינט שמבקש ייצוגים שונים (מפורט, כללי, עבור תצוגה, רשימה של כולם) של אותו resource
    דרך אפשרית לפתרון היא graphql שמבקשת מהלקוח לתאר את המידע שהוא צריך כשאילתא (הבעיה עוברת לצד שלו)

    1. תודה רוני על ההערות!

      ההשוואה בין REST ל RPC ל GraphQL / ODATA היא מעניינת בעיניי.
      אני מניח שבמערכת מורכבת יש מקום לשלושתם זה לצד זה – ולא בהכח הם צריכים להיות תחליפיים.

      במובנים רבים, GraphQL דווקא משלב הרבה מרכיבים של ה REST Architectural Sytle, ובעיקר – ש"השליטה" על המידע (הבנה, יוזמה) היא בצד הלקוח. אני חושב שזו תכונה חשובה בהחלטה היכן להתאים אותו (הרבה יותר טבעי ב API GW, הרבה פחות טבעי בלב מערכת עסקית מורכבת). לחבר לפוסט מהזמן האחרון (אפשור מול ריסון) – GraphQL הוא בפירוש "מאפשר" ללקוח, ולא "מגן" על השרת.
      אני מניח שאפשר לכתוב על זה פוסט שלם….

  3. גם אצלנו נתקלנו בבעיות דומות, אצלנו אנחנו מחזירים 200 ב response status ובתוך הbody מוסיפים שדה status משלנו.
    גם זה לא מושלם כיוון שהקליינט תמיד צריך להוסיף טיפול במקרה של 4XX או 5XX לא צפויים

    מעניין האם שקלתם/ניסיתם graphql?
    כיביכול אמור להיות תחליף ל REST אבל בכל הקשור ל PUBLIC API לא נראה שנפוץ כלכך.
    מה לגבי שימוש בין מודולים פניימים?

    1. שקלנו ובחרנו ב GraphQL לנקודות ספציפיות במערכת, באמת בממשק בין ה Web Application ל Backend.

      עבור API פנימיים אני חושש ש GraphQL מזיק, כי הוא בעצם שובר את ההכמסה, וחושף מודל נתונים מלא ללקוח.
      או שמישהו יבנה עולם הפשטות מושלם עבור ה API ה GraphQL-י (המון עבודה), או שהוא יחשוף את המודל הפנימי – ואך "יתקע" את עצמו ממסוגלות לשינויים משמעותיים במודל.

      מה שאתה מתאר הוא סוג של Custom RPC (להחזיר 200 עם סטטוס פנימי), שאני חושב שהוא יותר מקובל ממה שנדמה, כי המון קוראים לו "REST" ורק בבירור מעמיק מבינים שזה יותר RPC מ REST (לטעמי). יש המון שאלות על פרטי המימוש ומה ה"שפה/סטנדרט" שאנחנו רוצים להגדיר בו APIs, דיון שקצת נדחה – ולא עדיין ארגנו את המחשבות והגענו לסיכומים.

להגיב על Ester Kaufman לבטל