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

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

Дочитали статтю?