- full-stack SaaS חי על free tier: Worker אחד (Hono) + Static Assets (SPA) + D1 + R2 + KV + Workers AI + Vectorize RAG, מגודר ב-Turnstile + Zero Trust Access — והכל מ-
wrangler.tomlיחיד. wrangler.tomlאחד שמכריז על 7 bindings ([assets],[[d1_databases]],[[kv_namespaces]],[[r2_buckets]],[ai],[[vectorize]],[[analytics_engine_datasets]]) ונטען ב-wrangler devללא שגיאת config.- Hono router אחד עם SPA passthrough native (דרך
[assets]), route בריאות, route כתיבה ל-D1 מגודר ב-Turnstile, ו-/api/askRAG עם מודל GA (glm-4.7-flash, לא llama deprecated). - מטריצת pay-or-not מלאה כטבלה: לכל primitive → מגבלה חינמית → המגבלה שנופלת ראשונה → trigger לשדרוג → עלות משוערת ב-1,000 DAU → המלצה.
- חישוב מבוסס-מספרים של נקודת ה-$5: איזה primitive שובר את ה-free tier ראשון באפליקציה שלך, ומשפט החלטה יחיד — $0 או $5, ולמה.
- audit סיכונים: מה GA ובטוח ל-production, מה עדיין beta (price risk עם 30-day notice), ואילו שמות מודלים הוסרו ב-2026-05-30.
- production checklist בן 8 סעיפים + מסמך pay-or-not שמיר (Markdown/PDF) — ה-deliverable שמלווה כל deploy עתידי שלך.
- לחבר full-stack SaaS חי על ה-free tier בלבד עם כל ה-bindings ב-
wrangler.tomlאחד (Worker + Static Assets + D1 + R2 + KV + AI + Vectorize). - לגדר את האפליקציה ב-Turnstile ו-Zero Trust Access ולהוסיף observability עם Analytics Engine — הכל ב-$0.
- לבנות מטריצת pay-or-not: לכל primitive → המגבלה הנופלת ראשונה → trigger לשדרוג → עלות משוערת ב-1,000 DAU.
- לאמוד את נקודת ה-$5: לזהות איזה primitive שובר ראשון את ה-free tier באפליקציה אמיתית ולנמק את ההחלטה במספרים.
- סיימת את פרקים 1-5. מפרק 1 יש לך Worker חי + Static Assets + הבנת ה-free-tier matrix ו-
.assetsignore. מפרק 2 — bindings של KV / D1 / R2 / DO וקיר ה-10ms CPU. - מפרק 3 — Workers AI, מטבע ה-Neurons (10,000/day) ו-Vectorize RAG. מפרק 4 — R2 לאחסון media. מפרק 5 — Turnstile, Zero Trust Access (50 seats), Analytics Engine ו-
wrangler secret put. - אתה יודע לקרוא TypeScript ולהריץ
npx wrangler dev/deploy. אין צורך לכתוב מאפס — נדביק חלקים שכבר ראית. - אזהרה לפני שמתחילים: זה פרק רגיש-לזמן. במיוחד שמות מודלי AI — מודלים שכל מדריך עדיין ממליץ עליהם (
llama-3.1-8b) הוסרו ב-2026-05-30. נשתמש רק במודלי GA, ותמיד אמת מול הקטלוג החי לפני שמקבעים string.
הפרקים הקודמים (1-5): בנית את החלקים בנפרד — Worker + Static Assets (פרק 1), KV/D1/R2/DO וקיר ה-10ms (פרק 2), Workers AI + Vectorize RAG + תקציב neurons (פרק 3), שכבת media עם R2 (פרק 4), ושכבת ה-glue: Turnstile, Access, Analytics Engine (פרק 5). אתה מגיע לכאן עם ארגז כלים מלא, אבל מפוזר.
הפרק הזה (הקאפסטון): מחברים את כל החלקים ל-SaaS אחד חי — wrangler.toml יחיד, Worker יחיד (Hono), אותם bindings מ-1-5 ביחד. ואז בונים את מטריצת ה-pay-or-not שעונה במספרים מתי $0 הופך ל-$5.
הפרק הבא: אין הבא — זה הסיום. אחרי הפרק הזה האפליקציה שלך חיה, מגודרת ונמדדת, ובידך מסמך החלטה מבוסס-מספרים שמלווה אותה ל-production. הקורס נסגר עם דבר אמיתי פרוס, לא עם תרגיל.
| מונח | בעברית | הסבר |
|---|---|---|
| full-stack on free tier | פוּל-סטאק על השכבה החינמית | ארכיטקטורה שבה כל שכבות האפליקציה (compute, storage, AI, auth, analytics) רצות על ה-free tier של Cloudflare ב-$0, עד נקודת השבירה של המגבלה הראשונה. Worker אחד ניגש לכולן דרך bindings. |
| binding composition | הרכבת ביינדינגים | הכרזה של כל ה-bindings (D1, KV, R2, AI, Vectorize, Analytics) בקובץ wrangler.toml יחיד, כך ש-Worker אחד ניגש לכולם דרך typed Env interface — בלי לנהל חיבורים, מפתחות או SDK-ים נפרדים. |
| pay-or-not decision matrix | מטריצת החלטת תשלום | טבלה שממפה לכל primitive את המגבלה החינמית, המגבלה שנופלת ראשונה, ה-trigger לשדרוג והעלות המשוערת בקנה מידה — בסיס להחלטה מבוססת-מספרים אם להישאר $0 או לשלם $5. |
| first-limit-to-fall | המגבלה שנופלת ראשונה | ה-primitive שמגבלתו החינמית נשברת ראשונה באפליקציה נתונה. כלל אצבע (לא הצהרה רשמית של Cloudflare): בדרך כלל KV writes (1k/day) או Workers AI neurons (10k/day). |
| cost-at-scale estimation | אומדן עלות בקנה מידה | שיטה לאמוד עלות חודשית: per-DAU action × DAU × 30 ימים, מושווה למגבלה החודשית-שקולה של כל primitive. בלי החישוב הזה ה-pay-or-not הוא ניחוש. |
| $5 break-even point | נקודת האיזון של $5 | הנקודה שבה Workers Paid ($5/חודש) הופך משתלם — בפועל החלטה בינארית: כל לולאת כתיבה פר-משתמש או כל פיצ'ר AI בקנה מידה מחייבים אותו. ה-$5 ממיר daily caps ל-monthly pools. |
| production checklist | רשימת בדיקות ל-production | רשימת בדיקות לפני go-live: מודלי GA בלבד, secrets ב-wrangler secret put, .assetsignore, smoke test ב---remote, named tunnel ל-SSE, התאמת binding names ל-Env. |
| GA vs beta risk audit | ביקורת סיכוני GA מול beta | בדיקה האם כל מוצר בסטאק הוא GA (יציב, בטוח ל-production) או beta (API/מחיר עלולים להשתנות עם 30-day notice) — כולל שמות מודלים מסומנים להסרה. |
מהפרקים הבודדים לאפליקציה אחת — מה אנחנו מחברים עכשיו
חמישה פרקים בנית חלקים. עכשיו תעצור רגע ותסתכל על מה שיש לך ביד: Worker שפרוס על workers.dev, אחסון מכל הסוגים (KV ל-cache, D1 לטבלאות, R2 לקבצים), inference של AI עם RAG, ושכבת glue שמגדרת ומודדת. כל אחד מהם עבד בנפרד. הקאפסטון לא מוסיף primitive חדש — הוא לוקח את כולם ומחבר אותם ל-Worker אחד, מ-wrangler.toml אחד.
זו בדיוק העוצמה של הפלטפורמה ל-Vibe Coder: בארכיטקטורה רגילה היית צריך שרת ל-API, מסד נתונים מנוהל, bucket ב-S3, שירות vector חיצוני, ספריית auth, ושירות analytics — שישה חשבונות, שישה מפתחות, שישה מחירונים. כאן הכל binding אחד בקובץ אחד, ו-Worker יחיד ניגש לכולם דרך אובייקט env מוקלד. זה לא רק נוח — זה מה שמאפשר full-stack שלם ב-$0, כי אין שום צד-שלישי שמתחיל לחייב מהשנייה הראשונה.
שים לב להבדל המנטלי בין מה שעשית עד עכשיו לבין מה שאתה עושה כאן. בכל פרק קודם, ההקשר היה "המוצר הזה לבד": איך KV מתנהג, מה Workers AI יודע לעשות, איך Turnstile מאמת. עכשיו ההקשר התהפך — השאלה היא איך כל המוצרים חיים יחד תחת deploy אחד, כשבקשה אחת של משתמש עלולה לגעת בארבעה מהם ברצף (אימות Turnstile → קריאה מ-KV cache → כתיבה ל-D1 → רישום ב-Analytics). זו קפיצת המדרגה מ-"אני יודע להשתמש ב-primitive" ל-"אני יודע לחבר מערכת". וזה גם בדיוק המקום שבו מתחילות הבעיות שלא ראית קודם — כי כל primitive מביא איתו את המגבלה החינמית שלו, וכשהם רצים יחד, אחת מהן תיגע בקיר לפני כל השאר.
אבל "ב-$0" הוא לא נצחי, וזו הסיבה שהפרק הזה הוא לא רק חיווט. אחרי שנחבר הכל, נעשה את הדבר שאף מדריך לא עושה: נשב ונחשב, primitive אחר primitive, מתי ה-$0 נשבר ולמה. התוצר המרכזי של הקאפסטון הוא לא רק האפליקציה החיה — אלא מסמך ה-pay-or-not שאומר לך, במספרים, מתי לשלוף את כרטיס האשראי.
למה זה כל כך חשוב דווקא ל-Vibe Coder? כי הדפוס המוכר הוא: בונים משהו מגניב עם כלי AI בסוף שבוע, פורסים, זה עובד, החשבונית $0, וכולם שמחים. ואז משהו מצליח — עולה ל-Product Hunt, מקבל אזכור, מגיעים 800 משתמשים ביום אחד — והאפליקציה נשברת בשקט. לא קריסה דרמטית, פשוט ה-API מתחיל להחזיר שגיאות לחלק מהמשתמשים, ואתה לא מבין למה. הסיבה כמעט תמיד אותה סיבה: מגבלה חינמית יומית אחת (KV writes או neurons) נגעה בקיר, ואתה לא ידעת שהיא שם. הפרק הזה הוא החיסון: אתה תדע מראש איזו מגבלה תיגע בקיר ראשונה, ובאיזה מספר משתמשים — כך שתשדרג ל-$5 ב-בחירה, לא ב-פאניקה.
פתח את wrangler.toml של הפרויקט שבנית בפרק 1 ורשום בצד אילו bindings כבר יש לך מפרקים 2-5 (KV? D1? R2? AI? Vectorize?). זו רשימת ה-inventory שנחבר עכשיו ל-Worker אחד.
ארכיטקטורת ה-Free Full-Stack — מפת המוצרים על דף אחד
לפני קוד, מפה. הנה איך נראה SaaS שלם שרץ כולו על ה-free tier, ב-primitive אחד למרכז — ה-Worker עם Hono — וכל השאר תלוי בו כ-bindings:
- Worker (Hono) — המוח. router אחד שמטפל בכל בקשת
/api/*. כל ה-bindings זמינים לו דרךenv. - Static Assets — ה-frontend. ה-SPA הבנוי (תיקיית
dist) מוגש ישירות מה-edge, בלי לעבור דרך ה-Worker. רק/api/*"נופל" ל-Worker. - D1 — הנתונים הרלציוניים (משתמשים, רשומות, הזמנות). strongly consistent writes.
- R2 — קבצים והעלאות. zero egress — אתה לא משלם על הורדה.
- KV — sessions, cache ו-feature flags. read-heavy, eventually consistent.
- Workers AI — inference ב-edge (chat, embeddings). 10,000 neurons/day.
- Vectorize — ה-index הווקטורי ל-RAG. נמדד לפי dimensions.
- Turnstile — CAPTCHA בלתי נראה על טפסים. לא binding — מאומת server-side.
- Zero Trust Access — גידור routes לפי זהות. לא binding — מוגדר ב-dashboard.
- Analytics Engine — events ו-observability. כתיבה מקוד, query ב-SQL.
הנקודה הקריטית להבנה היא זרימת הבקשה. בקשה מגיעה ל-edge של Cloudflare. תחילה [assets] בודק: יש קובץ סטטי שתואם? אם כן — מוגש מיד, ה-Worker אפילו לא רץ (וזה חינם ובלתי מוגבל). אם לא — הבקשה "נופלת" (fallthrough) ל-Worker, ושם Hono מנתב אותה. הגדרנו ש-/api/* תמיד רץ ב-Worker (run_worker_first), ושכל מסלול לא-מוכר אחר חוזר ל-index.html של ה-SPA (not_found_handling = "single-page-application"). ככה frontend ו-backend חיים תחת אותו דומיין, בלי CORS ובלי שרת proxy.
למה זה משנה כל כך לחשבון העלות? כי הגשת קבצים סטטית היא חינמית ובלתי מוגבלת, וה-Worker רץ רק כשבאמת צריך לוגיקה. כל בקשה ל-index.html, ל-CSS, ל-JS או לתמונה ב-frontend לא צורכת ולו invocation אחד של Worker מתוך 100,000 היומיים. רק קריאות ה-API "עולות" משהו מבחינת המגבלה. בארכיטקטורה שבה ה-Worker מגיש גם את ה-frontend, היית שורף את מכסת ה-Workers על דברים שאין בהם שום לוגיקה. ההפרדה הזו — assets מגישים את ה-static, Worker מטפל רק ב-/api/* — היא לא רק נקייה אלא חוסכת לך את המגבלה היקרה ביותר.
עוד דבר שכדאי להפנים עכשיו: שלושה מתוך עשרת הרכיבים במפה (Turnstile, Zero Trust Access, וגם — מבחינה מסוימת — ה-frontend הסטטי) הם לא bindings ב-wrangler.toml. Turnstile עובד דרך secret ואימות server-side; Access מוגדר כולו ב-Zero Trust dashboard ועוטף את ה-route מבחוץ, לפני שהבקשה בכלל מגיעה ל-Worker. זה בלבול נפוץ — מפתחים מחפשים [[turnstile]] או [[access]] ב-toml ולא מוצאים. שמור את ההבחנה הזו: יש primitives שמתחברים כ-binding (D1, KV, R2, AI, Vectorize, Analytics), ויש כאלה שמתחברים כשכבת גידור חיצונית (Turnstile, Access).
צייר על נייר את זרימת הבקשה: דפדפן → Cloudflare edge → [assets] (קובץ סטטי?) → אם /api/* → Worker (Hono). סמן ליד החץ איפה כל primitive נכנס (D1 על write, KV על cache, AI על RAG). תחזיק את הציור הזה ליד התרגילים.
wrangler.toml אחד עם כל ה-Bindings — מפתחות 2026 המדויקים
זה הלב של החיווט. במקום שישה קבצי config, יש קובץ אחד שמכריז על כל ה-bindings. הנה ה-wrangler.toml המלא, עם מפתחות 2026 המדויקים — העתק אותו כבסיס ושנה רק את שמות ה-ID-ים:
name = "free-stack-app"
main = "src/index.ts"
compatibility_date = "2026-05-01"
compatibility_flags = ["nodejs_compat"]
# --- Static assets: serve the built SPA, run Worker first only for /api/* ---
[assets]
directory = "./dist"
binding = "ASSETS"
not_found_handling = "single-page-application"
run_worker_first = ["/api/*"]
# --- D1 (relational user data) ---
[[d1_databases]]
binding = "DB"
database_name = "app-db"
database_id = "<your-d1-database-id>"
# --- KV (sessions / cache / feature flags) ---
[[kv_namespaces]]
binding = "KV"
id = "<your-kv-namespace-id>"
# --- R2 (user uploads / files, zero egress) ---
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "app-uploads"
# --- Workers AI (edge LLM / embeddings) ---
[ai]
binding = "AI"
# --- Vectorize (RAG vector index) ---
[[vectorize]]
binding = "VECTORIZE"
index_name = "app-rag-index"
# --- Analytics Engine (first-party events, free beta) ---
[[analytics_engine_datasets]]
binding = "ANALYTICS"
dataset = "app_events"
# NOTE: Turnstile and Zero Trust Access are NOT wrangler.toml bindings.
# Turnstile secret -> `wrangler secret put TURNSTILE_SECRET`; verify token server-side.
# Access -> configured in the Cloudflare Zero Trust dashboard, gates the route by identity.
שלוש דקויות שמפילות אנשים, ושחובה לקלוט:
[ai]היא טבלה יחידנית — סוגריים בודדים, לא[[ai]]. כל שאר ה-bindings שיכולים להופיע יותר מפעם אחת (D1, KV, R2, Vectorize, Analytics) משתמשים בסוגריים כפולים[[ ]]. ה-AI binding יחיד ולכן[ai]. זו שגיאת config שקטה אם תטעה.run_worker_first = ["/api/*"]— זה מה שמבטיח שבקשת API לא "תיבלע" ע"י ה-asset server. בלי זה, אם יש קובץ בשם דומה הוא יוגש במקום ה-route שלך.- Turnstile ו-Access אינם כאן — אל תחפש אותם ב-toml. Turnstile עובד דרך secret + אימות server-side; Access מוגדר לגמרי ב-Zero Trust dashboard. אם תחפש
[[turnstile]]תתסכל לשווא.
שווה להבין מה כל מפתח עושה, כי כל אחד פותר בעיה אמיתית. directory = "./dist" מצביע על תיקיית ה-build של ה-frontend (התוצר של vite build או דומה). binding = "ASSETS" נותן ל-Worker גישה לקבצים הסטטיים גם מקוד, אם תרצה. not_found_handling = "single-page-application" הוא מה שגורם ל-router בצד הלקוח (React Router, וכד') לעבוד — כל מסלול לא-מוכר חוזר ל-index.html במקום לזרוק 404, וה-JS בצד הלקוח מטפל בניווט. ו-run_worker_first = ["/api/*"] מבטיח שבקשות ה-API תמיד עוברות דרך ה-Worker ולא נבלעות ע"י ה-asset server. ארבעת המפתחות האלה יחד הם המתכון המדויק ל-SPA + API תחת דומיין אחד ב-2026.
ולמה בכלל קובץ אחד ולא חיבור הדרגתי? כי wrangler.toml הוא ה-source of truth של כל הפריסה. כשכל ה-bindings במקום אחד, אתה רואה במבט אחד את כל מה שה-Worker יכול לגעת בו, ה-deploy אטומי (הכל עולה יחד או כלום), וה-onboarding של מישהו אחר לפרויקט הוא קריאה של קובץ אחד. בארכיטקטורות מבוזרות זה היה דורש לעקוב אחרי שישה dashboards שונים; כאן זה 30 שורות TOML שכל אחד מבין. זה גם מה שמאפשר את ה-type Env המוקלד שנראה בסעיף הבא — ה-toml וה-interface הם שתי תמונות של אותו דבר.
בקובץ wrangler.toml שלך, הוסף את הטבלה [ai] עם שורה אחת בלבד: binding = "AI". שים לב שזו טבלה יחידנית — [ai], לא [[ai]] עם סוגריים כפולים.
ודא ש-compatibility_date בקובץ שלך הוא תאריך עדכני (למשל 2026-05-01) ושיש compatibility_flags = ["nodejs_compat"]. שמור.
- קח את ה-
wrangler.tomlמפרק 1 כבסיס. - הוסף
[assets]עםdirectory,binding = "ASSETS",not_found_handling = "single-page-application", ו-run_worker_first = ["/api/*"]. - הוסף
[[d1_databases]](binding=DB),[[kv_namespaces]](binding=KV), ו-[[r2_buckets]](binding=BUCKET). - הוסף
[ai](binding=AI, טבלה יחידנית),[[vectorize]](binding=VECTORIZE, index_name), ו-[[analytics_engine_datasets]](binding=ANALYTICS, dataset). - ודא
compatibility_dateעדכני +nodejs_compat. - הרץ
npx wrangler devוהוכח שהקובץ נטען בלי שגיאת config — wrangler ידפיס את רשימת ה-bindings שנטענו.
פלט נראה לעין: קובץ wrangler.toml יחיד שמכריז על 7 bindings ונטען ב-wrangler dev ללא שגיאות — צלם את פלט הטרמינל שמציג את שבעת ה-bindings.
Hono כ-Router אחד — SPA Passthrough ו-API מעל D1
Hono הוא ה-router הקליל המומלץ ל-Workers ב-2026 (zero-dependency, מוקלד, תמיכה מובנית ב-Workers; גרסה אחרונה v4.12.x). הוא מקבל את כל בקשות /api/* וה-Static Assets מטפל בכל השאר. שים לב — אנחנו לא משתמשים ב-serveStatic הישן של hono/cloudflare-workers; ה-[assets] ה-native מטפל ב-SPA passthrough לבד.
הדבר הראשון שעושים הוא להגדיר type Env שמתאר את כל ה-bindings. זה מה שנותן ל-TypeScript לדעת ש-c.env.DB הוא D1, ש-c.env.AI הוא Workers AI וכו'. שמות ה-bindings ב-Env חייבים להתאים בדיוק לשמות ב-wrangler.toml — אי-התאמה היא באג שקט שמתפוצץ רק בזמן ריצה:
// src/index.ts -- one Worker, all bindings, SPA passthrough handled by [assets]
import { Hono } from 'hono'
type Env = {
ASSETS: Fetcher
DB: D1Database
KV: KVNamespace
BUCKET: R2Bucket
AI: Ai
VECTORIZE: VectorizeIndex
ANALYTICS: AnalyticsEngineDataset
TURNSTILE_SECRET: string
}
const app = new Hono<{ Bindings: Env }>()
// health
app.get('/api/health', (c) => c.json({ ok: true }))
// Turnstile-gated write -> D1 + KV cache invalidation
app.post('/api/note', async (c) => {
const { token, text } = await c.req.json<{ token: string; text: string }>()
// ... Turnstile verify (נראה בסעיף הבא) ...
await c.env.DB.prepare('INSERT INTO notes (text) VALUES (?)').bind(text).run()
await c.env.KV.delete('notes:list') // bust cache
return c.json({ ok: true })
})
// everything else ("/", "/about", static files) is served by [assets] automatically
export default app
שים לב לדפוס הכתיבה ב-/api/note: כתיבה ל-D1 (INSERT) ואז cache bust ב-KV (KV.delete('notes:list')). זה הדפוס הקלאסי — D1 הוא מקור האמת, KV הוא ה-cache המהיר; אחרי כתיבה אתה מבטל את ה-cache כדי שהקריאה הבאה תרענן אותו. שתי הכתיבות האלה הן בדיוק מה שיתחיל לשבור את ה-free tier בקנה מידה — נחזור לזה במטריצה.
למה Hono ולא לכתוב router ידני עם if (url.pathname === ...)? שלוש סיבות מעשיות. ראשית, typing — כשאתה כותב new Hono<{ Bindings: Env }>(), כל handler מקבל c.env מוקלד מלא, וה-IDE משלים לך את שמות ה-bindings ומתריע אם טעית. שנית, middleware — שורה אחת (app.use('/api/*', ...)) רצה לפני כל route תחת /api, וזה בדיוק איפה נשים את ה-Analytics logging ואת בדיקת ה-auth. שלישית, גודל — Hono הוא zero-dependency וזעיר, כך שהוא לא נוגס מה-CPU בזמן ה-cold path ולא מנפח את ה-bundle. בקיצור: אותו router שעבד לך בפרק 2 עכשיו מאחד את כל ה-API תחת מבנה אחד נקי, בלי שום עלות נוספת.
נקודה אחרונה על ה-passthrough: שים לב שאין בקוד שום route ל-/, ל-/about, או לקבצי ה-CSS/JS. זה מכוון — ה-[assets] מטפל בהם אוטומטית, וה-Worker אפילו לא מתעורר בשבילם. אם כן תכתוב app.get('/', ...), אתה דורס את ה-asset server ומכריח את ה-Worker לרוץ על כל טעינת דף — בדיוק הבזבוז שדיברנו עליו בסעיף הארכיטקטורה. תן ל-assets לעשות את העבודה שלהם.
הוסף ל-Worker שלך route בריאות: app.get('/api/health', c => c.json({ ok: true })). פרוס (wrangler deploy) והרץ curl https://<your-app>/api/health. צריך לחזור {"ok":true}.
- הגדר
type Envעם כל ה-bindings מהתרגיל הקודם (ASSETS, DB, KV, BUCKET, AI, VECTORIZE, ANALYTICS). - צור
const app = new Hono<{ Bindings: Env }>(). - הוסף
GET /api/healthשמחזיר{ok:true}. - הוסף
POST /api/noteשכותב טקסט ל-D1 (INSERT) ומבטל cache ב-KV (KV.delete). - ודא ש-
/ושאר ה-routes הסטטיים מוגשים אוטומטית ע"י[assets](אל תכתוב להם route). - פרוס והרץ
curlעל/api/healthועל/api/note; ואזwrangler d1 execute app-db --command "SELECT * FROM notes"כדי לראות את השורה.
פלט נראה לעין: URL חי שבו / מגיש את ה-SPA, /api/health מחזיר JSON, ו-/api/note כותב שורה ל-D1 — צלם את פלט ה-wrangler d1 execute שמראה את השורה שנכתבה.
חיווט RAG לתוך ה-SaaS — Workers AI + Vectorize בתוך תקציב Neurons
דרישת קדם: כדי ש-/api/ask יחזיר תשובות מבוססות-context (ולא תשובה ריקה), ה-Vectorize index חייב להיות קיים ומאוכלס עם מסמכים — כפי שנלמד ב-פרק 3 (יצירת index, embedding מסמכי דוגמה, upsert). אם דילגת על תרגיל ה-RAG בפרק 3, בצע אותו עכשיו לפני שממשיכים.
עכשיו נותנים לאפליקציה מוח. ה-RAG (Retrieval-Augmented Generation) שבנית בפרק 3 נכנס כעת כ-route אחד ב-Worker המלא. הדפוס: embed השאלה (bge-small-en-v1.5, 384-dim) → VECTORIZE.query עם topK → בנה context מ-top-k → קרא ל-chat model עם system+user. הנה ה-route:
// RAG endpoint: embed -> Vectorize query -> GA chat model (NOT deprecated llama-3.1-8b)
app.post('/api/ask', async (c) => {
const { q } = await c.req.json<{ q: string }>()
const emb = await c.env.AI.run('@cf/baai/bge-small-en-v1.5', { text: [q] })
const hits = await c.env.VECTORIZE.query(emb.data[0], { topK: 3, returnMetadata: true })
const ctx = hits.matches.map((m) => m.metadata?.text).join('\n')
const ans = await c.env.AI.run('@cf/zai-org/glm-4.7-flash', {
messages: [
{ role: 'system', content: `Answer using only:\n${ctx}` },
{ role: 'user', content: q },
],
})
return c.json({ answer: ans.response })
})
למה זה מפתה: כל מדריך, כל דוגמה ב-Stack Overflow, וכל cache של מודל ה-AI שכתב לך את הקוד עדיין ממליצים על @cf/meta/llama-3.1-8b-instruct — זה היה ברירת המחדל החינמית של הפלטפורמה במשך שנה.
למה זה טעות: המודל הזה (יחד עם llama-3/3.1/2, mistral-7b, gemma-7b ועוד) הוסר ב-2026-05-30. קוד שמקובע אליו נשבר בשקט בזמן ריצה — אין שום שגיאת deploy שתזהיר אותך. ה-build עובר, הפריסה מצליחה, ורק קריאת ה-AI הראשונה בשטח נכשלת.
מה לעשות במקום: השתמש רק במודלי GA. האנלוג הקטן/מהיר הקרוב הוא @cf/zai-org/glm-4.7-flash; חלופות GA נוספות: @cf/google/gemma-4-26b-a4b-it או @cf/moonshotai/kimi-k2.6. חפש בקוד את המחרוזת llama-3.1-8b והחלף. (וריאנטים עם -fast/-lora נשארו פעילים — אבל לא שם הבסיס.)
שים לב שיש כאן שתי קריאות AI על כל שאלה: קריאת ה-embedding (bge-small-en-v1.5) שממירה את השאלה לוקטור, וקריאת ה-chat (glm-4.7-flash) שמייצרת את התשובה. שתיהן נספרות מול אותם 10,000 neurons. ה-embedding זול מאוד (בערך 1-3 neurons), אבל ה-chat הוא היקר — ולכן הוא זה שקובע את התקציב. אם אתה מוסיף re-ranking או קריאת LLM נוספת ל-summary, כל אחת מוסיפה לחשבון. כלל אצבע: ספור את מספר קריאות ה-chat לכל בקשת משתמש, וכפול ב-300-600.
על ה-תקציב neurons: יש לך 10,000 neurons/day חינם. קריאת chat ב-glm-4.7-flash עולה בערך 300-600 neurons (תלוי באורך התשובה — output tokens שורפים יותר). זה משאיר לך בערך 15 עד 30 קריאות chat ביום בתקציב החינמי. זה מספיק לפיתוח ולדמו — אבל הוא חושף את האמת הגדולה של הפרק: כל פיצ'ר AI אמיתי בקנה מידה ישבור את ה-free tier כמעט מיד. תחשוב על זה: אם 30 משתמשים שונים ישאלו שאלה אחת כל אחד ביום, מיצית את התקציב. זה הופך את ה-AI ל-trigger הברור ביותר ל-$5, ונחזור לזה במטריצה ובסעיף ה-break-even.
על Vectorize: החיוב הוא לפי dimensions, לא לפי מסמכים. 5M dimensions מאוחסנים חינם, ועם bge-small (384-dim) זה כ-13,000 מסמכים. ה-embeddings וה-storage של ה-vectors כמעט אף פעם לא ישברו ראשונים — ה-neurons של ה-chat ישברו הרבה לפניהם. אם רוצים לחסוך עוד neurons על תשובות חוזרות, נתב את קריאות ה-AI דרך AI Gateway ל-caching (ראינו בפרק 3).
בקוד ה-RAG שלך, ודא ששם מודל הצ'אט הוא '@cf/zai-org/glm-4.7-flash' ולא '@cf/meta/llama-3.1-8b-instruct'. חפש בקובץ את המחרוזת llama-3.1-8b — אם היא קיימת, החלף.
חשב: צ'אט אחד ב-glm-4.7-flash ≈ 300-600 neurons. כמה קריאות צ'אט נשארות לך מתוך 10,000 neurons/day? (חלק 10,000 ב-600 וב-300 לקבלת הטווח — תקבל בערך 17 עד 33.) רשום את המספר.
- צור Vectorize index:
wrangler vectorize create app-rag-index --dimensions=384 --metric=cosine, וחבר אותו ב-[[vectorize]]ב-toml. - הכנס 3-5 מסמכי דוגמה: embed כל אחד עם
bge-small-en-v1.5→upsertל-Vectorize עםmetadata.text. - הוסף
POST /api/ask: embed השאלה →VECTORIZE.queryעםtopK=3→ בנה context מה-matches. - קרא ל-
AI.run('@cf/zai-org/glm-4.7-flash')עם system+user messages, כשה-system מזריק את ה-context. - ודא שאין שום שימוש ב-
llama-3.1-8bבקוד (grep על הקובץ). - חשב כמה קריאות
/api/askנשארות מתוך 10k neurons/day ורשום את המספר במסמך הפרויקט.
פלט נראה לעין: endpoint /api/ask חי שמחזיר תשובה מבוססת-context ממסמכי הדוגמה, עם מודל GA בלבד + חישוב neuron budget כתוב — צלם את תשובת ה-RAG ואת שם המודל בקוד.
גידור ו-Observability — Turnstile, Zero Trust Access ו-Analytics Engine
אפליקציה אמיתית צריכה הגנה מבוטים, גידור של אזורי admin, ומדידה. שלושתם ב-$0, ושלושתם לא bindings ב-toml (חוץ מ-Analytics).
Turnstile — CAPTCHA בלתי נראה. ה-widget בצד הלקוח מחזיר token; ה-Worker מאמת אותו server-side מול challenges.cloudflare.com/turnstile/v0/siteverify עם ה-secret. ה-secret לא בקוד — הוא ב-wrangler secret put TURNSTILE_SECRET. הנה ה-validation בתוך /api/note:
app.post('/api/note', async (c) => {
const { token, text } = await c.req.json<{ token: string; text: string }>()
const verify = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ secret: c.env.TURNSTILE_SECRET, response: token }),
}).then((r) => r.json<{ success: boolean }>())
if (!verify.success) return c.json({ error: 'turnstile failed' }, 403)
await c.env.DB.prepare('INSERT INTO notes (text) VALUES (?)').bind(text).run()
await c.env.KV.delete('notes:list')
return c.json({ ok: true })
})
Zero Trust Access — גידור route לפי זהות, בלי לכתוב קוד auth. מגדירים ב-Zero Trust dashboard ש-/admin/* דורש email-OTP (או Google/GitHub). חינם עד 50 seats — מצוין ל-admin פנימי, לא לכלי ציבורי פתוח (50 הוא hard cap).
הסיבה ש-Turnstile מאומת server-side ולא רק בצד הלקוח קריטית: token של Turnstile שמגיע מהדפדפן הוא חסר ערך עד שהשרת שלך מאמת אותו מול Cloudflare עם ה-secret. תוקף יכול לדלג על ה-widget לגמרי ולשלוח בקשה ישירה ל-/api/note. רק האימות ב-Worker (siteverify שמחזיר success: true) הוא ההגנה האמיתית — ה-widget בצד הלקוח הוא רק חוויית המשתמש. זו טעות נפוצה: לשים את ה-widget ולשכוח את האימות, ואז להישאר פגיע לחלוטין.
Analytics Engine — observability. ה-binding ANALYTICS כן ב-toml. כותבים event מ-middleware על כל בקשת /api/* עם writeDataPoint (blobs למחרוזות, doubles למספרים, indexes לסיווג), ואז query ב-SQL. 100k data points/day חינם. שים לב ש-writeDataPoint רץ אחרי await next() ב-middleware — כלומר אחרי שה-route סיים והתשובה מוכנה — כך שאפשר לרשום את ה-status הסופי. זה גם מה שיאפשר לך, בהמשך, לעקוב אחרי ה-first-limit-to-fall: רשום את סוג הפעולה (write/read/ai) ותוכל לשאול ב-SQL כמה writes ביום אתה באמת עושה, ולראות את הקיר מתקרב לפני שהוא נשבר:
// middleware: log every API hit to Analytics Engine (free beta)
app.use('/api/*', async (c, next) => {
await next()
c.env.ANALYTICS.writeDataPoint({
blobs: [c.req.path, c.req.method],
doubles: [c.res.status],
indexes: ['api'],
})
})
וכשתרצה לבדוק כמה כתיבות אתה באמת עושה ביום — בדיוק המספר שקובע אם KV עומד לשבור אותך — תריץ query כזה מול ה-SQL API של Analytics Engine:
SELECT
blob1 AS path,
count() AS hits
FROM app_events
WHERE timestamp > now() - INTERVAL '1' DAY
GROUP BY path
ORDER BY hits DESC
ה-query הזה הופך את ה-observability מ-"נחמד שיש" ל-כלי ניהול עלות: אתה רואה אילו endpoints נקראים הכי הרבה, וכמה כתיבות הם מייצרים — וזה ההזנה הישירה למטריצת ה-pay-or-not.
הרץ: wrangler secret put TURNSTILE_SECRET — הדבק את ה-secret של ה-widget שלך. אל תשים אותו בקוד או ב-.dev.vars שעולה ל-git.
ב-middleware של /api/*, הוסף שורת writeDataPoint שכותבת את ה-path וה-status ל-Analytics Engine. פרוס ושלח 2-3 בקשות, ואז ודא שהן נצברו.
- הוסף widget של Turnstile ל-frontend ו-
wrangler secret put TURNSTILE_SECRET. - ב-
POST /api/noteהוסף ולידציה server-side: POST ל-https://challenges.cloudflare.com/turnstile/v0/siteverifyעם body{ secret: env.TURNSTILE_SECRET, response: token }; אםsuccess=false→ החזר 403. הקוד המלא מופיע בסעיף Turnstile בפרק זה (ראה "גידור ו-Observability" למעלה), וגם ב-פרק 5, סעיף Turnstile. - הוסף middleware על
/api/*שכותבwriteDataPoint(path, method, status) ל-ANALYTICS. - ב-Zero Trust dashboard, גדר route
/admin/*מאחורי Access עם email-OTP (עד 50 seats). - פרוס; שלח בקשה עם token תקין ובקשה בלי token; ודא 403 על השנייה.
- הרץ SQL query על Analytics Engine (דרך ה-API) וודא שה-events נכתבו.
פלט נראה לעין: endpoint מוגן ש-403 ללא token תקין, route admin מגודר ב-Access, ו-events נצברים ב-Analytics Engine — צלם את תוצאות ה-SQL query ואת דחיית ה-403.
סדר נפילת המגבלות באפליקציה אמיתית — מי שובר ראשון (כלל אצבע)
כל primitive פרסם מגבלה חינמית משלו. אבל באפליקציה אמיתית השאלה היחידה שחשובה היא: מי נשבר ראשון? Cloudflare מפרסמת את כל המגבלות, אבל היא לא מדרגת איזו מגבלה אפליקציה טיפוסית תפגוש קודם. הדירוג שאני נותן לך הוא כלל אצבע / בפועל, נגזר מהמספרים החינמיים — לא הצהרה רשמית.
השיטה פשוטה: לכל primitive, חשב פעולה-לכל-DAU × מספר-DAU × 30 ימים והשווה למגבלה. כשתעשה את זה ל-1,000 DAU, יתגלה דפוס עקבי:
- KV writes (1,000/day) — שובר כמעט תמיד ראשון. זו מגבלה יומית זעירה. 1,000 DAU × write אחד ביום = 1,000 כתיבות — בדיוק על הקיר. שתי כתיבות = פי 2 מעל.
- Workers AI neurons (10,000/day) — שובר שני, אם יש פיצ'ר AI. צ'אט אחד ≈ 300-600 neurons → רק ~15-30 קריאות chat ביום. כל AI בקנה מידה מתפוצץ מיד.
- CPU 10ms — שובר אם יש חישוב כבד ב-Worker (parse של payload גדול, regex, לולאות). לא volume אלא per-invocation.
- D1 writes (100,000/day) — שובר רביעי. 1,000 DAU × 50 כתיבות = 50k — עדיין בתוך החינם, אבל מתקרב.
בוא נראה את החישוב בפועל ל-1,000 DAU, כי המספרים מספרים את כל הסיפור. Workers requests: 1,000 × ~30 בקשות/יום = 30,000/יום — רחוק מ-100k/יום, בסדר גמור. D1 rows written: 1,000 × 50 = 50,000/יום — מתחת ל-100k, אבל מתקרב. D1 rows read: 1,000 × 200 = 200,000/יום — זניח מול 5M/יום. KV writes: 1,000 × אפילו write אחד ביום = 1,000/יום — בדיוק על הקיר; שני writes = פי 2 מעל. Workers AI neurons: צ'אט אחד ≈ 300-600 neurons → רק ~15-30 קריאות chat ביום סה"כ — כל פיצ'ר AI ב-1,000 DAU מתפוצץ מיד. רואה את הדפוס? KV writes ו-neurons הם היחידים שנוגעים בקיר; כל השאר ברווח.
מה כמעט אף פעם לא שובר ראשון: אחסון (R2/D1 ב-GB) ו-Workers requests. 1,000 DAU × 30 בקשות = 30k/day — רחוק מ-100k/day. אל תבזבז דאגה עליהם. וזו תובנה משחררת: אתה לא צריך לעקוב אחרי שמונה מגבלות במקביל. אתה צריך לזהות את ה-אחת או שתיים שיישברו ראשונות באפליקציה שלך, ולשים עליהן עין — את השאר אפשר להתעלם מהן עד שהאפליקציה גדלה בסדר גודל.
שאלה 1 — יש לולאת כתיבה פר-משתמש? (כל משתמש כותב state/log ביום) → KV writes (1k/day) נופל ראשון.
שאלה 2 — אם לא, יש פיצ'ר AI בקנה מידה? → Workers AI neurons (10k/day) נופל ראשון.
שאלה 3 — אם לא, יש חישוב כבד ב-Worker? (parse/regex/לולאות על payload גדול) → CPU 10ms נופל ראשון.
שאלה 4 — אם לא לכל אלה? → D1 writes (100k/day) בסוף, ורק בנפח גבוה. Storage ו-Workers requests כמעט אף פעם לא ראשונים.
זכור: זהו כלל אצבע אינסטרוקטיבי שנגזר מהמספרים, לא דירוג רשמי של Cloudflare.
כתוב פרופיל פעולה ל-DAU של האפליקציה שלך: כמה writes, כמה reads, וכמה AI calls כל משתמש עושה ביום? רשום 3 מספרים. נשתמש בהם בתרגיל המטריצה.
מטריצת Pay-or-Not — לכל Primitive: מגבלה ראשונה, Trigger לשדרוג, עלות ב-1,000 DAU
זה התוצר המרכזי של הקורס. המטריצה ממפה לכל primitive: המגבלה החינמית, המגבלה שנופלת ראשונה, ה-trigger שמכריח שדרוג, ועלות משוערת ב-1,000 DAU. המודל המנטלי לזכור: free = daily caps; $5 = monthly pools. ה-$5 לא רק מגדיל מספרים — הוא ממיר את ה-caps היומיים לבריכות חודשיות נדיבות (KV: מ-1,000 writes/יום ל-1M writes/חודש).
כל מספרי ה-overage למטה מאומתים מול התיעוד הרשמי (מאי 2026). שים לב — אלה דוגמאות מייצגות לפרופיל SaaS טיפוסי; החלף את המספרים בפרופיל ה-DAU שלך.
לפני שתקרא את הטבלה, הפנם את שיטת החישוב, כי זה הלב של כל ההחלטה. לכל primitive אתה לוקח פעולה אחת לכל משתמש פעיל ביום, כופל במספר המשתמשים (כאן 1,000), וכופל ב-30 ימים לקבלת מספר חודשי. אבל הטריק הוא להשוות נכון: ה-free tier מודד ב-caps יומיים, ולכן הסכנה האמיתית היא לא הסכום החודשי אלא היום העמוס ביותר. KV עם 1,000 writes/יום נשבר ברגע ש-1,000 משתמשים כותבים פעם אחת ביום — לא צריך לחכות לסוף החודש. לכן בעמודה "מגבלה ראשונה ליפול" אנחנו מסתכלים על ה-cap היומי, ובעמודת "עלות ב-1,000 DAU" אנחנו כבר מתרגמים לבריכה החודשית של $5.
נניח פרופיל של 2 כתיבות + 5 קריאות + קריאת AI אחת לכל DAU ביום. ב-1,000 DAU זה אומר: 2,000 KV writes/יום (פי 2 מעל מגבלת החינם 1,000 → trigger מיידי לKV), 5,000 KV reads (100k/יום — בסדר), ו-1,000 קריאות AI × ~500 neurons = 500,000 neurons/יום (פי 50 מעל 10,000 → trigger ברור). תוצאה: שני triggers נשברים בו-זמנית ב-1,000 DAU. כשתמלא את המטריצה שלך — החלף את הערכים האלה בפרופיל האמיתי שלך.
שאלה 1: הצריכה היומית של ה-primitive < מגבלה חינמית? → השאר $0.
שאלה 2: 1,000 DAU × פעולה × 30 חוצה את ה-cap היומי? → ה-trigger הופעל → שדרג ל-$5 (ה-cap היומי הופך לבריכה חודשית).
שאלה 3: גם הבריכה החודשית נחצית? → הוסף overage: KV $5/M writes, D1 $1/M writes, neurons $0.011/1k, R2 $0.015/GB-חודש.
| Primitive | מגבלה חינמית | מגבלה ראשונה ליפול | Trigger לשדרוג | עלות ב-1,000 DAU (אומדן) |
|---|---|---|---|---|
| Workers | 100k req/יום; 10ms CPU | CPU (אם יש חישוב כבד), לא volume | CPU > 10ms או > 100k req/יום | ~30k req/יום → $0 (רחוק מהקיר) |
| KV | 100k reads + 1,000 writes/יום | writes — נשבר ראשון | > 1,000 writes/יום (כל לולאת כתיבה) | 2 writes/DAU = 2,000/יום → חוצה → $5; overage $5/M writes |
| D1 | 5M reads + 100k writes/יום; 5GB | writes (בנפח גבוה) | > 100k writes/יום | 50k writes/יום → $0 (קרוב); overage $1/M writes |
| R2 | 10GB; 1M Class A; 10M Class B/חודש | כמעט אף פעם לא ראשון | > 10GB אחסון מצטבר | בד"כ $0; overage $0.015/GB-חודש, $0 egress |
| Workers AI | 10,000 neurons/יום | neurons — שני ליפול | כל פיצ'ר AI בקנה מידה (chat ≈ 300-600 neurons) | פיצ'ר AI אמיתי → חוצה מיד → $5+; overage $0.011/1k neurons |
| Vectorize | 5M stored + 30M queried dims/חודש | כמעט אף פעם לא ראשון | > ~13k מסמכים (bge-small 384-dim) | בד"כ $0 בקנה מידה זה; ה-neurons שוברים לפני |
קרא את הטבלה כסיפור, לא כרשימה. שתי שורות צבועות אדום בראש: KV (writes — נשבר ראשון) ו-Workers AI (neurons — שני ליפול). אלה היחידים שבהם עמודת "עלות ב-1,000 DAU" אומרת $5 ולא $0. כל השאר — Workers, D1, R2, Vectorize — נשארים $0 ברמת ה-1,000 DAU, כי המגבלות שלהם נדיבות בהרבה ביחס לפרופיל הצריכה הטיפוסי. זה לא צירוף מקרים: KV ו-AI הם היחידים עם cap יומי נמוך באמת (1,000 ו-10,000), בעוד שלשאר יש או cap יומי גבוה (D1 100k, Workers 100k) או מדידה לפי storage שלוקח חודשים להצטבר.
וזה בדיוק מה שהופך את ההחלטה לבינארית, כפי שנראה בסעיף הבא: אם יש לך פיצ'ר שכותב ל-KV פר-משתמש, או פיצ'ר AI — שתי השורות האדומות נדלקות, וה-$5 בלתי נמנע. אם אין לך אף אחד מהם, אתה יכול לרוץ ב-$0 חודשים. אין מצב ביניים מורכב; יש שני triggers ברורים. המטריצה היא לא טבלה אקדמית — היא ה-fork בדרך.
למה זה מפתה: פרסת, זה רץ, החשבונית אומרת $0. נראה שניצחת את המערכת לתמיד.
למה זה טעות: בלי מטריצת מגבלות אתה לא רואה את KV writes (1k/יום) או CPU (10ms) נשברים — וכשהם נשברים, ה-API פשוט מתחיל להחזיר שגיאות בלי שום שגיאת deploy שתזהיר אותך מראש. גילית את הקיר מ-bug report של משתמש.
מה לעשות במקום: בנה את מטריצת ה-pay-or-not לפני ה-go-live, סמן איזה primitive שובר ראשון, ועקוב אחריו (ב-Analytics Engine). דע מראש שהיום שבו תעבור 500-1,000 DAU הוא היום שבו KV או neurons ישברו.
בעמודת 'עלות ב-1,000 DAU' של המטריצה שלך, מלא את שורת KV: 1,000 DAU × 2 writes/יום × 30 = 60k writes/חודש. עדיין בתוך הבריכה החודשית של $5 (1M) — אבל ה-cap היומי החינמי (1,000) כבר נשבר. זו בדיוק הנקודה: ה-trigger הוא ה-cap היומי, לא החודשי.
נקודת ה-Break-Even של $5 — למה היא לרוב בלתי נמנעת ולמה היא משתלמת
אחרי שבנית את המטריצה, הדפוס בולט: ההחלטה של $5 היא בינארית, לא הדרגתית. אתה לא "מטפס" לאט לעבר התשלום — או שאתה הרבה מתחת לקיר, או שפעולה אחת מסוימת מפילה אותך. ובפועל יש בדיוק שני triggers שמפילים כמעט כל אפליקציה:
- לולאת כתיבה פר-משתמש — כל פיצ'ר שבו כל משתמש כותב state/log/אירוע ביום. ב-1,000 DAU זה שובר את KV writes (1k/יום) מיד.
- פיצ'ר AI אמיתי בקנה מידה — צ'אט, סיכום, חיפוש סמנטי. ~15-30 קריאות/יום חינם זה כלום לאפליקציה עם משתמשים.
אם יש לך אחד מהשניים — ה-$5 בלתי נמנע, ואין טעם להילחם בזה. וזו לא תבוסה: ה-$5 ממיר את ה-daily caps הזעירים לבריכות חודשיות נדיבות. KV עובר מ-1,000 writes/יום ל-1M writes/חודש — פי 30 ביום-שקול. הקפיצה היא פרופורציונלית ומוצדקת: אתה משלם $5 ומקבל פי-עשרות יותר ראש.
חשוב להבין למה זה לא רק "הגדלת מספרים". ה-free tier מודד ב-caps יומיים דווקא כדי להגן על המערכת מפני spike: 1,000 writes ביום זה גג שמתאפס כל בוקר, ואין דרך "לחסוך" את אתמול להיום. ברגע שאתה ב-$5, המודל מתחלף לחלוטין — אתה מקבל בריכה חודשית שאתה מנצל איך שאתה רוצה. יום עמוס עם 50,000 writes ויום שקט עם 100 — שניהם פשוט נשאבים מאותה בריכה של מיליון לחודש. זו הסיבה ש-1,000/יום (≈30,000/חודש שקול) קופץ ל-1,000,000/חודש: זה לא פי 33 שרירותי, זו החלפת מודל חיוב. הבנה זו היא בדיוק התשובה לשאלת ה-check-yourself "למה $5 הופך daily caps ל-monthly pools ולא רק מגדיל אותם".
ומה אם אתה לא אחד מהשניים — רק קריאות ואחסון קל, בלי לולאת כתיבה פר-משתמש ובלי AI? אז באמת אפשר להישאר $0 לאורך זמן, ואפילו בקנה מידה לא קטן. בלוג סטטי עם תגובות שנשמרות מדי פעם, כלי שמגיש מידע מ-D1 לקריאה בלבד, CDN תמונות על R2 — כל אלה יכולים לרוץ חודשים ב-$0 גם עם אלפי משתמשים, כי הם נוגעים רק במגבלות שכמעט אף פעם לא שוברות ראשונות (reads, requests, storage). ההחלטה הבינארית עובדת לשני הכיוונים: או trigger ברור שמחייב $5, או היעדר trigger שמרשה $0 בלי דאגה.
למה זה מפתה: "כל הקורס היה על להישאר ב-$0 — לשלם זה להודות בכישלון." יש פיתוי רגשי להישאר חינמי בכל מחיר.
למה זה טעות: כשה-trigger כבר הופעל, ה-cap היומי נשבר, ומשתמשים מקבלים שגיאות 4006/429 — אתה מפסיד אמינות. אתה לא חוסך $5, אתה שורף משתמשים אמיתיים בשביל גאווה.
מה לעשות במקום: כשה-trigger מופעל (לולאת כתיבה פר-משתמש או פיצ'ר AI בקנה מידה) — שדרג ל-$5. זו החלטה בינארית מבוססת מספרים, לא רגש. ה-$5 ממיר daily caps ל-monthly pools — קפיצה מוצדקת.
שאלה 1 — יש לולאת כתיבה פר-משתמש? → $5 בלתי נמנע. עצור כאן, שדרג.
שאלה 2 — יש פיצ'ר AI אמיתי בקנה מידה? → $5 בלתי נמנע. עצור כאן, שדרג.
שאלה 3 — רק קריאות ואחסון קל? → אפשר להישאר $0 לאורך זמן. עקוב במטריצה.
מודל מנטלי: free = daily caps; $5 = monthly pools. ההחלטה בינארית — או הרבה מתחת לקיר, או trigger יחיד שמפיל.
ענה לעצמך בכן/לא: האם לאפליקציה שלך יש (א) לולאת כתיבה פר-משתמש, או (ב) פיצ'ר AI אמיתי? אם כן לאחד מהם — סמן שאתה כנראה תצטרך $5. זו ההחלטה הבינארית.
מה לא לשים על Free Tier ל-Production + Audit סיכוני GA/Beta/Deprecation
לא כל מה שזמין בחינם בטוח ל-production. שלוש קטגוריות סיכון שחובה לעבור עליהן לפני go-live:
1. מה שאין בכלל ב-free tier — אל תבנה עליהם בהנחה שהם חינמיים: Cloudflare Stream (אין free tier אחסון/delivery), Cloudflare Images storage (paid-only — רק ה-Transforms חינמיים), Email sending (דורש Workers Paid; קבלת email דרך Email Routing חינמית), ו-Containers (paid-only).
2. GA מול Beta — מה יציב ומה עלול להשתנות. נכון למאי 2026:
| מצב | מוצרים | מה זה אומר ל-production |
|---|---|---|
| GA — בטוח | Workers, Static Assets, KV, D1, R2, Durable Objects (SQLite), Workers AI, Vectorize, Turnstile, Zero Trust Access, AI Search | API ומחיר יציבים. בנה עליהם core flow ללא חשש. |
| BETA — בזהירות | Email Service, Analytics Engine, Secrets Store | API/מחיר עלולים להשתנות עם 30-day notice, אין SLA. עטוף ב-feature-flag, אל תתלה core flow. |
שים לב מיוחד ל-Analytics Engine: הוא חינמי ב-2026, אבל Cloudflare כבר פרסמה מחיר עתידי ($0.25/M writes, $1/M reads) שעדיין לא הופעל. כלומר זה "חינם בינתיים, מתומחר אחר כך" — לא "חינם לנצח". אל תבנה עליו תלות קריטית שתכאיב כשהחיוב יידלק.
3. מודלים מסומנים להסרה — כפי שראינו בסעיף ה-RAG, llama-3.1-8b-instruct (וכ-17 מודלים נוספים) הוסרו ב-2026-05-30. קוד שמקובע אליהם נשבר בשקט. השתמש רק ב-GA: glm-4.7-flash, gemma-4-26b-a4b-it, kimi-k2.6.
למה ה-deprecation של מודל מסוכן יותר מ-beta של מוצר? כי מוצר ב-beta נותן לך 30-day notice ואז משנה מחיר — אתה עדיין רץ, רק משלם. מודל שהוסר פשוט מפסיק לענות, וה-deploy שלך עובר בלי שום אזהרה כי שם המודל הוא רק string. אין compiler שיתפוס string שגוי. זו הסיבה שהפכנו את "מודלי GA בלבד" לסעיף הראשון ב-production checklist, ולמה כדאי לרכז את שם המודל ב-constant אחד בקוד (או ב-env var) — כך שביום שגם glm-4.7-flash יסומן להסרה, תחליף במקום אחד ולא תחפש בעשרים קבצים.
הבחנה אחרונה ל-audit: proxied models (ניתוב דרך AI Gateway ל-OpenAI/Anthropic) הם לא חלק מסיפור ה-$0-to-$5 בכלל. הם 0 neurons חינמיים ומחויבים אצל הצד השלישי מהקריאה הראשונה. אם המטריצה שלך כוללת AI, ודא שאתה מדבר על hosted models (שרצים על Cloudflare ונספרים מול 10k neurons) ולא על proxied — אחרת החישוב שלך מנותק מהמציאות.
למה זה מפתה: Analytics Engine ו-AI Search עובדים מצוין ב-$0, אז קל להפוך אותם ל-core של ה-flow.
למה זה טעות: AI Search כבר GA — בסדר. אבל Email Service / Analytics Engine / Secrets Store עדיין beta. מחיר ומגבלה עלולים להשתנות עם 30-day notice, ואין SLA. core flow שתלוי בהם נמצא בסיכון שאתה לא שולט בו.
מה לעשות במקום: הרץ את ה-GA-vs-beta audit (הטבלה למעלה). עטוף כל מוצר beta ב-feature-flag, ואל תבנה עליו core flow קריטי — שיהיה observability/nice-to-have, לא עמוד התווך.
אם המוצר GA (Workers, Static Assets, KV, D1, R2, DO-SQLite, Workers AI, Vectorize, Turnstile, Access, AI Search) → בטוח לבנות עליו production core flow.
אם המוצר BETA (Email Service, Analytics Engine, Secrets Store) → API/מחיר עלולים להשתנות עם 30-day notice → עטוף ב-feature-flag, אל תתלה עליו core.
אם מודל מסומן deprecation (llama-3.1-8b וכו', הוסר 2026-05-30) → אל תקבע אותו בקוד; השתמש ב-GA (glm-4.7-flash).
עבור על כל ה-bindings ב-wrangler.toml שלך וסמן ליד כל אחד GA או BETA לפי הטבלה. אם משהו BETA (Analytics Engine) — הוסף הערה שצריך feature-flag ולא לתלות בו core flow.
Production Checklist — לפני שאתה אומר 'זה חי'
האפליקציה רצה ב-wrangler dev — אבל "רץ אצלי" זה לא "production". הנה רשימת הבדיקות שמפרידה בין השניים, כל אחת נגזרת מבאג אמיתי שראינו לאורך הקורס:
- מודלי GA בלבד — אל תקבע
@cf/meta/llama-3.1-8b-instruct(הוסר 2026-05-30). השתמש ב-glm-4.7-flash/gemma-4-26b-a4b-it/kimi-k2.6. - secrets ב-
wrangler secret put— לעולם לא בקוד ולא ב-.dev.varsשעולה ל-git (.dev.varsהוא local-only). .assetsignore— Static Assets לא מחריגnode_modules/.git/.DS_Storeאוטומטית. בלי הקובץ הזה אתה עלול להעלות עשרות אלפי קבצים ולפגוע במגבלת ה-20,000.- smoke test ב-
wrangler dev --remote— Miniflare הלוקלי מתפצל מ-production ב-DO alarms, KV TTL propagation ו-R2 multipart. בדוק מול הענן לפני deploy. - named tunnel ל-SSE — אם האפליקציה משתמשת ב-streaming/SSE, השתמש ב-named tunnel ולא ב-Quick Tunnel (trycloudflare) — Quick Tunnel לא תומך SSE ומוגבל ל-200 concurrent.
- beta = feature-flag — עטוף Email Service / Analytics Engine / Secrets Store; אין ערובת יציבות.
- binding names =
Envinterface — ודא שכל שם ב-wrangler.tomlתואם בדיוק ל-type Envב-Worker. אי-התאמה =undefinedבזמן ריצה. compatibility_date+nodejs_compat— תאריך עדכני, ו-flag אם תלות כלשהי צריכה Node built-ins.
צור קובץ .assetsignore בתיקיית ה-assets שלך עם השורות: node_modules, .git, .DS_Store. שמור. זה מונע העלאה של עשרות אלפי קבצים ופגיעה במגבלת 20,000 הקבצים.
Capstone — פריסת ה-SaaS המלא + מסמך ה-Pay-or-Not לפרויקט שלך
הגענו לסוף. הכל מחובר: wrangler.toml אחד עם 7 bindings, Worker אחד עם Hono, SPA מוגש מ-[assets], D1 ל-נתונים, KV ל-cache, R2 ל-קבצים, RAG עם מודל GA, מגודר ב-Turnstile ו-Access, ונמדד ב-Analytics Engine. עכשיו פורסים את הדבר השלם, ובונים את ה-deliverable שמלווה אותו — מסמך ה-pay-or-not.
זה רגע הקאפסטון: לא רק "האפליקציה חיה", אלא "אני יודע במספרים מתי היא תעלה כסף ולמה". מסמך ה-pay-or-not הוא מה שהופך אותך מ-Vibe Coder שמקווה שיישאר ב-$0, ל-Vibe Coder שיודע מתי לשלם ומקבל החלטה מבוססת-מספרים.
שים לב מה אתה מסכם כאן, כי זה כל הקורס בתמצית. פרק 1 נתן לך את הבסיס — Worker + Static Assets ואת ההבנה מה באמת חינמי. פרק 2 נתן את שכבת האחסון ואת קיר ה-10ms. פרק 3 הוסיף את ה-AI ואת מטבע ה-neurons. פרק 4 את ה-media. פרק 5 את ה-glue שמגדר ומודד. כל אחד מהם היה "מיומנות". הקאפסטון לוקח את כל המיומנויות והופך אותן ל-שיקול דעת: לא "איך משתמשים ב-KV" אלא "האם KV ב-architecture הזה ישבור אותי ראשון, ומה אני עושה עם זה". זה ההבדל בין מי שיודע להפעיל primitives למי שיודע לתכנן מערכת ולתמחר אותה.
והמסמך שאתה בונה עכשיו הוא לא תרגיל חד-פעמי — הוא תבנית. בכל פרויקט Cloudflare שתתחיל מעכשיו, תפתח את PAY-OR-NOT.md, תמלא את פרופיל ה-DAU הצפוי, ותדע תוך חמש דקות אם זה $0 או $5, ואיזו מגבלה לשים עליה עין. זה ההרגל שמפריד בין "נתקעתי כי ה-API התחיל להחזיר שגיאות ולא הבנתי למה" לבין "ידעתי מראש שביום שאעבור 500 DAU ה-KV writes יישברו, אז שדרגתי בזמן".
הרץ wrangler deploy על האפליקציה המלאה. פתח את ה-URL החי, שלח בקשת /api/ask אחת, וודא שחזרה תשובת RAG. צלם את התוצאה — זו ההוכחה שכל החלקים מ-1-5 חיים יחד.
- רשום את פרופיל ה-DAU של האפליקציה שלך (writes, reads, AI calls ליום למשתמש) — מה-do-now של סעיף 7.
- בנה טבלה: שורה לכל primitive (Workers, KV, D1, R2, Workers AI, Vectorize).
- לכל primitive מלא: מגבלה חינמית → המגבלה שנופלת ראשונה → trigger לשדרוג → עלות משוערת ב-1,000 DAU → המלצה.
- חשב
1,000 DAU × פעולה × 30לכל primitive והשווה למגבלה (היומית-שקולה). - סמן איזה primitive שובר ראשון את ה-free tier באפליקציה שלך.
- כתוב משפט החלטה אחד: $0 או $5, ולמה — מבוסס מספרים, לא תחושה.
פלט נראה לעין: מסמך מטריצת pay-or-not מלא (טבלה + משפט החלטה) לאפליקציה האמיתית שלך — הפלט המרכזי של הקאפסטון, ניתן לשמירה כ-Markdown/PDF. זה המסמך שתפתח בכל deploy עתידי.
שגרת עבודה
| מתי | פעולה |
|---|---|
| בתחילת כל פרויקט full-stack | הכרז את כל ה-bindings ב-wrangler.toml אחד; ודא ששמותיהם תואמים ל-type Env. |
| לפני כל קביעת מודל AI בקוד | בדוק שה-string GA וחי (לא llama deprecated); רכז את שם המודל בנקודה אחת בקוד. |
| לפני go-live | עבור על ה-production checklist (8 סעיפים) + הרץ GA-vs-beta audit על כל binding. |
| לפני go-live (קריטי) | בנה את מטריצת ה-pay-or-not; סמן את ה-first-limit-to-fall ועקוב אחריו ב-Analytics Engine. |
| כשה-trigger מופעל | שדרג ל-$5 — החלטה בינארית מבוססת מספרים, לא רגש. ה-$5 ממיר daily caps ל-monthly pools. |
שמור את מסמך ה-pay-or-not שבנית כקובץ PAY-OR-NOT.md ב-repo של הפרויקט, ליד ה-README. הרגע שבו אתה רואה את ה-first-limit-to-fall שלך כתוב במספרים — ולא כחשש מעורפל — הוא הרגע שבו כל הקורס הופך ממושגים להחלטה. זה המסמך היחיד הכי גבוה-מינוף שלקחת מכאן.
- למה KV writes שובר את ה-free tier לפני Workers requests, גם כשמספר הבקשות גדול ממספר הכתיבות? (רמז: השווה 1,000 writes/יום ל-100,000 requests/יום — איזה cap זעיר יותר.)
- למה ה-$5 הופך daily caps ל-monthly pools ולא רק מגדיל את ה-caps היומיים? (רמז: מודל החיוב — daily ל-free, monthly pool ל-paid; KV 1k/יום → 1M/חודש.)
- איך תזהה שקוד RAG שכתב לך מדריך/AI ישן ישבר בשטח, גם אם ה-deploy עובר? (רמז: חפש
llama-3.1-8b— נשבר בשקט בזמן ריצה ב-2026-05-30.) - למה
[ai]נכתב עם סוגריים בודדים בעוד[[d1_databases]]עם כפולים, ומה קורה אם תטעה? (רמז: טבלה יחידנית מול מערך — שגיאת config שקטה.) - מתי תבנה core flow על Analytics Engine ומתי לא, ולמה? (רמז: beta + מחיר עתידי פורסם אך לא הופעל + 30-day notice.)
נכנסת לקורס עם כלי AI שכותבים לך קוד, ויצאת עם full-stack SaaS חי על ה-free tier: Worker אחד עם Hono, SPA מ-Static Assets, D1 ל-נתונים, KV ל-cache, R2 ל-קבצים, RAG עם מודל GA, מגודר ב-Turnstile ו-Access, ונמדד ב-Analytics Engine — הכל מ-wrangler.toml יחיד. שישה פרקים בנו את החלקים; הפרק הזה חיבר אותם ל-דבר אחד.
אבל ה-deliverable האמיתי של הקורס הוא לא רק הקוד — אלא ההחלטה מבוססת-המספרים. בנית מטריצת pay-or-not שאומרת לך לכל primitive מה המגבלה שתיפול ראשונה (כלל אצבע: KV writes ו-neurons), מתי ה-$5 בלתי נמנע (לולאת כתיבה פר-משתמש או פיצ'ר AI), ולמה הקפיצה משתלמת (daily caps → monthly pools). למדת שההחלטה בינארית, לא הדרגתית, ושהיא לא עניין של גאווה.
ולקחת הרגלים שיישארו: לאמת מודלים מול הקטלוג החי (כי llama-3.1-8b כבר לא שם), להריץ GA-vs-beta audit לפני production, להכריז bindings בקובץ אחד, ולבנות את מסמך ה-pay-or-not לפני go-live — לא אחרי שמשתמש מתלונן. אין פרק הבא; זה הסיום. מכאן והלאה, כל פרויקט שתתחיל מתחיל עם הידע מתי הוא $0 ומתי $5 — ולמה.
צ'קליסט — סיכום פרק 6
- הרכבתי
wrangler.tomlאחד עם 7 bindings ([assets], D1, KV, R2,[ai], Vectorize, Analytics) שנטען ב-wrangler devללא שגיאה. - אני יודע ש-
[ai]טבלה יחידנית (סוגריים בודדים) ושכל השאר משתמשים ב-[[ ]]כפולים. - אני מבין ש-Turnstile ו-Zero Trust Access אינם bindings ב-toml — secret/siteverify ו-dashboard בהתאמה.
- בניתי Hono router אחד עם
type Envמוקלד,/api/health, ו-/api/noteשכותב ל-D1 ומבטל cache ב-KV. - ה-SPA מוגש אוטומטית דרך
[assets](not_found_handling=single-page-application+run_worker_first=["/api/*"]) — בלי route ידני. - חיווטתי RAG ב-
/api/ask: embed (bge-small) →VECTORIZE.query→glm-4.7-flash— מודל GA בלבד. - חיפשתי
llama-3.1-8bבקוד וּוידאתי שהוא לא מקובע (הוסר 2026-05-30, נשבר בשקט). - חישבתי שצ'אט ≈ 300-600 neurons → ~15-30 קריאות/יום חינם, והבנתי שכל AI בקנה מידה מחייב $5.
- גידרתי
/api/noteב-Turnstile (siteverifyserver-side, 403 ללא token) ו-/admin/*ב-Access. - הוספתי middleware עם
writeDataPointל-Analytics Engine ואימתתי events ב-SQL. - אני יודע את סדר נפילת המגבלות (כלל אצבע): KV writes → neurons → CPU → D1 writes.
- בניתי מטריצת pay-or-not מלאה: לכל primitive מגבלה → first-to-fall → trigger → עלות ב-1,000 DAU.
- אני מבין שההחלטה בינארית: לולאת כתיבה פר-משתמש או פיצ'ר AI → $5 בלתי נמנע; free=daily caps, $5=monthly pools.
- הרצתי GA-vs-beta audit: GA בטוח, beta (Email/Analytics/Secrets Store) ב-feature-flag, Analytics 'חינם בינתיים'.
- עברתי על production checklist (GA models, secrets,
.assetsignore,--remote, named tunnel, binding names). - פרסתי את ה-SaaS המלא, שלחתי
/api/askחי, ושמרתיPAY-OR-NOT.mdעם משפט החלטה מבוסס-מספרים.