Skip to main content

Edge runtime проти Node.js runtime у Next.js

Edge runtime запускає серверний код Next.js у V8 ізолятах (isolates), розподілених між 200+ CDN-локаціями по всьому світу. Node.js runtime (за замовчуванням) дає повний доступ до екосистеми Node.js: fs, нативні модулі, постійні з'єднання та будь-який npm-пакет.

Теорія

TL;DR

  • Node.js - це повноцінна кухня з усіма приладами: fs, crypto, net, нативні модулі, повна пам'ять
  • Edge - це вендинговий автомат у кожному аеропорту: обмежені інструменти, але миттєво звідусіль
  • Головна різниця: Node.js використовує V8 + event loop libuv; Edge - V8 ізоляти тільки з Web API
  • Холодний старт: Edge ~40ms проти ~400ms у Node.js (дані Vercel)
  • Правило вибору: Edge для автентифікації та редиректів; Node.js для бази даних і важких обчислень

Швидкий приклад

tsx
// Node.js runtime (за замовчуванням) - повний доступ до API import fs from 'fs'; export async function GET() { const data = fs.readFileSync('./products.json', 'utf-8'); return Response.json(JSON.parse(data)); // читає з локального диска } // Edge runtime - тільки Web API export const runtime = 'edge'; export async function GET() { const res = await fetch('https://api.example.com/products'); return Response.json(await res.json()); // ~40ms з найближчої точки присутності }

Node.js читає файл з диска. Edge не може звертатись до файлової системи взагалі, тому ти звертаєшся до зовнішнього API. Компроміс простий: повні можливості проти глобальної затримки.

Ключова різниця

Node.js runtime - це стандартний серверний процес: V8 плюс I/O-цикл libuv. Він відкриває сокети, читає файли, запускає нативні доповнення. Edge runtime компілює код у V8 ізоляти, прибирає все що потребує системних викликів ОС, і розгортає копії в точках присутності (PoPs) CDN по всьому світу. Ти втрачаєш доступ до файлової системи та вбудовані модулі Node.js. Отримуєш глобальне розміщення та холодний старт ~40ms замість ~400ms.

