הטמעת Edge Middleware ופונקציות Serverless על Deno Deploy
כאשר האתר שלך משרת משתמשים ברחבי העולם, זמן האחזור הופך קריטי. כל נסיעה חזרה לשרת המקור מוסיפה מאות אלפיות שנייה, ומחמירה את LCP ו-TTFB. פונקציות Edge על Deno Deploy פותרות זאת על ידי ביצוע קוד בקצה הרשת — ביותר מ-35 אזורים, על V8 Isolates ללא התחלה קרה, ומשפרות ישירות את Core Web Vitals. אנו בונים פונקציות אלה במפתח מלא: middleware, יצירת תמונות OG, caching, אימות הרשאות והתאמה אישית בקצה הרשת.
הניסיון שלנו: 5+ שנים בפיתוח edge, 15+ פרויקטים במפתח מלא. אנו מבטיחים יציבות ועמידה ב-Core Web Vitals. בניגוד ל-Lambda או Cloud Functions, הקוד רץ בתוך אלפיות שנייה מהמשתמש מכיוון שלא מופעל container — ה-isolate כבר חם. כפי שמתועד בתיעוד Deno Deploy, הפלטפורמה משתמשת ב-V8 Isolates ומבטלת התחלות קרות.
משימות טיפוסיות שאנו פותרים
- בדיקות A/B ברמת ה-CDN
- התאמה אישית לפי מיקום גיאוגרפי
- Middleware לאימות הרשאות
- העברת בקשות עם טרנספורמציה
- יצירה דינמית של תמונות OG
השוואה: פונקציות Edge לעומת פונקציות Cloud
| פרמטר | פונקציות Edge (Deno Deploy) | פונקציות Cloud (AWS Lambda) |
|---|---|---|
| התחלה קרה | אין (isolate מחומם מראש) | ~200 אלפיות שנייה–1 שנייה (container) |
| גיאוגרפיה | 35+ אזורים | בחירת אזור בעת deployment |
| שפות | TypeScript באופן טבעי | רבות, אך עם שכבת קונפיגורציה |
| אחסון מובנה | Deno KV (גלובלי) | נדרש חיצוני |
Deno Deploy מהיר עד פי 10 מ-AWS Lambda בשל היעדר התחלות קרות וביצוע בקצה הרשת.
איך Deno Deploy עובד
כל פונקציה היא מודול ES עם handler מסוג Deno.serve. הפלטפורמה אינה תומכת במערכת קבצים (למעט bundle), אין setTimeout ארוך, ואין ביצוע ברקע לאחר תגובה (למעט waitUntil).
// entry.ts
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
if (url.pathname === '/api/geo') {
const country = req.headers.get('x-deno-country') ?? 'unknown';
const region = req.headers.get('x-deno-region') ?? 'unknown';
return Response.json({ country, region });
}
return new Response('Not Found', { status: 404 });
});Deploy דרך CLI עם פקודה אחת: // entry.ts Deno.serve(async (req: Request) => { const url = new URL(req.url); if (url.pathname === '/api/geo') { const country = req.headers.get('x-deno-country') ?? 'unknown'; const region = req.headers.get('x-deno-region') ?? 'unknown'; return Response.json({ country, region }); } return new Response('Not Found', { status: 404 }); }); .
למה פונקציות Edge עדיפות על Backend מסורתי
היתרון המרכזי הוא היעדר התחלה קרה וביצוע בקצה הרשת. בקשות מטופלות באזור המשתמש, מה שמפחית את זמן האחזור מ-200–500 אלפיות שנייה ל-10–30 אלפיות שנייה — ומשפר את LCP בעד 40%. זה משפר ישירות את Core Web Vitals. בנוסף, פונקציות edge מורידות עומס משרת המקור על ידי טיפול ב-caching ואימות הרשאות ללא נסיעות נוספות.
מקרה בוחן: Middleware לאימות JWT
לעיתים קרובות יש צורך לאמת JWT לפני הגעה לשרת המקור. בקצה הרשת, זה קורה ללא פנייה לשרת הראשי:
import { create, verify, getNumericDate } from 'https://deno.land/x/[email protected]/mod.ts';
const JWT_SECRET = Deno.env.get('JWT_SECRET')!;
async function getKey(secret: string): Promise<CryptoKey> {
const enc = new TextEncoder();
return await crypto.subtle.importKey(
'raw',
enc.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign', 'verify']
);
}
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
if (url.pathname.startsWith('/public') || url.pathname === '/') {
return await fetch(req);
}
const authHeader = req.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.slice(7);
try {
const key = await getKey(JWT_SECRET);
const payload = await verify(token, key);
const modifiedReq = new Request(req, {
headers: {
...Object.fromEntries(req.headers),
'x-user-id': String(payload.sub),
'x-user-role': String(payload.role ?? 'user'),
},
});
return await fetch(modifiedReq);
} catch {
return new Response('Invalid token', { status: 401 });
}
});
טעות נפוצה היא ייבוא מפתח שגוי — השתמש ב-deployctl deploy --project=my-site entry.ts עם הפורמט הנכון.
שילוב פונקציות Edge עם האתר הקיים שלך
Deno Deploy עובד כשכבת Edge לפני שרת המקור. הזרימה פשוטה: DNS → Deno Deploy → שרת מקור.
- הגדר DNS: הפנה תעבורה ל-Deno Deploy (לדוגמה, דרך CNAME ל-
import { create, verify, getNumericDate } from 'https://deno.land/x/[email protected]/mod.ts'; const JWT_SECRET = Deno.env.get('JWT_SECRET')!; async function getKey(secret: string): Promise<CryptoKey> { const enc = new TextEncoder(); return await crypto.subtle.importKey( 'raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'] ); } Deno.serve(async (req: Request) => { const url = new URL(req.url); if (url.pathname.startsWith('/public') || url.pathname === '/') { return await fetch(req); } const authHeader = req.headers.get('Authorization'); if (!authHeader?.startsWith('Bearer ')) { return new Response('Unauthorized', { status: 401 }); } const token = authHeader.slice(7); try { const key = await getKey(JWT_SECRET); const payload = await verify(token, key); const modifiedReq = new Request(req, { headers: { ...Object.fromEntries(req.headers), 'x-user-id': String(payload.sub), 'x-user-role': String(payload.role ?? 'user'), }, }); return await fetch(modifiedReq); } catch { return new Response('Invalid token', { status: 401 }); } });או לדומיין משלך). - כתוב פונקציות handler שמחליטות אם להעביר ל-backend, לשנות את הבקשה או להגיב ישירות.
- בצע deployment דרך CLI או GitHub Actions. אין צורך בשינויים ב-backend הנוכחי שלך.
דוגמה לקונפיגורציית deno.json
{ "tasks": { "dev": "deno run --allow-net --allow-env --watch entry.ts", "deploy": "deployctl deploy --project=my-site --prod entry.ts" }, "imports": { "djwt": "https://deno.land/x/[email protected]/mod.ts" }, "deploy": { "project": "my-site", "entrypoint": "entry.ts", "include": ["entry.ts", "lib/"] } }
</details>
### Генерация OG-изображений на Edge
Satori — библиотека для рендеринга JSX в SVG — работает в Deno Deploy. Позволяет генерировать уникальные preview-картинки для каждой страницы без prebuild:
```typescript
import satori from 'npm:[email protected]';
import { Resvg } from 'npm:@resvg/[email protected]';
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
if (!url.pathname.startsWith('/og')) return new Response('Not Found', { status: 404 });
const title = url.searchParams.get('title') ?? 'My Site';
const description = url.searchParams.get('desc') ?? '';
const fontResponse = await fetch('https://your-cdn.com/fonts/Inter-Bold.ttf');
const fontBuffer = await fontResponse.arrayBuffer();
const svg = await satori(
{
type: 'div',
props: {
style: {
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
background: '#0f172a',
padding: '60px',
fontFamily: 'Inter',
},
children: [
{
type: 'h1',
props: {
style: {
color: '#f8fafc',
fontSize: 56,
margin: 0,
lineHeight: 1.2
},
children: title,
},
},
{
type: 'p',
props: {
style: {
color: '#94a3b8',
fontSize: 28,
marginTop: 24
},
children: description,
},
},
],
},
},
{
width: 1200,
height: 630,
fonts: [{ name: 'Inter', data: fontBuffer, weight: 700, style: 'normal' }],
}
);
const resvg = new Resvg(svg);
const png = resvg.render().asPng();
return new Response(png, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=86400, stale-while-revalidate=604800',
},
});
});
``` מקרה בוחן: יצירת תמונות OG בקצה הרשת
Satori — ספרייה לרינדור JSX ל-SVG — עובדת על Deno Deploy. היא יוצרת תמונות תצוגה מקדימה ייחודיות לכל עמוד ללא בנייה מוקדמת:
const kv = await Deno.openKv();
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
const cacheKey = ['cache', url.pathname + url.search];
const cached = await kv.get<string>(cacheKey);
if (cached.value) {
return new Response(cached.value, {
headers: {
'Content-Type': 'application/json',
'X-Cache': 'HIT',
},
});
}
const response = await fetch(`https://api.example.com${url.pathname}`);
const data = await response.text();
await kv.set(cacheKey, data, { expireIn: 5 * 60 * 1000 });
return new Response(data, {
headers: {
'Content-Type': 'application/json',
'X-Cache': 'MISS',
},
});
}); מקרה בוחן: Caching עם Deno KV
Deno KV הוא חנות מפתח-ערך מובנית עם שכפול גלובלי. שימושי ל-caching, מונים, הגבלת קצב:
const kv = await Deno.openKv();
Deno.serve(async (req: Request) => {
const url = new URL(req.url);
const cacheKey = ['cache', url.pathname + url.search];
const cached = await kv.get<string>(cacheKey);
if (cached.value) {
return new Response(cached.value, {
headers: {
'Content-Type': 'application/json',
'X-Cache': 'HIT',
},
});
}
const response = await fetch(`https://api.example.com${url.pathname}`);
const data = await response.text();
await kv.set(cacheKey, data, { expireIn: 5 * 60 * 1000 });
return new Response(data, {
headers: {
'Content-Type': 'application/json',
'X-Cache': 'MISS',
},
});
}); מה כלול בעבודה
- ניתוח תרחישי edge ובחירת ארכיטקטורה
- פיתוח פונקציות עם בדיקות ו-CI/CD
- תיעוד הפונקציות שהופעלו ופרטי אינטגרציה
- גישה לניטור ולוגים
- תמיכה לאחר deployment למשך 30 יום
- הדרכה לצוות שלך (עד שעתיים)
תהליך הפיתוח שלנו
| שלב | תוצאה | זמן משוער |
|---|---|---|
| ניתוח | מפרט תרחישי edge, בחירת ארכיטקטורה | יום אחד |
| עיצוב | אבות טיפוס של פונקציות, תוכנית אינטגרציה עם שרת המקור | 1–2 ימים |
| יישום | קוד, בדיקות, CI/CD | 2–5 ימים |
| בדיקות | בדיקות עומס, אימות בקצה הרשת | יום אחד |
| Deployment ותיעוד | גישה, ניטור, תיאור | יום אחד |
עם ניסיון של למעלה מ-5 שנים ו-15+ פרויקטים שהושלמו, יש לנו רקורד מוכח. השאר פנייה — נבחן את הפרויקט שלך תוך יום אחד. צור קשר כדי לדון במשימות ובלוחות זמנים. הזמן שכבת Edge במפתח מלא עם אחריות לביצועים.
לוחות זמנים והשקעה משוערים
- פונקציית Edge פשוטה (הפנייה, מיקום גיאוגרפי, middleware בסיסי): 1–2 ימים, מ-$500.
- Middleware עם אימות JWT והעברת בקשות: 2–3 ימים, מ-$1,500.
- יצירת תמונות OG עם caching דרך Deno KV: 3–5 ימים, מ-$2,500.
- שכבת Edge מלאה עם הגבלת קצב, בדיקות A/B, אנליטיקה: 1–2 שבועות, מ-$5,000.
העלות מחושבת באופן אישי. קבל ייעוץ ממהנדס — נספר לך אילו פונקציות יביאו את הרווח הגדול ביותר בביצועים.







