EMZETT.
Увійти

proxy.ts (наступник middleware Next.js)

Коротко: У Next.js 16 middleware.ts перейменовано на proxy.ts — у проєкті може існувати лише один із двох файлів, інакше збірка ламається.

Докладніше: Як і колишній middleware, він виконується до самого відображення/маршрутизації і може переписувати або перенаправляти запити чи достроково переривати зі своєю відповіддю (наприклад, 429 за перевищення ліміту частоти запитів). Matcher (config.matcher) визначає, для яких шляхів функція взагалі викликається.

Наш контекст: В Emzett proxy.ts навмисно розв’язує ДВА завдання в одному файлі: глобальне обмеження частоти запитів (широкий matcher, майже кожен запит, крім суто читальних сторінок без форм) і легке перенаправлення автентифікації для захищених областей (лише перевірка «cookie є» — повна перевірка сеансу за базою даних, як і раніше, виконується на сервері в getCurrentUser()). Згідно з AGENTS.md у корені проєкту — навмисно задокументована пастка Next.js 16, бо знання з навчання тут досі передбачають middleware.ts.

Подробиці

Matcher як важіль продуктивності

matcher в експортованому config важливий для продуктивності: якщо функція виконується за КОЖНОГО запиту (включно з усіма статичними ресурсами), це коштує зайвих викликів edge-функцій — кожен виклик означає додаткову затримку й на більшості платформ хостингу додаткові витрати. Типовий шаблон спеціально виключає те, що ніколи не мусить проходити через middleware:

export const config = {
  matcher: [
    "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)",
  ],
};

Усередині matcher компілюється в регулярний вираз і перевіряється ПЕРЕД кожним запитом — тобто виконується, навіть якщо сама функція middleware зрештою не запускається. Статичні рядки matcher (як вище) продуктивніші, ніж зібрані динамічно методами масивів, бо Next.js може повністю проаналізувати їх під час збірки.

Переписування, перенаправлення або переривання

Усередині функції middleware є по суті три способи відповісти: NextResponse.next() дозволяє запиту тривати без змін, NextResponse.redirect(url) надсилає браузеру справжнє перенаправлення HTTP (URL в адресному рядку помітно змінюється), а NextResponse.rewrite(url) віддає вміст з іншого внутрішнього URL, щоб браузер цього не помічав (адресний рядок не змінюється). Для перенаправлень автентифікації зазвичай підходить redirect() (користувач має бачити, що потрапляє, наприклад, на /login), а rewrite() краще підходить для внутрішнього зіставлення зі статичними експортами чи варіантами A/B-тестів.

Взаємодія з rewrites() із next.config.ts

Важливий, легко пропущений момент: rewrites() із next.config.ts і middleware виконуються на різних фазах обробки запиту — якщо шлях через rewrite вказує на цілком інший самостійний статичний вміст (наприклад, заздалегідь зібраний статичний експорт сайту), а matcher middleware все ще його покриває, УСІ завантажувані потім ресурси цієї сторінки (CSS, JS, JSON) окремо проходять через ту саму логіку middleware — за глобально застосовуваного обмеження частоти запитів це легко перевищує ліміт для одного «перегляду сторінки», бо враховуються десятки окремих запитів замість одного. Для таких шляхів тоді потрібен явний виняток у matcher чи у власній логіці middleware.

Межі середовища виконання edge

Middleware/proxy.ts за замовчуванням виконується в середовищі edge, а не в повноцінному середовищі Node.js — багато API Node (наприклад, нативний доступ fs до файлової системи, деякі драйвери баз даних) там недоступні. Це одна з причин, чому перевірки автентифікації в proxy.ts зазвичай виконують лише легку перевірку «cookie є» (замість справжнього запиту до бази даних), тоді як справжня повна перевірка сеансу, як і раніше, відбувається у звичайному серверному компоненті чи обробнику маршруту з повноцінним середовищем Node.js.

Див. також: Next.js App Router, Обмеження частоти запитів, Відкрите перенаправлення