6 שלב האינטגרציה

Full-Stack חינמי — חיווט הכל ביחד, והחלטת Pay-or-Not

חמישה פרקים בנית חלקים: Worker, אחסון, AI, media ו-glue. בפרק הזה — האחרון — מחברים את כולם ל-SaaS אחד חי שרץ על ה-free tier בלבד: wrangler.toml יחיד עם כל ה-bindings, Hono כ-router אחד, RAG עם מודל GA, וגידור ב-Turnstile ו-Access. ואז עושים את הדבר הכי חשוב שלא עשינו עד עכשיו — בונים מטריצת pay-or-not מבוססת מספרים שעונה: איזו מגבלה תיפול ראשונה, מתי ה-$5 בלתי נמנע, וכמה זה יעלה ב-1,000 משתמשים פעילים.

מה יהיה לך ביד בסוף הפרק
מטרות למידה
מה צריך לפני שמתחילים
חוט הפרויקט

הפרקים הקודמים (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. הקורס נסגר עם דבר אמיתי פרוס, לא עם תרגיל.

מילון מונחים — פרק 6
מונחבעבריתהסבר
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) — כולל שמות מודלים מסומנים להסרה.
מתחיל7 דקותחינםמושג

מהפרקים הבודדים לאפליקציה אחת — מה אנחנו מחברים עכשיו

חמישה פרקים בנית חלקים. עכשיו תעצור רגע ותסתכל על מה שיש לך ביד: 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 ב-בחירה, לא ב-פאניקה.

עשו עכשיו 4 דקות

פתח את wrangler.toml של הפרויקט שבנית בפרק 1 ורשום בצד אילו bindings כבר יש לך מפרקים 2-5 (KV? D1? R2? AI? Vectorize?). זו רשימת ה-inventory שנחבר עכשיו ל-Worker אחד.

מתחיל8 דקותחינםמושג

ארכיטקטורת ה-Free Full-Stack — מפת המוצרים על דף אחד

לפני קוד, מפה. הנה איך נראה SaaS שלם שרץ כולו על ה-free tier, ב-primitive אחד למרכז — ה-Worker עם Hono — וכל השאר תלוי בו כ-bindings:

הנקודה הקריטית להבנה היא זרימת הבקשה. בקשה מגיעה ל-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).

