Українська

Дослідіть middleware у Next.js, потужну функцію для перехоплення та модифікації вхідних запитів. Дізнайтеся, як реалізувати автентифікацію, авторизацію, редиректи та A/B-тестування на практичних прикладах.

Middleware у Next.js: Майстерне перехоплення запитів для динамічних додатків

Middleware у Next.js надає гнучкий та потужний спосіб перехоплювати та модифікувати вхідні запити перед тим, як вони досягнуть ваших маршрутів. Ця можливість дозволяє реалізувати широкий спектр функцій, від автентифікації та авторизації до редиректів та A/B-тестування, оптимізуючи при цьому продуктивність. Цей вичерпний посібник проведе вас через основні концепції middleware у Next.js та продемонструє, як його ефективно використовувати.

Що таке Middleware у Next.js?

Middleware у Next.js — це функція, яка виконується до того, як запит буде завершено. Вона дозволяє вам:

Функції middleware визначаються у файлі middleware.ts (або middleware.js) у кореневому каталозі вашого проєкту. Вони виконуються для кожного маршруту у вашому додатку або для конкретних маршрутів на основі конфігурованих матчерів (matchers).

Ключові концепції та переваги

Об'єкт Request

Об'єкт request надає доступ до інформації про вхідний запит, включаючи:

Об'єкт Response

Функції middleware повертають об'єкт Response для контролю результату запиту. Ви можете використовувати такі відповіді:

Матчери (Matchers)

Матчери дозволяють вказати, до яких маршрутів має застосовуватися ваше middleware. Ви можете визначати матчери за допомогою регулярних виразів або шаблонів шляхів. Це гарантує, що ваше middleware запускається лише за потреби, покращуючи продуктивність та зменшуючи навантаження.

Edge Runtime

Middleware у Next.js працює на Edge Runtime, яке є полегшеним середовищем виконання JavaScript, що може бути розгорнуте близько до ваших користувачів. Ця близькість мінімізує затримку та покращує загальну продуктивність вашого додатка, особливо для глобально розподілених користувачів. Edge Runtime доступне в Edge Network від Vercel та на інших сумісних платформах. Edge Runtime має деякі обмеження, зокрема щодо використання Node.js API.

Практичні приклади: Реалізація функцій Middleware

1. Автентифікація

Middleware для автентифікації можна використовувати для захисту маршрутів, які вимагають, щоб користувачі були залогінені. Ось приклад того, як реалізувати автентифікацію за допомогою файлів cookie:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Це middleware перевіряє наявність файлу cookie auth_token. Якщо cookie не знайдено, користувач перенаправляється на сторінку /login. config.matcher вказує, що це middleware має виконуватися лише для маршрутів під /dashboard.

Глобальна перспектива: Адаптуйте логіку автентифікації для підтримки різних методів (наприклад, OAuth, JWT) та інтегруйте її з різними провайдерами ідентифікації (наприклад, Google, Facebook, Azure AD), щоб задовольнити потреби користувачів з різних регіонів.

2. Авторизація

