Edge Runtime
Edge Runtime — это изолированная среда выполнения JavaScript на базе V8 для SSR и Edge Functions. Поддерживает Web APIs и платформенные APIs ONREZA.
Web APIs
Заголовок раздела «Web APIs»Edge Runtime полностью поддерживает стандартные Web APIs:
Fetch & Network
Заголовок раздела «Fetch & Network»| API | Описание |
|---|---|
fetch() | HTTP запросы с SSRF защитой |
Request / Response | HTTP объекты |
Headers | HTTP заголовки |
FormData | Form data для POST запросов |
Streams
Заголовок раздела «Streams»| API | Описание |
|---|---|
ReadableStream / WritableStream | Потоки чтения/записи |
TransformStream | Трансформация потоков |
CompressionStream / DecompressionStream | Сжатие (gzip, deflate) |
Криптография
Заголовок раздела «Криптография»| API | Описание |
|---|---|
crypto.subtle | Web Crypto API (encrypt, decrypt, sign, verify, digest) |
crypto.getRandomValues() | Криптографически безопасный random |
crypto.randomUUID() | UUID v4 |
| API | Описание |
|---|---|
TextEncoder / TextDecoder | Кодирование строк |
Blob / File | Бинарные данные |
structuredClone() | Глубокое копирование |
URL / URLSearchParams | URL парсинг |
URLPattern | Паттерны для URL роутинга |
Таймеры
Заголовок раздела «Таймеры»| API | Описание |
|---|---|
setTimeout / clearTimeout | Таймауты |
setInterval / clearInterval | Интервалы |
queueMicrotask | Микротаски |
События
Заголовок раздела «События»| API | Описание |
|---|---|
Event / EventTarget | Событийная модель |
CustomEvent | Пользовательские события |
AbortController / AbortSignal | Отмена операций |
EventSource | Server-Sent Events клиент |
Node.js Compatibility
Заголовок раздела «Node.js Compatibility»| API | Описание |
|---|---|
Buffer | Node.js Buffer полифил |
process.env | Доступ к Environment Variables |
process.version | Node.js version stub |
Платформенный API
Заголовок раздела «Платформенный API»Edge Runtime предоставляет доступ к платформенным API через SDK @onreza/runtime:
import { kv } from '@onreza/runtime/kv';import { db } from '@onreza/runtime/db';import { env } from '@onreza/runtime/env';import { getContext, getGeo } from '@onreza/runtime/context';import { imageUrl, generateSrcset } from '@onreza/runtime/image';Environment Variables
Заголовок раздела «Environment Variables»import { env, getEnv } from '@onreza/runtime/env';
const dbUrl = env.get("DATABASE_URL"); // string | undefinedconst timeout = getEnv("TIMEOUT", "5000"); // string (с дефолтом)
if (env.has("API_KEY")) { // ...}KV Store
Заголовок раздела «KV Store»import { kv } from '@onreza/runtime/kv';
await kv.set("key", "value", { ttl: 3600 });const value = await kv.get("key");await kv.delete("key");const { keys, cursor } = await kv.list({ prefix: "user:" });D1 Database
Заголовок раздела «D1 Database»import { db } from '@onreza/runtime/db';
// Prepared statements с bindconst { results } = await db.prepare("SELECT * FROM users WHERE id = ?") .bind(userId) .all();
// Batch (атомарная транзакция)await db.batch([ db.prepare("INSERT INTO users (name) VALUES (?)").bind("Alice"), db.prepare("INSERT INTO users (name) VALUES (?)").bind("Bob"),]);
// DDLawait db.exec("CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY, title TEXT NOT NULL)");Ограничения D1
Заголовок раздела «Ограничения D1»| Параметр | Лимит |
|---|---|
| Строк в ответе | 5,000 |
| Максимальная длина SQL | 1 MB |
Request Context
Заголовок раздела «Request Context»import { getContext, getGeo, getClientIp, getRequestId } from '@onreza/runtime/context';
const ctx = getContext();console.log(ctx.geo?.country); // "RU"console.log(ctx.clientIp); // "203.0.113.42"console.log(ctx.requestId); // UUID v7Image Optimization
Заголовок раздела «Image Optimization»import { imageUrl, generateSrcset } from '@onreza/runtime/image';
const url = imageUrl('/images/photo.jpg', { width: 800, quality: 85, format: 'webp' });const srcset = generateSrcset('/images/photo.jpg', [640, 1080, 1920]);→ Подробнее об Image Optimization
HTTP Headers
Заголовок раздела «HTTP Headers»Входящие заголовки
Заголовок раздела «Входящие заголовки»Edge Runtime автоматически проксирует входящие HTTP заголовки:
export default async function handler(request: Request) { // Доступ к заголовкам const userAgent = request.headers.get("user-agent"); const contentType = request.headers.get("content-type");
return new Response("OK");}Специальные заголовки ONREZA
Заголовок раздела «Специальные заголовки ONREZA»Следующие заголовки добавляются платформой к каждому запросу:
| Header | Описание | Пример |
|---|---|---|
x-onreza-request-id | Уникальный ID запроса | 018f3... |
x-onreza-deployment-id | ID текущего деплоя | dep_abc123 |
x-onreza-project-id | ID проекта | proj_xyz789 |
x-onreza-environment | Окружение | production |
x-forwarded-for | Оригинальный IP клиента | 203.0.113.42 |
x-forwarded-proto | Оригинальный протокол | https |
Использование Request ID
Заголовок раздела «Использование Request ID»Заголовок x-onreza-request-id содержит уникальный идентификатор, удобный для сортировки и трейсинга:
export default async function handler(request: Request) { // Получить request ID для логирования/трейсинга const requestId = request.headers.get("x-onreza-request-id"); // Пример: "018f3a4c-7b2d-7e8a-9f1b-2c3d4e5f6a7b"
// Уникальный идентификатор содержит timestamp в первых 48 битах // Можно извлечь время создания запроса const timestamp = parseInt(requestId!.slice(0, 8), 16); const requestTime = new Date(timestamp * 1000);
// Логирование с request ID console.log(`[${requestId}] Processing request...`);
// Передача в downstream сервисы для distributed tracing const response = await fetch("https://api.example.com/data", { headers: { "X-Request-ID": requestId, }, });
return new Response(JSON.stringify({ requestId, data: await response.json() }));}Исходящие заголовки
Заголовок раздела «Исходящие заголовки»Вы можете устанавливать любые исходящие заголовки в ответе:
export default async function handler(request: Request) { return new Response("Hello", { headers: { "Content-Type": "text/plain", "Cache-Control": "public, max-age=3600", "X-Custom-Header": "value", }, });}Middleware
Заголовок раздела «Middleware»Bidirectional Middleware с next()
Заголовок раздела «Bidirectional Middleware с next()»Edge Runtime поддерживает bidirectional middleware — обработку запроса до и после основного handler:
export default async function middleware(request: Request, next: () => Promise<Response>): Promise<Response> { // --- Pre-processing --- const startTime = Date.now(); const requestId = request.headers.get("x-onreza-request-id");
console.log(`[${requestId}] Request started: ${request.url}`);
// Проверка аутентификации const authHeader = request.headers.get("authorization"); if (!authHeader) { return new Response("Unauthorized", { status: 401 }); }
// Вызов следующего middleware или handler const response = await next();
// --- Post-processing --- const duration = Date.now() - startTime;
// Добавление заголовков response.headers.set("x-response-time", `${duration}ms`); response.headers.set("x-request-id", requestId!);
console.log(`[${requestId}] Request completed in ${duration}ms`);
return response;}Ограничения middleware
Заголовок раздела «Ограничения middleware»| Ограничение | Описание |
|---|---|
| Single next() | next() может быть вызван только один раз |
| Buffer size | Максимальный размер buffered response: 5 MB |
| Timeout | Общий таймаут на весь pipeline: 30 секунд |
| Memory | Общий лимит памяти: 128 MB |
// Неправильно: multiple next() callsexport default async function badMiddleware(request: Request, next: () => Promise<Response>) { const response1 = await next(); const response2 = await next(); // Error: next() already called return response1;}
// Неправильно: обработка тела после next()export default async function badMiddleware(request: Request, next: () => Promise<Response>) { const response = await next(); const body = await response.text(); // Buffering > 5MB вызовет ошибку return new Response(body.toUpperCase());}
// Правильно: единственный вызов next()export default async function goodMiddleware(request: Request, next: () => Promise<Response>) { // Модификация request перед передачей const modifiedRequest = new Request(request, { headers: { ...request.headers, "x-processed-at": Date.now().toString(), }, });
const response = await next();
// Модификация response после получения return new Response(response.body, { status: response.status, headers: { ...response.headers, "x-middleware": "processed", }, });}Безопасность
Заголовок раздела «Безопасность»Rate Limiting
Заголовок раздела «Rate Limiting»ONREZA автоматически применяет tiered per-IP rate limiting для защиты от DDoS и злоупотреблений:
- Лимиты применяются на уровне edge-ноды по IP-адресу клиента
- При превышении лимита возвращается HTTP 429 с заголовком
Retry-After - Повторные нарушения приводят к временной блокировке IP
SSRF Protection
Заголовок раздела «SSRF Protection»fetch() автоматически блокирует запросы к внутренним сетям:
// Заблокировано — приватная сетьawait fetch("http://10.0.0.1/internal");
// Заблокировано — localhostawait fetch("http://127.0.0.1:8080");
// Заблокировано — metadata облакаawait fetch("http://169.254.169.254/latest/meta-data/");
// Разрешено — публичный интернетawait fetch("https://api.example.com/data");Что запрещено
Заголовок раздела «Что запрещено»| Операция | Статус |
|---|---|
| Файловая система | Запрещена (нет fs модуля) |
| Child processes | Запрещены (spawn, exec) |
| FFI / Native modules | Запрещены |
| Сетевая информация | Запрещена (os.networkInterfaces) |
Handler формат
Заголовок раздела «Handler формат»Basic handler
Заголовок раздела «Basic handler»// server.ts или handler файлexport default async function handler(request: Request): Promise<Response> { const url = new URL(request.url);
if (url.pathname === "/api/hello") { return Response.json({ message: "Hello from ONREZA!" }); }
return new Response("Not Found", { status: 404 });}С доступом к env
Заголовок раздела «С доступом к env»import { env } from '@onreza/runtime/env';
export default async function handler(request: Request): Promise<Response> { const apiKey = env.get("API_KEY");
if (!apiKey) { return new Response("API_KEY not configured", { status: 500 }); }
const data = await fetch("https://api.service.com/data", { headers: { "Authorization": `Bearer ${apiKey}` }, });
return new Response(data.body);}Производительность
Заголовок раздела «Производительность»Cold Start
Заголовок раздела «Cold Start»- Первый запрос: ~5ms
- Повторные запросы: ~0.5ms (кэширование кода)
Ограничения
Заголовок раздела «Ограничения»| Параметр | Лимит |
|---|---|
| Таймаут выполнения | 30 секунд |
| Максимальная память | 128 MB |
| Максимальный размер ответа | 10 MB |
| Максимальный размер тела запроса | 10 MB |
| Максимальный размер сообщения (WebSocket/RPC) | 10 MB |
Best Practices
Заголовок раздела «Best Practices»// Используйте streaming для больших ответовexport default async function handler(request: Request) { const stream = await fetchLargeDataStream(); return new Response(stream);}
// Отменяйте неактуальные запросыexport default async function handler(request: Request) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000);
try { const response = await fetch("https://api.example.com", { signal: controller.signal, }); clearTimeout(timeout); return response; } catch (e) { if (e.name === "AbortError") { return new Response("Timeout", { status: 504 }); } throw e; }}
// Кэшируйте соединения (keep-alive автоматический)Масштабирование
Заголовок раздела «Масштабирование»ONREZA использует serverless-модель: каждый запрос выполняется в отдельном V8 isolate. Масштабирование полностью автоматическое — настройка не требуется.
Как это работает
Заголовок раздела «Как это работает»- Запрос приходит на ближайшую edge-ноду
- Isolate создаётся за ~5ms (cold start) или переиспользуется за ~0.5ms
- Код выполняется в изолированной песочнице с ограничениями по памяти и времени
- Ответ отправляется клиенту, isolate освобождается
Платформа автоматически обрабатывает любую нагрузку: от единичных запросов до тысяч параллельных — без ручной настройки instances, auto-scaling policies или capacity planning.
Когда ONREZA подходит
Заголовок раздела «Когда ONREZA подходит»| Сценарий | Подходит? |
|---|---|
| SSR / API endpoints | Да |
| Edge Functions (auth, geo-routing) | Да |
| REST / GraphQL API | Да |
| Периодические задачи < 30s | Да |
| Long-running tasks (> 30s) | Нет — используйте внешние сервисы |
| WebSocket соединения | Нет |
| Тяжёлые вычисления (ML, видео) | Нет — превысит лимит памяти/времени |
Оптимизация производительности
Заголовок раздела «Оптимизация производительности»- Минимизируйте cold start: держите размер бандла минимальным, избегайте тяжёлых зависимостей
- Используйте KV Store: для кэширования результатов между запросами
- Streaming: для больших ответов используйте
ReadableStreamвместо буферизации - Таймауты: устанавливайте
AbortControllerдля внешних fetch-запросов
Совместимость с фреймворками
Заголовок раздела «Совместимость с фреймворками»Next.js
Заголовок раздела «Next.js»Next.js автоматически использует Edge Runtime при деплое на ONREZA. Настройка не требуется.
export const runtime = 'edge'; // Явное указание
export async function GET(request: Request) { const requestId = request.headers.get('x-onreza-request-id'); return Response.json({ requestId });}export const prerender = false;
export async function GET({ request }: { request: Request }) { const requestId = request.headers.get('x-onreza-request-id'); return new Response(JSON.stringify({ requestId }));}SvelteKit
Заголовок раздела «SvelteKit»export async function GET({ request }) { const requestId = request.headers.get('x-onreza-request-id'); return json({ requestId });}См. также
Заголовок раздела «См. также»- Runtime SDK — типизированный SDK для платформенных API (Edge Functions и Compute Apps)
- Compute Apps — Next.js, Astro SSR и другие Bun-приложения
- Image Optimization — оптимизация изображений
- D1 Database — SQL база данных
- KV Store — key-value хранилище