SaaS מלא על free tier — דף אחד, Worker אחד בקשה מהדפדפן Cloudflare edge [assets] — קובץ סטטי? SPA dist · מוגש מיד · $0 אם /api/* → fallthrough Worker — Hono router env.* · 10ms CPU D1 (DB) R2 (BUCKET) KV Workers AI Vectorize Analytics Engine Turnstile + Zero Trust Access לא bindings — secret/siteverify + dashboard הכל $0 עד המגבלה הראשונה ראה מטריצת pay-or-not בהמשך
עשו עכשיו 5 דקות

צייר על נייר את זרימת הבקשה: דפדפן → Cloudflare edge → [assets] (קובץ סטטי?) → אם /api/* → Worker (Hono). סמן ליד החץ איפה כל primitive נכנס (D1 על write, KV על cache, AI על RAG). תחזיק את הציור הזה ליד התרגילים.

בינוני10 דקותחינםתרגיל-מונחה

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.

שלוש דקויות שמפילות אנשים, ושחובה לקלוט:

שווה להבין מה כל מפתח עושה, כי כל אחד פותר בעיה אמיתית. 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 הם שתי תמונות של אותו דבר.

עשו עכשיו 3 דקות

בקובץ wrangler.toml שלך, הוסף את הטבלה [ai] עם שורה אחת בלבד: binding = "AI". שים לב שזו טבלה יחידנית — [ai], לא [[ai]] עם סוגריים כפולים.

עשו עכשיו 3 דקות

ודא ש-compatibility_date בקובץ שלך הוא תאריך עדכני (למשל 2026-05-01) ושיש compatibility_flags = ["nodejs_compat"]. שמור.

תרגיל: הרכבת wrangler.toml אחד עם כל ה-Bindings 20 דקות
  1. קח את ה-wrangler.toml מפרק 1 כבסיס.
  2. הוסף [assets] עם directory, binding = "ASSETS", not_found_handling = "single-page-application", ו-run_worker_first = ["/api/*"].
  3. הוסף [[d1_databases]] (binding=DB), [[kv_namespaces]] (binding=KV), ו-[[r2_buckets]] (binding=BUCKET).
  4. הוסף [ai] (binding=AI, טבלה יחידנית), [[vectorize]] (binding=VECTORIZE, index_name), ו-[[analytics_engine_datasets]] (binding=ANALYTICS, dataset).
  5. ודא compatibility_date עדכני + nodejs_compat.
  6. הרץ npx wrangler dev והוכח שהקובץ נטען בלי שגיאת config — wrangler ידפיס את רשימת ה-bindings שנטענו.

פלט נראה לעין: קובץ wrangler.toml יחיד שמכריז על 7 bindings ונטען ב-wrangler dev ללא שגיאות — צלם את פלט הטרמינל שמציג את שבעת ה-bindings.

בינוני10 דקותחינםתרגיל-מונחה

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 לעשות את העבודה שלהם.

עשו עכשיו 5 דקות

הוסף ל-Worker שלך route בריאות: app.get('/api/health', c => c.json({ ok: true })). פרוס (wrangler deploy) והרץ curl https://<your-app>/api/health. צריך לחזור {"ok":true}.

תרגיל: חיווט Hono Router עם SPA Passthrough + Route אחד מעל D1 25 דקות
  1. הגדר type Env עם כל ה-bindings מהתרגיל הקודם (ASSETS, DB, KV, BUCKET, AI, VECTORIZE, ANALYTICS).
  2. צור const app = new Hono<{ Bindings: Env }>().
  3. הוסף GET /api/health שמחזיר {ok:true}.
  4. הוסף POST /api/note שכותב טקסט ל-D1 (INSERT) ומבטל cache ב-KV (KV.delete).
  5. ודא ש-/ ושאר ה-routes הסטטיים מוגשים אוטומטית ע"י [assets] (אל תכתוב להם route).
  6. פרוס והרץ 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 שמראה את השורה שנכתבה.

מתקדם11 דקותחינםתרגיל-מונחה

חיווט 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 })
})
טעות נפוצה: לקבע בקוד שם מודל deprecated (llama-3.1-8b-instruct)

למה זה מפתה: כל מדריך, כל דוגמה ב-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).

עשו עכשיו 4 דקות

בקוד ה-RAG שלך, ודא ששם מודל הצ'אט הוא '@cf/zai-org/glm-4.7-flash' ולא '@cf/meta/llama-3.1-8b-instruct'. חפש בקובץ את המחרוזת llama-3.1-8b — אם היא קיימת, החלף.

עשו עכשיו 4 דקות

חשב: צ'אט אחד ב-glm-4.7-flash ≈ 300-600 neurons. כמה קריאות צ'אט נשארות לך מתוך 10,000 neurons/day? (חלק 10,000 ב-600 וב-300 לקבלת הטווח — תקבל בערך 17 עד 33.) רשום את המספר.

תרגיל: הוספת RAG עם Workers AI (glm-4.7-flash) + Vectorize בתוך תקציב Neurons 30 דקות
  1. צור Vectorize index: wrangler vectorize create app-rag-index --dimensions=384 --metric=cosine, וחבר אותו ב-[[vectorize]] ב-toml.
  2. הכנס 3-5 מסמכי דוגמה: embed כל אחד עם bge-small-en-v1.5upsert ל-Vectorize עם metadata.text.
  3. הוסף POST /api/ask: embed השאלה → VECTORIZE.query עם topK=3 → בנה context מה-matches.
  4. קרא ל-AI.run('@cf/zai-org/glm-4.7-flash') עם system+user messages, כשה-system מזריק את ה-context.
  5. ודא שאין שום שימוש ב-llama-3.1-8b בקוד (grep על הקובץ).
  6. חשב כמה קריאות /api/ask נשארות מתוך 10k neurons/day ורשום את המספר במסמך הפרויקט.

פלט נראה לעין: endpoint /api/ask חי שמחזיר תשובה מבוססת-context ממסמכי הדוגמה, עם מודל GA בלבד + חישוב neuron budget כתוב — צלם את תשובת ה-RAG ואת שם המודל בקוד.

בינוני9 דקותחינםתרגיל-מונחה

גידור ו-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.

עשו עכשיו 4 דקות

הרץ: wrangler secret put TURNSTILE_SECRET — הדבק את ה-secret של ה-widget שלך. אל תשים אותו בקוד או ב-.dev.vars שעולה ל-git.

עשו עכשיו 5 דקות

ב-middleware של /api/*, הוסף שורת writeDataPoint שכותבת את ה-path וה-status ל-Analytics Engine. פרוס ושלח 2-3 בקשות, ואז ודא שהן נצברו.

תרגיל: גידור ב-Turnstile + Access וכתיבת Events ל-Analytics Engine 25 דקות
  1. הוסף widget של Turnstile ל-frontend ו-wrangler secret put TURNSTILE_SECRET.
  2. ב-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.
  3. הוסף middleware על /api/* שכותב writeDataPoint (path, method, status) ל-ANALYTICS.
  4. ב-Zero Trust dashboard, גדר route /admin/* מאחורי Access עם email-OTP (עד 50 seats).
  5. פרוס; שלח בקשה עם token תקין ובקשה בלי token; ודא 403 על השנייה.
  6. הרץ SQL query על Analytics Engine (דרך ה-API) וודא שה-events נכתבו.

פלט נראה לעין: endpoint מוגן ש-403 ללא token תקין, route admin מגודר ב-Access, ו-events נצברים ב-Analytics Engine — צלם את תוצאות ה-SQL query ואת דחיית ה-403.

מתקדם9 דקותחינםמסגרת-מונחה

סדר נפילת המגבלות באפליקציה אמיתית — מי שובר ראשון (כלל אצבע)

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

השיטה פשוטה: לכל primitive, חשב פעולה-לכל-DAU × מספר-DAU × 30 ימים והשווה למגבלה. כשתעשה את זה ל-1,000 DAU, יתגלה דפוס עקבי:

  1. KV writes (1,000/day) — שובר כמעט תמיד ראשון. זו מגבלה יומית זעירה. 1,000 DAU × write אחד ביום = 1,000 כתיבות — בדיוק על הקיר. שתי כתיבות = פי 2 מעל.
  2. Workers AI neurons (10,000/day) — שובר שני, אם יש פיצ'ר AI. צ'אט אחד ≈ 300-600 neurons → רק ~15-30 קריאות chat ביום. כל AI בקנה מידה מתפוצץ מיד.
  3. CPU 10ms — שובר אם יש חישוב כבד ב-Worker (parse של payload גדול, regex, לולאות). לא volume אלא per-invocation.
  4. 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.

סולם נפילת המגבלות — מי שובר ראשון (כלל אצבע) 1 · KV writes — 1,000/יום לולאת כתיבה פר-משתמש שוברת מיד ראשון ליפול 2 · Workers AI neurons — 10,000/יום ~15-30 קריאות chat — כל AI בקנה מידה 3 · CPU — 10ms לכל invocation parse/regex/לולאות כבדות 4 · D1 writes — 100,000/יום storage ו-requests כמעט אף פעם לא ראשונים
עשו עכשיו 5 דקות

כתוב פרופיל פעולה ל-DAU של האפליקציה שלך: כמה writes, כמה reads, וכמה AI calls כל משתמש עושה ביום? רשום 3 מספרים. נשתמש בהם בתרגיל המטריצה.

מתקדם11 דקותחינםמסגרת-מונחה

מטריצת 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.

פרופיל DAU לדוגמה — עגנו את החישוב

נניח פרופיל של 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. כשתמלא את המטריצה שלך — החלף את הערכים האלה בפרופיל האמיתי שלך.

מסגרת החלטה: Pay-or-Not לכל primitive

שאלה 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 בדרך.

טעות נפוצה: להניח ש-full-stack פרוס יישאר $0 לנצח

למה זה מפתה: פרסת, זה רץ, החשבונית אומרת $0. נראה שניצחת את המערכת לתמיד.

למה זה טעות: בלי מטריצת מגבלות אתה לא רואה את KV writes (1k/יום) או CPU (10ms) נשברים — וכשהם נשברים, ה-API פשוט מתחיל להחזיר שגיאות בלי שום שגיאת deploy שתזהיר אותך מראש. גילית את הקיר מ-bug report של משתמש.

מה לעשות במקום: בנה את מטריצת ה-pay-or-not לפני ה-go-live, סמן איזה primitive שובר ראשון, ועקוב אחריו (ב-Analytics Engine). דע מראש שהיום שבו תעבור 500-1,000 DAU הוא היום שבו KV או neurons ישברו.

עשו עכשיו 5 דקות

בעמודת 'עלות ב-1,000 DAU' של המטריצה שלך, מלא את שורת KV: 1,000 DAU × 2 writes/יום × 30 = 60k writes/חודש. עדיין בתוך הבריכה החודשית של $5 (1M) — אבל ה-cap היומי החינמי (1,000) כבר נשבר. זו בדיוק הנקודה: ה-trigger הוא ה-cap היומי, לא החודשי.

בינוני9 דקות$0/$5מסגרת-מונחה

נקודת ה-Break-Even של $5 — למה היא לרוב בלתי נמנעת ולמה היא משתלמת

אחרי שבנית את המטריצה, הדפוס בולט: ההחלטה של $5 היא בינארית, לא הדרגתית. אתה לא "מטפס" לאט לעבר התשלום — או שאתה הרבה מתחת לקיר, או שפעולה אחת מסוימת מפילה אותך. ובפועל יש בדיוק שני triggers שמפילים כמעט כל אפליקציה:

אם יש לך אחד מהשניים — ה-$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 בלי דאגה.

טעות נפוצה: לדחות את ה-$5 כעניין של גאווה כשהאפליקציה כבר חוצה את הקיר

למה זה מפתה: "כל הקורס היה על להישאר ב-$0 — לשלם זה להודות בכישלון." יש פיתוי רגשי להישאר חינמי בכל מחיר.

למה זה טעות: כשה-trigger כבר הופעל, ה-cap היומי נשבר, ומשתמשים מקבלים שגיאות 4006/429 — אתה מפסיד אמינות. אתה לא חוסך $5, אתה שורף משתמשים אמיתיים בשביל גאווה.

מה לעשות במקום: כשה-trigger מופעל (לולאת כתיבה פר-משתמש או פיצ'ר AI בקנה מידה) — שדרג ל-$5. זו החלטה בינארית מבוססת מספרים, לא רגש. ה-$5 ממיר daily caps ל-monthly pools — קפיצה מוצדקת.

מסגרת החלטה: נקודת ה-break-even של $5

שאלה 1 — יש לולאת כתיבה פר-משתמש? → $5 בלתי נמנע. עצור כאן, שדרג.

שאלה 2 — יש פיצ'ר AI אמיתי בקנה מידה? → $5 בלתי נמנע. עצור כאן, שדרג.

שאלה 3 — רק קריאות ואחסון קל? → אפשר להישאר $0 לאורך זמן. עקוב במטריצה.

מודל מנטלי: free = daily caps; $5 = monthly pools. ההחלטה בינארית — או הרבה מתחת לקיר, או trigger יחיד שמפיל.

עץ ההחלטה: $0 או $5? לולאת כתיבה פר-משתמש? כן לא פיצ'ר AI בקנה מידה? כן לא $5 בלתי נמנע daily caps → monthly pools · קפיצה פרופורציונלית השאר $0 עקוב במטריצה
עשו עכשיו 3 דקות

ענה לעצמך בכן/לא: האם לאפליקציה שלך יש (א) לולאת כתיבה פר-משתמש, או (ב) פיצ'ר AI אמיתי? אם כן לאחד מהם — סמן שאתה כנראה תצטרך $5. זו ההחלטה הבינארית.

בינוני10 דקותחינםמסגרת-מונחה

מה לא לשים על 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 — אחרת החישוב שלך מנותק מהמציאות.

טעות נפוצה: לבנות production על מוצרי beta בלי audit

למה זה מפתה: 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 מול Beta — מה בטוח ל-Production

אם המוצר 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).

עשו עכשיו 5 דקות

עבור על כל ה-bindings ב-wrangler.toml שלך וסמן ליד כל אחד GA או BETA לפי הטבלה. אם משהו BETA (Analytics Engine) — הוסף הערה שצריך feature-flag ולא לתלות בו core flow.

בינוני9 דקותחינםצ'קליסט

Production Checklist — לפני שאתה אומר 'זה חי'

האפליקציה רצה ב-wrangler dev — אבל "רץ אצלי" זה לא "production". הנה רשימת הבדיקות שמפרידה בין השניים, כל אחת נגזרת מבאג אמיתי שראינו לאורך הקורס:

  1. מודלי GA בלבד — אל תקבע @cf/meta/llama-3.1-8b-instruct (הוסר 2026-05-30). השתמש ב-glm-4.7-flash / gemma-4-26b-a4b-it / kimi-k2.6.
  2. secrets ב-wrangler secret put — לעולם לא בקוד ולא ב-.dev.vars שעולה ל-git (.dev.vars הוא local-only).
  3. .assetsignore — Static Assets לא מחריג node_modules/.git/.DS_Store אוטומטית. בלי הקובץ הזה אתה עלול להעלות עשרות אלפי קבצים ולפגוע במגבלת ה-20,000.
  4. smoke test ב-wrangler dev --remote — Miniflare הלוקלי מתפצל מ-production ב-DO alarms, KV TTL propagation ו-R2 multipart. בדוק מול הענן לפני deploy.
  5. named tunnel ל-SSE — אם האפליקציה משתמשת ב-streaming/SSE, השתמש ב-named tunnel ולא ב-Quick Tunnel (trycloudflare) — Quick Tunnel לא תומך SSE ומוגבל ל-200 concurrent.
  6. beta = feature-flag — עטוף Email Service / Analytics Engine / Secrets Store; אין ערובת יציבות.
  7. binding names = Env interface — ודא שכל שם ב-wrangler.toml תואם בדיוק ל-type Env ב-Worker. אי-התאמה = undefined בזמן ריצה.
  8. compatibility_date + nodejs_compat — תאריך עדכני, ו-flag אם תלות כלשהי צריכה Node built-ins.
עשו עכשיו 4 דקות

צור קובץ .assetsignore בתיקיית ה-assets שלך עם השורות: node_modules, .git, .DS_Store. שמור. זה מונע העלאה של עשרות אלפי קבצים ופגיעה במגבלת 20,000 הקבצים.

מתקדם8 דקותחינםתרגיל-מונחה

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 יישברו, אז שדרגתי בזמן".

עשו עכשיו 5 דקות

הרץ wrangler deploy על האפליקציה המלאה. פתח את ה-URL החי, שלח בקשת /api/ask אחת, וודא שחזרה תשובת RAG. צלם את התוצאה — זו ההוכחה שכל החלקים מ-1-5 חיים יחד.

תרגיל (Capstone): בניית מטריצת ה-Pay-or-Not לאפליקציה שלך 30 דקות
  1. רשום את פרופיל ה-DAU של האפליקציה שלך (writes, reads, AI calls ליום למשתמש) — מה-do-now של סעיף 7.
  2. בנה טבלה: שורה לכל primitive (Workers, KV, D1, R2, Workers AI, Vectorize).
  3. לכל primitive מלא: מגבלה חינמית → המגבלה שנופלת ראשונה → trigger לשדרוג → עלות משוערת ב-1,000 DAU → המלצה.
  4. חשב 1,000 DAU × פעולה × 30 לכל primitive והשווה למגבלה (היומית-שקולה).
  5. סמן איזה primitive שובר ראשון את ה-free tier באפליקציה שלך.
  6. כתוב משפט החלטה אחד: $0 או $5, ולמה — מבוסס מספרים, לא תחושה.

פלט נראה לעין: מסמך מטריצת pay-or-not מלא (טבלה + משפט החלטה) לאפליקציה האמיתית שלך — הפלט המרכזי של הקאפסטון, ניתן לשמירה כ-Markdown/PDF. זה המסמך שתפתח בכל deploy עתידי.

שגרת עבודה

שגרת עבודה — חיווט full-stack והחלטת pay-or-not
מתיפעולה
בתחילת כל פרויקט 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 שלך כתוב במספרים — ולא כחשש מעורפל — הוא הרגע שבו כל הקורס הופך ממושגים להחלטה. זה המסמך היחיד הכי גבוה-מינוף שלקחת מכאן.

בדוק את עצמך — 5 שאלות
  1. למה KV writes שובר את ה-free tier לפני Workers requests, גם כשמספר הבקשות גדול ממספר הכתיבות? (רמז: השווה 1,000 writes/יום ל-100,000 requests/יום — איזה cap זעיר יותר.)
  2. למה ה-$5 הופך daily caps ל-monthly pools ולא רק מגדיל את ה-caps היומיים? (רמז: מודל החיוב — daily ל-free, monthly pool ל-paid; KV 1k/יום → 1M/חודש.)
  3. איך תזהה שקוד RAG שכתב לך מדריך/AI ישן ישבר בשטח, גם אם ה-deploy עובר? (רמז: חפש llama-3.1-8b — נשבר בשקט בזמן ריצה ב-2026-05-30.)
  4. למה [ai] נכתב עם סוגריים בודדים בעוד [[d1_databases]] עם כפולים, ומה קורה אם תטעה? (רמז: טבלה יחידנית מול מערך — שגיאת config שקטה.)
  5. מתי תבנה 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