Middleware для авторизації можна використовувати для контролю доступу до ресурсів на основі ролей або дозволів користувача. Наприклад, у вас може бути панель адміністратора, доступ до якої мають лише певні користувачі.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Example: Fetch user roles from an API (replace with your actual logic)
 const userResponse = await fetch('https://api.example.com/userinfo', {
 headers: {
 Authorization: `Bearer ${token}`,
 },
 });
 const userData = await userResponse.json();

 if (userData.role !== 'admin') {
 return NextResponse.redirect(new URL('/unauthorized', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/admin/:path*'],
}

Це middleware отримує роль користувача та перевіряє, чи має він роль admin. Якщо ні, його перенаправляють на сторінку /unauthorized. Цей приклад використовує заповнювач для кінцевої точки API. Замініть `https://api.example.com/userinfo` на вашу реальну кінцеву точку сервера автентифікації.

Глобальна перспектива: Пам'ятайте про правила конфіденційності даних (наприклад, GDPR, CCPA) під час обробки даних користувачів. Впроваджуйте відповідні заходи безпеки для захисту конфіденційної інформації та забезпечення дотримання місцевих законів.

3. Редирект

Middleware для редиректу можна використовувати для перенаправлення користувачів на основі їхнього місцезнаходження, мови або інших критеріїв. Наприклад, ви можете перенаправляти користувачів на локалізовану версію вашого веб-сайту на основі їхньої IP-адреси.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const country = request.geo?.country || 'US'; // Default to US if geo-location fails

 if (country === 'DE') {
 return NextResponse.redirect(new URL('/de', request.url))
 }

 if (country === 'FR') {
 return NextResponse.redirect(new URL('/fr', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/'],
}

Це middleware перевіряє країну користувача на основі його IP-адреси та перенаправляє його на відповідну локалізовану версію веб-сайту (/de для Німеччини, /fr для Франції). Якщо геолокація не вдається, за замовчуванням використовується американська версія. Зауважте, що це залежить від доступності властивості `geo` (наприклад, при розгортанні на Vercel).

Глобальна перспектива: Переконайтеся, що ваш веб-сайт підтримує кілька мов та валют. Надайте користувачам можливість вручну вибирати бажану мову або регіон. Використовуйте відповідні формати дати та часу для кожної локалі.

4. A/B-тестування

Middleware можна використовувати для реалізації A/B-тестування, випадковим чином призначаючи користувачів різним варіантам сторінки та відстежуючи їхню поведінку. Ось спрощений приклад:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

function getRandomVariant() {
 return Math.random() < 0.5 ? 'A' : 'B';
}

export function middleware(request: NextRequest) {
 let variant = request.cookies.get('variant')?.value;

 if (!variant) {
 variant = getRandomVariant();
 const response = NextResponse.next();
 response.cookies.set('variant', variant);
 return response;
 }

 if (variant === 'B') {
 return NextResponse.rewrite(new URL('/variant-b', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/'],
}

Це middleware призначає користувачів або варіанту 'A', або 'B'. Якщо у користувача ще немає файлу cookie variant, він випадково призначається і встановлюється. Користувачі, яким призначено варіант 'B', переписуються на сторінку /variant-b. Потім ви відстежуєте продуктивність кожного варіанту, щоб визначити, який з них ефективніший.

Глобальна перспектива: Враховуйте культурні відмінності при розробці A/B-тестів. Те, що добре працює в одному регіоні, може не знайти відгуку у користувачів в іншому. Переконайтеся, що ваша платформа для A/B-тестування відповідає правилам конфіденційності в різних регіонах.

5. Флаги функцій (Feature Flags)

Флаги функцій дозволяють вмикати або вимикати функції у вашому додатку без розгортання нового коду. Middleware можна використовувати для визначення, чи повинен користувач мати доступ до певної функції на основі його ID, місцезнаходження або інших критеріїв.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 // Example: Fetch feature flags from an API
 const featureFlagsResponse = await fetch('https://api.example.com/featureflags', {
 headers: {
 'X-User-Id': 'user123',
 },
 });
 const featureFlags = await featureFlagsResponse.json();

 if (featureFlags.new_feature_enabled) {
 // Enable the new feature
 return NextResponse.next();
 } else {
 // Disable the new feature (e.g., redirect to an alternative page)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

export const config = {
 matcher: ['/new-feature'],
}

Це middleware отримує флаги функцій з API і перевіряє, чи встановлено прапор new_feature_enabled. Якщо так, користувач може отримати доступ до сторінки /new-feature. В іншому випадку його перенаправляють на /alternative-page.

Глобальна перспектива: Використовуйте флаги функцій для поступового розгортання нових функцій для користувачів у різних регіонах. Це дозволяє вам відстежувати продуктивність та вирішувати будь-які проблеми перед випуском функції для ширшої аудиторії. Також переконайтеся, що ваша система флагів функцій масштабується глобально та надає послідовні результати незалежно від місцезнаходження користувача. Враховуйте регіональні регуляторні обмеження для розгортання функцій.

Просунуті техніки

Ланцюгування Middleware

Ви можете об'єднувати кілька функцій middleware в ланцюжок для виконання послідовності операцій над запитом. Це може бути корисно для розбиття складної логіки на менші, більш керовані модулі.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const response = NextResponse.next();

 // First middleware function
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Second middleware function
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Цей приклад показує два middleware в одному. Перший виконує автентифікацію, а другий встановлює власний заголовок.

Використання змінних середовища

Зберігайте конфіденційну інформацію, таку як ключі API та облікові дані бази даних, у змінних середовища, а не жорстко кодуйте їх у ваших функціях middleware. Це покращує безпеку та полегшує керування конфігурацією вашого додатка.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const API_KEY = process.env.API_KEY;

export async function middleware(request: NextRequest) {
 const response = await fetch('https://api.example.com/data', {
 headers: {
 'X-API-Key': API_KEY,
 },
 });

 // ...
}

export const config = {
 matcher: ['/data'],
}

У цьому прикладі API_KEY отримується зі змінної середовища.

Обробка помилок

Впроваджуйте надійну обробку помилок у ваших функціях middleware, щоб запобігти несподіваним помилкам, які можуть призвести до збою вашого додатка. Використовуйте блоки try...catch для перехоплення винятків та належного логування помилок.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 try {
 const response = await fetch('https://api.example.com/data');
 // ...
 } catch (error) {
 console.error('Error fetching data:', error);
 return NextResponse.error(); // Or redirect to an error page
 }
}

export const config = {
 matcher: ['/data'],
}

Найкращі практики

Вирішення поширених проблем

Висновок

Middleware у Next.js — це потужний інструмент для створення динамічних та персоналізованих веб-додатків. Опанувавши перехоплення запитів, ви можете реалізувати широкий спектр функцій, від автентифікації та авторизації до редиректів та A/B-тестування. Дотримуючись найкращих практик, викладених у цьому посібнику, ви зможете використовувати middleware Next.js для створення високопродуктивних, безпечних та масштабованих додатків, що відповідають потребам вашої глобальної бази користувачів. Використовуйте потужність middleware, щоб відкрити нові можливості у ваших проєктах на Next.js та забезпечити винятковий досвід користувачів.