Edge рантайм. Один из главных функционалов компании Vercel — компании, которая разработала и развивает next.js. Тем не менее, её влияние по edge рантайму вышло далеко за рамки её фреймворков и утилит. Edge рантайм работает и в недавно купленном Vercel Svelte, и в nuxt, и в более чем 30 других фронтенд фреймворках. Эта статья будет посвящена edge рантайму — что это, как это используется в Vercel, какими возможностями дополняет next.js и какие решения сделал я, чтобы эти возможности расширить.
![](https://habrastorage.org/getpro/habr/upload_files/831/c22/cdc/831c22cdc954af0fd1f4bbd8749bdf32.png)
Vercel Edge Network
Простыми словами Edge рантайм представляет из себя сеть доставки контента (CDN/распределённая инфраструктура), то есть множество точек по всему миру. Таким образом пользователь взаимодействует не с единым сервером (который может лежать в офисе компании на другом конце света), а с ближайшей к нему точкой сети.
![Разница обращения напрямую на сервер или через Edge Network Разница обращения напрямую на сервер или через Edge Network](https://habrastorage.org/getpro/habr/upload_files/f2b/265/bf2/f2b265bf2d3aae284548d175cd77409d.png)
При этом, это не копии приложения, а отдельный функционал, который может работать между клиентом и сервером. То есть это своего рода мини-сервера со своими особенностями (о которых будет рассказано позже).
Эта система позволяет пользователям ходить не сразу на ваш далёкий сервер, а на близлежащую точку. В ней принимаются решения по а/б тестам, делаются проверки авторизации, кешируются запросы, возвращаются ошибки и многое другое. Уже после этого, если нужно, запрос пойдёт на сервер, сразу за нужной информацией. Иначе же пользователь в минимальные сроки получает ошибку или, например, редирект.
![Обработка запросов через Edge Network Обработка запросов через Edge Network](https://habrastorage.org/getpro/habr/upload_files/f90/d8e/330/f90d8e330fd1a04b0acd8072a096208d.png)
Конечно, сама эта концепция не является заслугой Vercel. Так умеет и CloudFlare, и Google Cloud CDN и множество других решений. Однако, Vercel, своим влиянием на фреймворки, вывел это на новый уровень, развернув не просто промежуточный роутер на уровне CDN, а сделав мини‑приложения, способные даже рендерить страницы в ближайшей к пользователю точке. И самое главное — сделать это можно просто добавив привычные JS файлы в проект.
Edge-рантайм в next.js
В next.js пожалуй главным функционалом этой среды является файл middleware. Любой сегмент (API или страница) также могут исполняться в edge рантайме. Но перед их описанием, немного о next.js сервере.
Next.js — это фуллстак фреймворк. То есть он содержит и клиентское приложение, и сервер. Запуская next.js (next start
) — запускается именно сервер и уже он отвечает за выдачу страниц, работу API, кеширование, реврайты и т. д.
Работает это всё в следующем порядке:
headers
изnext.config.js
;redirects
изnext.config.js
;Middleware;
beforeFiles
реврайты fromnext.config.js
;Файлы и статичные сегменты (
public/
,_next/static/
,pages/
,app/
, etc.);afterFiles
реврайты изnext.config.js
;Динамические сегменты (
/blog/[slug]
);fallback
реврайты изnext.config.js
.
Когда определяется, что текущей запрос доходит именно до сегмента (а не, например, до редиректа) — запускается его обработка (это либо возврат статически собранного сегмента, либо чтение из кеша, либо его выполнение и возврат результата).
В Vercel, вероятно, весь этот цикл может пройти в edge рантайме. Тем не менее, действительно интересны здесь именно пункты 3, 5 и 7.
Сам middleware в базовой имплементации выглядит так:
import { NextResponse, type NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url));
}
В нём, например, можно:
Сделать запросы (напр. чтобы узнать данные из третьих сервисов);
Выполнить реврайт или редирект (напр. чтобы провести а/б тест или проверить авторизацию);
Вернуть некое тело (напр. чтобы отобразить базовую заглушку при определённых ситуациях);
Прочитать и/или изменить заголовки и куки (напр. сохранить или прочитать информацию о доступах).
Подробнее про области применения можно почитать в документации next.js по middleware.
Тоже самое можно делать и в сегментах (т.е. API и страницы). Чтобы сегмент работал в edge рантайме нужно экспортировать из файла сегмента:
export const runtime = 'edge';
Таким образом сегмент будет исполняться в edge рантайме, а не на самом сервере.
Однако, стоит сделать важную оговорку. Всё описанное выше само по себе не является полноценным edge рантаймом. В Edge Network это распределится только при деплое сервиса в Vercel.
Также, помимо всех этих возможностей, у edge рантайма есть и ряд ограничений. Например, несмотря на то, что при запуске приложения вне Vercel, edge рантайм является частью сервера — взаимодействовать с этим сервером не удастся. И сделано это так потому, что разрабатывалось это именно под Vercel Edge Network.
Концепция edge рантайма в Vercel
Как уже говорилось, edge рантайм можно назвать мини-приложениями. А мини они потому, что работают на node.js V8 (на котором работают например Google Chrome и Electron). Это их ключевая особенность, от которой зависят не только возможности предыдущего раздела, но и запреты.
А именно, в edge рантайме нельзя:
Делать действия с файловой системой;
Взаимодействовать с окружением сервера;
Вызывать
require
. Можно использовать только ES модули. От этого есть дополнительные ограничения на сторонние решения.
Полный список поддерживаемых API и ограничений можно найти на странице документации next.js.
Таким образом Vercel Edge Network может отвечать, например, за:
Роутинг;
Рендер страниц;
Выполнение API роутов;
Кеширование.
Edge рантайм выступает первым этапом обработки сегмента и наиболее эффективен он для ситуаций, когда вся обработка может пройти внутри edge контейнера. Например для редиректов или возврата закешированных данных. Весь процесс обработки в Vercel обычно работает в следующем порядке:
![Порядок обработки запроса в Vercel, источник - vercel.com Порядок обработки запроса в Vercel, источник - vercel.com](https://habrastorage.org/getpro/habr/upload_files/7ad/d19/cc1/7add19cc103acc0cee8110de7f9c1bb3.png)
Vercel после сборки отправляет новый код Edge рантайма (для которого недавно сделали компиляцию в машинный код) на точки сети и те сразу начинают работать с новым кодом.
Vercel настраивает Edge рантайм для всех приложений и всех запросов к ним. То есть после подключения домена Vercel сразу настроит, что этот домен лежит ещё и на этих точках по миру. В следующий раз, когда пользователь зайдёт на страницу — провайдер спросит у сети — где этот домен лежит — ему в ответе придут доступные места, он выберет ближайшую точку и пойдёт на неё.
На этих точках всегда лежит логика кеширования, также, если в проекте есть реврайты, редиректы, middleware или сегменты в Edge рантайме — после сборки он отправит это всё на edge сервера.
Далее Edge рантайм начнёт обрабатывать запросы: проверит реврайты и редиректы → проведёт через middleware → посмотрит есть ли в кеше → если сегмент в edge рантайме — выполнит его у себя, если нет — отправит запрос на оригинальный сервер (но про все эти порядки в Edge рантайме и внутренности Vercel нигде не пишет, но вероятнее всего это проходит именно так).
То есть, суммарно — хорошо использовать Edge рантайм, когда можно сделать всю обработку внутри Edge среды (Запрос будет Client → Edge). Если же нужно ходить к основному серверу (допустим к БД которая подключена внутри проекта, или по какой‑то причине нужно читать файлы) — это будет не выгодно. То есть запрос всё равно будет Client → Edge → Server. А раз всё равно нужно ходить на сервер — лучше сразу всю обработку сделать в нём — в нём есть весь кеш, рядом лежит БД, рядом вся система, да и в целом больше возможностей.
Ожидаемые изменения в edge рантайме
Несмотря на то, что edge рантайм — одна из ключевых возможностей Vercel как хостинга, команда активно его пересматривает. При чём не только само применение, но и необходимость в целом. Так, недавно VP Vercel — Ли Робинсон в своём твите поделился, что Vercel [как компания] перестала использовать edge рантайм во всех своих сервисах и вернулась к nodejs рантайму. Также команда ожидает, что экспериментальный частичный пререндер (PPR) будет настолько эффективен, что генерация в edge рантайме окончательно потеряет ценность.
И именно PPR вместе с продвинутым кешированием вытеснил edge рантайм на второй план. То есть раньше страница рендерилась целиком что на сервере, что в edge рантайме. Edge рантайм выигрывал именно за счёт более близкого расположения. Теперь же, страницы в большей части генерируются предварительно. Затем, при запросе, рендерятся отдельные динамические части и кешируются. Кеш, в свою очередь, в edge рантайме для каждой точки свой, когда на сервере он един для всех пользователей.
Ну и, конечно, у сервера есть доступы к окружению, БД и файловой системе. Поэтому если странице нужны эти данные — nodejs рантайм значительно выигрывает (собрать всё в одном окружении быстрее, чем каждый раз делать запросы на сервер из edge среды).
Скорее всего Vercel введёт новые приоритеты в своих прайсингах, перестроив их вокруг частичного пререндера. Возможно с их изменением твитов со счетами в десятки тысяч долларов станет меньше (но это не точно).
![Инвойс за использование Vercel в $100000, источник - твит фаундера Cara. Инвойс за использование Vercel в $100000, источник - твит фаундера Cara.](https://habrastorage.org/getpro/habr/upload_files/cbc/947/014/cbc947014b56056328e0ee730ded4055.png)
Помимо этого, недавно команда Next.js поделилась твитом о переработке middleware. Весьма вероятно ему, также как и сегментам, добавят выбор среды исполнения. Опять же, учитывая что вне Vercel middleware работает как часть сервера — это очень логичное решение. Также возможно, что вместе с изменениями будет добавлен отдельный middleware для API роутов.
Расширение Edge рантайма
Я автор ряда пакетов под next.js nimpl.tech. Я уже упоминал геттеры с информацией о текущей странице в статье «Next.js App Router. Опыт использования. Путь в будущее или поворот не туда», библиотеке переводов в «Больше библиотек богу библиотек или как я переосмыслил i18n [next.js v14]», пакеты для кэша в «Кеширование next.js. Дар или проклятие». Но в этом семействе есть также и пакеты, построенные именно под edge рантайм — router и middleware‑chain.
@nimpl/router
Как уже говорилось, edge рантайм лучше всего работает если он может обработать весь запрос в замкнутом мини‑приложении. Во всех остальных случаях это ненужный шаг, так как запрос все равно будет ходить на сервер, но более длинным путём.
Одна из таких задач — роутинг. В роутинг входят также реврайты, редиректы, basePath и i18n из next.config.js
.
Их главные проблема в том, что задаются они лишь единожды — в файле конфигурации — для всего приложения, а i18n полон багов. Поэтому в том числе в App Router нет информации об опции i18n и документация рекомендуют использовать для этой задачи middleware. Но такое разделение означает, что редиректы из конфига и i18n роутинг из middleware обрабатываются отдельно. От этого могут происходить двойные редиректы (сперва выполнится редирект из конфига, затем редирект из middleware) и вылезать различные неожиданные артефакты.
Чтобы этого избежать весь этот функционал стоит собрать в одном месте. И, как рекомендует документация для i18n — таким местом должен стать middleware.
import { createMiddleware } from '@nimpl/router';
export const middleware = createMiddleware({
redirects: [
{
source: '/old',
destination: '/',
permanent: false,
},
],
rewrites: [
{
source: '/home',
destination: '/',
locale: false,
},
],
basePath: '/doc',
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
});
Привычные Next.js редиректы, реврайты, basePath и i18n настройки, но на уровне edge рантайма. Документация пакета @nimpl/router.
@nimpl/middleware-chain
Работая с готовыми решениями или создавая свои — из раза в раз я сталкивался с проблемой их объединения в одном middleware. То есть когда к одному проекту нужно подключить два и более готовых middleware.
Проблема в том, что middleware в next.js это не тоже самое, что в express или koa — он сразу возвращает финальный результат. Поэтому каждый пакет просто создаёт финальный middleware. Например у next‑intl это выглядит следующим образом:
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en',
});
Я не первый столкнулся с этой проблемой, и в npm можно найти уже готовые решения. Все они работают через свои собственные API — сделанные в стиле express или в их собственном видении. Они полезны, хорошо реализованы и удобны. Но только в тех случаях, когда вы можете обновить каждый используемый middleware.
Однако, есть много ситуаций, когда нужно добавить уже готовые решения. Обычно в задачах этих решений можно найти «добавить поддержку для добавления пакета цепи A», «работать с пакетом цепи B». Именно для таких ситуаций и создан @nimpl/middleware‑chain.
Этот пакет позволяет создавать цепь нативных next.js middleware без каких-либо модификаций (то есть, можно добавить любой готовый middleware в цепь).
import { default as authMiddleware } from "next-auth/middleware";
import createMiddleware from "next-intl/middleware";
import { chain } from "@nimpl/middleware-chain";
const intlMiddleware = createMiddleware({
locales: ["en", "dk"],
defaultLocale: "en",
});
export default chain([
intlMiddleware,
authMiddleware,
]);
Цепь обрабатывает каждый middleware поочерёдно. При обработке собираются все модификации до завершения цепи или пока какой-нибудь элемент цепи не вернёт FinalNextResponse.
export default chain([
intlMiddleware,
(req) => {
if (req.summary.type === "redirect") return FinalNextResponse.next();
},
authMiddleware,
]);
Это не Koa и не Express, это пакет для next.js, в его уникальном стиле и в формате его API. Документация пакета @nimpl/middleware-chain.
Ну и в завершении позволю себе немного ссылок.
Мой хабр с другими полезными статьями | nimpl.tech с документацией пакетов | github с кнопкой звёздочек
Карта из точек, используемая в качестве фона изображений в начале статьи сделана mocrovector с freepik.
Спасибо @Dartess за комментарий с дельными вопросами (после которых статья была дополнена).