ה-API שלך הוא חוזה, ואתה תחיה איתו שנים
ב-2002, ג'ף בזוס שלח את ה-API Mandate המפורסם לכל מהנדסי Amazon: "כל team חייבת לחשוף את הנתונים שלה דרך APIs. כולם חייבים לתכנן את ה-API שלהם כאילו הוא עתיד להיות public. כל מי שלא יעשה זאת, יפוטר." זו היתה ה-genesis של AWS. ה-לקח: API טוב לא נולד, הוא מתוכנן. ב-2026, כ-93% מהחברות משתמשות ב-REST API כלשהו, ולכל חברת SaaS ישראלית שמתחברת לעולם, Rapyd, Tipalti, Monday.com, ה-API הוא מוצר בפני עצמו. שינוי שבורה ב-API אחרי שיש integrations חיים? זו הודעת דחיפה ל-800 מפתחים בשעה שלוש בבוקר.
REST, GraphQL, gRPC, מה לבחור ומתי
לפי סקר 2026, כ-93% מהצוותים משתמשים ב-REST, כ-40% ניסו GraphQL לפיצ'רים חדשים, וכ-25% משתמשים ב-gRPC בתוך microservices. זה לא מקרי, לכל protocol יש את ה-sweet spot שלו.
| Protocol | מתי מתאים | חיסרון עיקרי |
|---|---|---|
| REST | Public API, פשטות, HTTP caching, כשהclient לא ידוע | Over-fetching ו-under-fetching, endpoints רבים |
| GraphQL | Client-driven queries, mobile apps שצורכות רק חלק מהנתונים | N+1 queries, caching מורכב, learning curve |
| gRPC | Internal microservices, ביצועים גבוהים, strongly-typed contracts | לא browser-native, קשה לdebug, תיעוד מורכב יותר |
| tRPC | TypeScript full-stack monorepos, type safety מקצה לקצה | רק TypeScript, לא מתאים לAPI חיצוני |
עקרונות REST, מה שרוב האנשים דולגים עליו
REST אמיתי שונה ממה שרוב האנשים כותבים. Roy Fielding הגדיר ב-dissertation שלו מ-2000 שישה constraints. המרכזי: Uniform Interface, resources, HTTP methods, ו-hypermedia. מה שרוב האנשים כותבים הוא RPC over HTTP. הסימנים:
- RPC over HTTP (שגוי): POST /api/createPayment, POST /api/doRefund, GET /api/cancelOrder
- RESTful (נכון): POST /payments, POST /payments/{id}/refunds, DELETE /orders/{id}
- כלל האגודל: URIs = שמות עצם (nouns), לא פעלים. HTTP method = הפועל.
HTTP status codes הם שפה, 401 אומר "מי אתה?" (authentication), 403 אומר "אני יודע מי אתה אבל אסור לך" (authorization). שני הקודים האלה מבולבלים בכ-40% מה-APIs שנסקרו. ו-422 הוא לvalidation errors בbody, לא 400.
Versioning, איך לשנות API בלי לשבור אף אחד
כל API שמגיע ל-production יצטרך לשנות. השאלה היא לא אם, אלא מתי ואיך. שינויים שוברים (breaking changes) ברורים: הסרת endpoint, שינוי שם field, שינוי סוג שדה, הוספת required field לrequest. שינויים שוברים שקטים שמפתחים מפספסים: שינוי validation rules, שינוי default values, שינוי rate limits.
אסטרטגיות versioning נפוצות:
- URL versioning (/v1/payments, /v2/payments): הפשוט ביותר לdebug ולtest, אבל מפר את עיקרון ה-stable URI של REST. Spring Framework 7 (נובמבר 2025) הוסיף תמיכה first-class בזאת.
- Header versioning (Accept: application/vnd.myapi+json;version=2): REST-purer אבל קשה יותר לbrowse ולtest ידנית.
- GraphQL approach: אין versions בכלל, רק additive schema evolution. אי אפשר להסיר שדה, רק לdeprecate.
כלל אחד שמציל: תן 6-12 חודשי deprecation notice עם migration guide. Stripe, שה-API שלה הוא benchemark בתעשייה, שמרה על backward compatibility מלאה מ-2011. זה ה-moat שלה.
Pagination, שלוש דרכים ורק אחת נכונה לkale
שלוש אסטרטגיות, לכל אחת context שבו היא מנצחת:
- Offset-based (?page=3&limit=20): פשוטה לUI. אבל: OFFSET 990000 LIMIT 20 = full table scan. ב-10 מיליון records, קטסטרופה ביצועים. בנוסף: אם record נוסף בין דפים, page 3 עשויה לכלול duplicates.
- Cursor-based (?after=eyJpZCI6MTIz&limit=20): הcursor הוא opaque pointer. מהיר, stable, לא מושפע מinserts. חיסרון: אי אפשר לקפוץ לpage ספציפית. מתאים לfeeds, real-time data.
- Keyset (?after_id=1230&limit=20): variant של cursor שמשתמש ב-DB column ישירות (WHERE id > 1230). הכי מהיר, שימוש ב-index. מוגבל לsorting על indexed column.
כלל: עד 10,000 records, offset בסדר. מעל? cursor או keyset.
Idempotency וError Design, מה שמבדיל API תעשייתי מ-hobby project
Idempotency אומרת: קריאות מרובות עם אותם parameters מייצרות אותה תוצאה ואותה response. GET, PUT, DELETE הם idempotent. POST לא, ברירת מחדל. החיים בpayments: network timeout, client לא יודע אם החיוב עבר, שולח שוב. בלי idempotency key, לקוח חויב פעמיים. Stripe פתרו זאת עם Idempotency-Key header, לכל POST על payment, client שולח UUID ייחודי. שרת שומר את ה-response ומחזיר אותו שוב אם אותו key מגיע שוב. חברות ישראליות כמו Tipalti, שמעבדות תשלומים לספקים גלובליים, חייבות ליישם זאת, שגיאה אחת = העברה כפולה למאות ספקים.
Error design לפי RFC 7807 (Problem Details for HTTP APIs):
- type: URI שמזהה את סוג השגיאה
- title: תיאור קצר בשפה אנושית
- status: HTTP status code
- detail: הסבר ספציפי לinstance הזה
- instance: URI שמזהה את הoccurrence הספציפי
טעות קלאסית: להחזיר {"error": "SQLException: column user_id does not exist in table payments"}. זה חושף ל-attacker את ה-schema שלך. בproduction, errors generic. ב-logs, פרטיים.
שלוש טעויות נפוצות
1. Chatty API: API שדורשת 10 קריאות לrender דף אחד. הפתרון: BFF (Backend for Frontend) pattern שמaggregates קריאות, או GraphQL לclient-driven queries. Monday.com ו-Wix משתמשות ב-GraphQL בדיוק בגלל זה, mobile app צורכת פחות נתונים ממה שdesktop צריך.
2. Rate Limit Headers חסרים: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, בלי זה, developers לא יודעים כמה requests נשארו ומקבלים 429 Too Many Requests בהפתעה. Stripe ו-GitHub מדגימות את הפורמט הנכון, developers לומדים ממנה.
3. URL versioning ללא תכנון deprecation: /v1/ ו-/v2/ בלי policy של "מה קורה ל-v1 בעוד שנה", teams מוצאות עצמן מתחזקות 4 versions בו-זמנית. מדיניות deprecation חייבת להיכתב לפני ה-launch, לא אחרי.
סיכום, API שמכבד את ה-developers שמשתמשים בו
ה-Stripe API documentation הוא ה-gold standard. לפני שמתחילים לעצב API, שווה לשאול: איך Stripe פתרו את הבעיה הזו? error objects עם type, code, message, param. idempotency keys. versioning שמעולם לא שבר integrations מ-2011. webhooks עם retry logic. Claude יכול לסנתז את כל ה-patterns האלה: "Analyze Stripe API design principles and apply them to this fintech API design." ה-takeaways המרכזיים: תכנן את ה-API כאילו הוא public מיום אחד. כתוב deprecation policy לפני ה-launch. השתמש ב-RFC 7807 לerrors. הוסף Rate Limit headers. ובכל ספק, בחן איך Stripe פתרו זאת.
