Розробка Edge Functions для сайту (Deno Deploy)
Deno Deploy — це розподілена runtime-платформа, яка запускає JavaScript та TypeScript на V8 Isolates у 35+ регіонах без холодного старту. На відміну від Lambda або Cloud Functions, код виконується в мілісекундах від користувача, тому що не піднімає контейнер — ізолят уже готовий.
Типові завдання для Edge Functions на сайті: A/B-тестування на рівні CDN, персоналізація за геолокацією, middleware для авторизації, проксирування запитів з трансформацією, генерація динамічних OG-зображень.
Як працює Deno Deploy
Кожна функція — це ES модуль з обробником 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 });
});
Розгортання через CLI:
deno install -A jsr:@deno/deployctl
deployctl deploy --project=my-site entry.ts
Middleware для авторизації
Часто потрібно перевірити JWT або токен сесії до того, як запит дійде до origin. На Edge це робиться без roundtrip до бекенду:
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); // проксирування на origin
}
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);
// Додаємо дані користувача в заголовок для origin
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 });
}
});
Геолокаційна персоналізація
Deno Deploy передає геодані через заголовки. Корисно для редиректів за мовою, регіональних цін, блокування за країною:
const COUNTRY_REDIRECTS: Record<string, string> = {
RU: 'https://ru.example.com',
BY: 'https://ru.example.com',
DE: 'https://de.example.com',
FR: 'https://fr.example.com',
};
Deno.serve((req: Request) => {
const url = new URL(req.url);
if (url.pathname !== '/') {
return fetch(req);
}
const cookies = req.headers.get('Cookie') ?? '';
if (cookies.includes('locale-selected=1')) {
return fetch(req);
}
const country = req.headers.get('x-deno-country');
const target = country ? COUNTRY_REDIRECTS[country] : null;
if (target) {
return new Response(null, {
status: 302,
headers: {
Location: target,
'Set-Cookie': 'locale-selected=1; Path=/; Max-Age=86400; SameSite=Lax',
},
});
}
return fetch(req);
});
Генерація OG-зображень на Edge
Satori — бібліотека для рендеринга JSX в SVG — працює в Deno Deploy. Дозволяє генерувати унікальні preview-картинки для кожної сторінки без prebuild:
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',
},
});
});
Кеширування з Deno KV
Deno KV — вбудоване key-value сховище з глобальною реплікацією. Використовується для кешу, лічильників, rate limiting:
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 Function (редирект, геолокація, базовий middleware) — 1–2 дні включаючи тестування та розгортання. Middleware з JWT-верифікацією та проксируванням — 2–3 дні. Генерація OG-зображень з кешуванням через Deno KV — 3–5 днів. Повноцінний Edge Layer з rate limiting, A/B-тестуванням та аналітикою — 1–2 тижні.