Коли що використовувати

  • Перевірка JWT, автентифікація, редиректи -> Edge (без звернення до бази, виконується близько до користувача)
  • Гео-маршрутизація, A/B тестування -> Edge (request.geo доступний лише в Edge middleware)
  • Запити до бази через Prisma або Drizzle -> Node.js (нативні драйвери не працюють в ізолятах)
  • Обробка файлів, маніпуляція зображеннями -> Node.js (потрібен fs і нативні прив'язки)
  • Довгі обчислення (>1с) -> Node.js (Edge обмежений 30с CPU та ~1MB пам'яті)
  • Стейтлес-шляхи з важливим холодним стартом -> Edge; пули з'єднань -> Node.js

Таблиця порівняння

МожливістьNode.js RuntimeEdge Runtime
APIПовний Node.js (fs, crypto, net)Web-стандарти (Fetch, URLSearchParams, crypto.subtle)
Де виконуєтьсяОдин регіон або self-hosted200+ PoPs глобально (V8 ізоляти)
Холодний старт~400ms~40ms
Ліміт CPU300с30с
Пам'ятьПовна купа (heap)~1MB
Файлова системаТак (модуль fs)Ні
Нативні модуліТакНі
npm-пакетиВсіТільки сумісні з Web API
СтрімінгТакТак (до 4MB у Next.js 15)
СтанПостійний (з'єднання перевикористовуються)Тільки stateless
Для чогоБаза даних, файли, важкі обчисленняАвтентифікація, редиректи, персоналізація

Як це працює зсередини

Next.js компілює Edge-маршрути в бандли V8 ізолятів через esbuild. Жодного I/O-циклу libuv тут немає. Будь-який імпорт вбудованого Node.js модуля призводить до помилки на етапі збірки (next build), а не під час виконання. При деплої на Vercel Edge-бандли запускаються в середовищі, сумісному з Cloudflare Workers, яке доповнює деякі відсутні Web API, але повністю відхиляє fs, path, net та подібні модулі.

Middleware у Next.js завжди виконується на Edge. Це не налаштовується.

Типові помилки

Імпорт fs в Edge-маршруті:

tsx
// Помилка збірки: "Module not found: Can't resolve 'fs'" export const runtime = 'edge'; import fs from 'fs'; // падає на next build // Виправлення: fetch з CDN або зовнішнього API const res = await fetch('https://my-cdn.com/data.json'); const data = await res.text();

Великі змінні середовища понад 4KB:

Edge серіалізує змінні середовища в заголовки запиту. Секрети понад 4KB обрізаються без жодного попередження в розробці. Баг проявляється лише в продакшені.

tsx
// Обрізається в продакшені, помилка не кидається const bigKey = process.env.HUGE_PRIVATE_KEY; // Виправлення: Vercel Edge Config або зовнішній API секретів const config = await fetch('https://edge-config.vercel.com/...');

Перенесення Express-обробників напряму:

tsx
// Працює в Node.js, повертає 400 в Edge export default (req, res) => res.json({ body: req.body }); // Виправлення: Edge використовує Web Request API export async function POST(request: Request) { const body = await request.json(); return Response.json({ body }); }

Несумісність крипто API між рантаймами: Edge використовує crypto.subtle (Web Crypto API). Node.js традиційно використовує crypto.createCipheriv. Якщо написати Edge-код з crypto.subtle і перенести в Node.js обробник, він спрацює на Node.js 18+ (там crypto.subtle є глобальним). У зворотньому напрямку - ніколи: createCipheriv в Edge-маршруті впаде на збірці.

Перевищення ліміту пам'яті 1MB у циклах: V8 ізолят завершує запит при нестачі пам'яті без зрозумілого повідомлення. Якщо обробляєш великі дані, використовуй стрімінг: for await (const chunk of stream) замість буферизації всього в пам'яті.

Де це використовується в реальних проєктах

  • Vercel Speed Insights: Edge для гео-вибірки між регіонами
  • Stripe і Supabase: Edge middleware для перевірки JWT (без звернення до бази)
  • Shopify Hydrogen: Edge-маршрути для персоналізації кошика
  • Linear.app: Edge API для розсилки вебхуків
  • T3 Stack (create-t3-app): middleware автентифікації на Edge за замовчуванням

Follow-up питання

Q: Які Node.js API відсутні в Edge runtime?
A: fs, path, net, zlib, child_process та більшість вбудованих модулів Node.js. Замінюй їх на Fetch, URLSearchParams та crypto.subtle.

Q: Як вибір Edge впливає на збірку проєкту?
A: esbuild прибирає Node.js-поліфіли і зупиняє збірку, якщо будь-який імпортований модуль залежить від Node built-in. Проблеми видно при next build, а не в продакшені.

Q: Які реальні числа холодного старту?
A: Бенчмарки Vercel показують Edge ~40ms і Node.js ~400ms. Різниця в ціні запуску ізоляту: майже нульова, проти ініціалізації Node-процесу.

Q: Які обмеження Edge з'явились у Next.js 15?
A: Стрімінг в Edge-маршрутах обмежений 4MB. Функція cache() з React також не працює в Edge-обробниках.

Q (senior): Ти хочеш обмежувати запити за IP через Redis в Edge middleware. У чому проблема і як її вирішити?
A: Edge stateless. Він не може тримати постійне TCP-з'єднання до Redis. Рішення - Upstash Redis, який обертає Redis-команди в HTTP-виклики, доступні через Fetch. На кожен запит платиш один HTTP-roundtrip, тому потрібно враховувати цю затримку і кешувати стан ліміту через TTL-заголовки.

Приклади

Edge проти Node.js для одного маршруту даних

tsx
// app/api/node/route.ts - Node.js runtime (за замовчуванням) import fs from 'fs'; import path from 'path'; export async function GET() { const filePath = path.join(process.cwd(), 'data', 'products.json'); const raw = fs.readFileSync(filePath, 'utf-8'); return Response.json(JSON.parse(raw)); // Читає з диска, один регіон, ~400ms холодний старт }
tsx
// app/api/edge/route.ts - Edge runtime export const runtime = 'edge'; export async function GET() { const res = await fetch('https://api.example.com/products'); return Response.json(await res.json()); // Без файлової системи, 200+ PoPs, ~40ms холодний старт }

Один і той самий ендпоінт, різні обмеження. Node.js - коли дані лежать на сервері. Edge - коли джерело даних зовнішнє, а користувачі розподілені по всьому світу.

Production middleware: гео-редирект і блокування ботів

tsx
// middleware.ts - завжди запускається на Edge, не налаштовується import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const country = request.geo?.country; const ua = request.headers.get('user-agent') ?? ''; // Блокуємо ботів зі США ще до origin if (country === 'US' && ua.toLowerCase().includes('bot')) { return NextResponse.redirect(new URL('/blocked', request.url)); } if (country === 'UA') { return NextResponse.rewrite(new URL('/ua', request.url)); } return NextResponse.next(); } export const config = { matcher: ['/((?!_next|favicon.ico).*)'], }; // Патерн з vercel/commerce - блокує ботів за <20ms глобально

request.geo доступний тільки в Edge. В Node.js API-маршрутах цього поля немає. Middleware виконується до рендеру сторінки, на рівні CDN, не торкаючись origin-сервера взагалі.

Несумісність крипто API при міграції між рантаймами

tsx
// app/api/encrypt/route.ts - написано для Edge export const runtime = 'edge'; export async function POST(request: Request) { // Web Crypto API - працює в Edge і в Node.js 18+ const key = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ); const iv = crypto.getRandomValues(new Uint8Array(12)); const payload = await request.arrayBuffer(); const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, key, payload ); return new Response(encrypted); } // Якщо хтось перепише це на Node.js зі старим API: // import { createCipheriv, randomBytes } from 'crypto'; // Той варіант ЗЛАМАЄ Edge на збірці. // Пиши через crypto.subtle - запуститься скрізь.

Пиши шифрування через crypto.subtle. Працює в Edge і в Node.js 18+. Я бачив цю проблему в командах, які копіювали обробники Stripe-вебхуків зі старого Node.js-сервісу в Edge middleware: збірка падала, бо оригінальний код використовував createCipheriv, якого Edge ніколи не підтримував.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.