Istražite napredne tehnike izmjene zahtjeva koristeći Next.js middleware. Naučite upravljati složenim usmjeravanjem, autentifikacijom, A/B testiranjem i strategijama lokalizacije za robusne web aplikacije.
Granični slučajevi Next.js Middlewarea: Ovladavanje uzorcima za izmjenu zahtjeva
Next.js middleware pruža moćan mehanizam za presretanje i izmjenu zahtjeva prije nego što stignu do ruta vaše aplikacije. Ova mogućnost otvara širok spektar prilika, od jednostavnih provjera autentifikacije do složenih scenarija A/B testiranja i strategija internacionalizacije. Međutim, učinkovito korištenje middlewarea zahtijeva duboko razumijevanje njegovih graničnih slučajeva i potencijalnih zamki. Ovaj sveobuhvatni vodič istražuje napredne uzorke za izmjenu zahtjeva, pružajući praktične primjere i korisne uvide koji će vam pomoći u izgradnji robusnih i performantnih Next.js aplikacija.
Razumijevanje osnova Next.js Middlewarea
Prije nego što zaronimo u napredne uzorke, ponovimo osnove Next.js middlewarea. Middleware funkcije se izvršavaju prije nego što se zahtjev dovrši, omogućujući vam da:
- Prepisujete URL-ove: Preusmjeravate korisnike na različite stranice na temelju određenih kriterija.
- Preusmjeravate korisnike: Šaljete korisnike na potpuno različite URL-ove, često u svrhe autentifikacije ili autorizacije.
- Mijenjate zaglavlja: Dodajete, uklanjate ili ažurirate HTTP zaglavlja.
- Odgovarate izravno: Vraćate odgovor izravno iz middlewarea, zaobilazeći Next.js rute.
Middleware funkcije nalaze se u datoteci middleware.js
ili middleware.ts
u vašem /pages
ili /app
direktoriju (ovisno o vašoj verziji i postavkama Next.js-a). One primaju NextRequest
objekt koji predstavlja dolazni zahtjev i mogu vratiti NextResponse
objekt za kontrolu daljnjeg ponašanja.
Primjer: Osnovni middleware za autentifikaciju
Ovaj primjer demonstrira jednostavnu provjeru autentifikacije. Ako korisnik nije autentificiran (npr. nema valjanog tokena u kolačiću), preusmjerava se na stranicu za prijavu.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const authToken = request.cookies.get('authToken')
if (!authToken) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/protected/:path*'],
}
Ovaj middleware će se izvršiti samo za rute koje odgovaraju /protected/:path*
. Provjerava postojanje kolačića authToken
. Ako kolačić nedostaje, korisnik se preusmjerava na stranicu /login
. U suprotnom, zahtjev se normalno nastavlja pomoću NextResponse.next()
.
Napredni uzorci za izmjenu zahtjeva
Sada, istražimo neke napredne uzorke za izmjenu zahtjeva koji pokazuju pravu moć Next.js middlewarea.
1. A/B testiranje pomoću kolačića (Cookies)
A/B testiranje je ključna tehnika za optimizaciju korisničkog iskustva. Middleware se može koristiti za nasumično dodjeljivanje korisnika različitim varijacijama vaše aplikacije i praćenje njihovog ponašanja. Ovaj uzorak se oslanja na kolačiće kako bi se zadržala dodijeljena varijanta korisnika.
Primjer: A/B testiranje odredišne stranice
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const VARIANT_A = 'variantA'
const VARIANT_B = 'variantB'
export function middleware(request: NextRequest) {
let variant = request.cookies.get('variant')?.value
if (!variant) {
// Randomly assign a variant
variant = Math.random() < 0.5 ? VARIANT_A : VARIANT_B
const response = NextResponse.next()
response.cookies.set('variant', variant)
return response
}
if (variant === VARIANT_A) {
return NextResponse.rewrite(new URL('/variant-a', request.url))
} else if (variant === VARIANT_B) {
return NextResponse.rewrite(new URL('/variant-b', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/'],
}
U ovom primjeru, kada korisnik prvi put posjeti korijensku putanju (/
), middleware ga nasumično dodjeljuje ili variantA
ili variantB
. Ova varijanta se sprema u kolačić. Naknadni zahtjevi istog korisnika bit će prepisani na /variant-a
ili /variant-b
, ovisno o dodijeljenoj varijanti. To vam omogućuje posluživanje različitih odredišnih stranica i praćenje koja ima bolje rezultate. Osigurajte da imate definirane rute za /variant-a
i /variant-b
u svojoj Next.js aplikaciji.
Globalna razmatranja: Prilikom provođenja A/B testiranja, uzmite u obzir regionalne varijacije. Dizajn koji odjekuje u Sjevernoj Americi možda neće biti jednako učinkovit u Aziji. Možete koristiti geolokacijske podatke (dobivene putem IP adrese ili korisničkih preferencija) kako biste prilagodili A/B test specifičnim regijama.
2. Lokalizacija (i18n) s prepisivanjem URL-ova
Internacionalizacija (i18n) je ključna za dosezanje globalne publike. Middleware se može koristiti za automatsko otkrivanje preferiranog jezika korisnika i preusmjeravanje na odgovarajuću lokaliziranu verziju vaše stranice.
Primjer: Preusmjeravanje na temelju zaglavlja `Accept-Language`
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const SUPPORTED_LANGUAGES = ['en', 'fr', 'es', 'de']
const DEFAULT_LANGUAGE = 'en'
function getPreferredLanguage(request: NextRequest): string {
const acceptLanguage = request.headers.get('accept-language')
if (!acceptLanguage) {
return DEFAULT_LANGUAGE
}
const languages = acceptLanguage.split(',').map((lang) => lang.split(';')[0].trim())
for (const lang of languages) {
if (SUPPORTED_LANGUAGES.includes(lang)) {
return lang
}
}
return DEFAULT_LANGUAGE
}
export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname
// Check if there's an existing locale in the pathname
if (
SUPPORTED_LANGUAGES.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
) {
return NextResponse.next()
}
const preferredLanguage = getPreferredLanguage(request)
return NextResponse.redirect(
new URL(`/${preferredLanguage}${pathname}`, request.url)
)
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)'
],
}
Ovaj middleware izvlači zaglavlje Accept-Language
iz zahtjeva i određuje preferirani jezik korisnika. Ako URL već ne sadrži jezični prefiks (npr. /en/about
), middleware preusmjerava korisnika na odgovarajući lokalizirani URL (npr. /fr/about
za francuski). Pobrinite se da imate odgovarajuću strukturu mapa u svom /pages
ili /app
direktoriju za različite lokalizacije. Na primjer, trebat će vam datoteke /pages/en/about.js
i /pages/fr/about.js
.
Globalna razmatranja: Osigurajte da vaša i18n implementacija ispravno rukuje jezicima koji se pišu s desna na lijevo (npr. arapski, hebrejski). Također, razmislite o korištenju mreže za isporuku sadržaja (CDN) za posluživanje lokaliziranih resursa s poslužitelja bližih vašim korisnicima, poboljšavajući performanse.
3. Zastavice funkcionalnosti (Feature Flags)
Zastavice funkcionalnosti omogućuju vam da omogućite ili onemogućite značajke u svojoj aplikaciji bez implementacije novog koda. Ovo je posebno korisno za postupno uvođenje novih značajki ili za testiranje značajki u produkciji. Middleware se može koristiti za provjeru statusa zastavice funkcionalnosti i odgovarajuću izmjenu zahtjeva.
Primjer: Omogućavanje beta funkcionalnosti
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const BETA_FEATURE_ENABLED = process.env.BETA_FEATURE_ENABLED === 'true'
export function middleware(request: NextRequest) {
if (BETA_FEATURE_ENABLED && request.nextUrl.pathname.startsWith('/new-feature')) {
return NextResponse.next()
}
// Optionally redirect to a "feature unavailable" page
return NextResponse.rewrite(new URL('/feature-unavailable', request.url))
}
export const config = {
matcher: ['/new-feature/:path*'],
}
Ovaj middleware provjerava vrijednost varijable okruženja BETA_FEATURE_ENABLED
. Ako je postavljena na true
i korisnik pokušava pristupiti ruti pod /new-feature
, zahtjev se dopušta. U suprotnom, korisnik se preusmjerava na stranicu /feature-unavailable
. Ne zaboravite odgovarajuće konfigurirati varijable okruženja za različita okruženja (razvojno, testno, produkcijsko).
Globalna razmatranja: Kada koristite zastavice funkcionalnosti, uzmite u obzir pravne implikacije omogućavanja značajki koje možda nisu u skladu s propisima u svim regijama. Na primjer, značajke povezane s privatnošću podataka možda će trebati biti onemogućene u određenim zemljama.
4. Detekcija uređaja i prilagodljivo usmjeravanje
Moderne web aplikacije moraju biti responzivne i prilagoditi se različitim veličinama zaslona i mogućnostima uređaja. Middleware se može koristiti za otkrivanje vrste uređaja korisnika i preusmjeravanje na optimizirane verzije vaše stranice.
Primjer: Preusmjeravanje mobilnih korisnika na poddomenu optimiziranu za mobilne uređaje
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { device } from 'detection'
export function middleware(request: NextRequest) {
const userAgent = request.headers.get('user-agent')
if (userAgent) {
const deviceType = device(userAgent)
if (deviceType.type === 'phone') {
const mobileUrl = new URL(request.url)
mobileUrl.hostname = 'm.example.com'
return NextResponse.redirect(mobileUrl)
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/'],
}
Ovaj primjer koristi biblioteku `detection` za određivanje vrste uređaja korisnika na temelju zaglavlja User-Agent
. Ako je korisnik na mobilnom telefonu, preusmjerava se na poddomenu m.example.com
(pod pretpostavkom da tamo imate verziju svoje stranice optimiziranu za mobilne uređaje). Ne zaboravite instalirati paket `detection`: `npm install detection`.
Globalna razmatranja: Osigurajte da vaša logika za detekciju uređaja uzima u obzir regionalne varijacije u korištenju uređaja. Na primjer, obični mobilni telefoni (feature phones) još su uvijek prisutni u nekim zemljama u razvoju. Razmislite o korištenju kombinacije detekcije User-Agenta i tehnika responzivnog dizajna za robusnije rješenje.
5. Obogaćivanje zaglavlja zahtjeva
Middleware može dodati informacije u zaglavlja zahtjeva prije nego što ih obrade rute vaše aplikacije. Ovo je korisno za dodavanje prilagođenih metapodataka, kao što su korisničke uloge, status autentifikacije ili ID-jevi zahtjeva, koje vaša aplikacijska logika može koristiti.
Primjer: Dodavanje ID-a zahtjeva
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const requestId = uuidv4()
const response = NextResponse.next()
response.headers.set('x-request-id', requestId)
return response
}
export const config = {
matcher: ['/api/:path*'], // Only apply to API routes
}
Ovaj middleware generira jedinstveni ID zahtjeva pomoću biblioteke uuid
i dodaje ga u zaglavlje x-request-id
. Ovaj ID se zatim može koristiti za bilježenje, praćenje i otklanjanje pogrešaka. Ne zaboravite instalirati paket `uuid`: `npm install uuid`.
Globalna razmatranja: Prilikom dodavanja prilagođenih zaglavlja, pazite na ograničenja veličine zaglavlja. Prekoračenje tih ograničenja može dovesti do neočekivanih pogrešaka. Također, osigurajte da su sve osjetljive informacije dodane u zaglavlja pravilno zaštićene, posebno ako je vaša aplikacija iza reverznog proxyja ili CDN-a.
6. Sigurnosna poboljšanja: Ograničavanje broja zahtjeva (Rate Limiting)
Middleware može djelovati kao prva linija obrane od zlonamjernih napada implementacijom ograničavanja broja zahtjeva. To sprječava zlouporabu ograničavanjem broja zahtjeva koje klijent može uputiti unutar određenog vremenskog okvira.
Primjer: Osnovno ograničavanje broja zahtjeva korištenjem jednostavnog spremišta
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const requestCounts: { [ip: string]: number } = {}
const WINDOW_SIZE_MS = 60000; // 1 minute
const MAX_REQUESTS_PER_WINDOW = 100;
export function middleware(request: NextRequest) {
const clientIP = request.ip || '127.0.0.1' // Get client IP, default to localhost for local testing
if (!requestCounts[clientIP]) {
requestCounts[clientIP] = 0;
}
requestCounts[clientIP]++;
if (requestCounts[clientIP] > MAX_REQUESTS_PER_WINDOW) {
return new NextResponse(
JSON.stringify({ message: 'Too many requests' }),
{ status: 429, headers: { 'Content-Type': 'application/json' } }
);
}
// Reset count after window
setTimeout(() => {
requestCounts[clientIP]--;
if (requestCounts[clientIP] <= 0) {
delete requestCounts[clientIP];
}
}, WINDOW_SIZE_MS);
return NextResponse.next();
}
export const config = {
matcher: ['/api/:path*'], // Apply to all API routes
}
Ovaj primjer održava jednostavno spremište u memoriji (requestCounts
) za praćenje broja zahtjeva s svake IP adrese. Ako klijent premaši MAX_REQUESTS_PER_WINDOW
unutar WINDOW_SIZE_MS
, middleware vraća pogrešku 429 Too Many Requests
. Važno: Ovo je pojednostavljen primjer i nije prikladan za produkcijska okruženja jer se ne skalira i ranjiv je na napade uskraćivanjem usluge (denial-of-service). Za produkcijsku upotrebu, razmislite o korištenju robusnijeg rješenja za ograničavanje broja zahtjeva poput Redisa ili namjenske usluge za to.
Globalna razmatranja: Strategije ograničavanja broja zahtjeva trebale bi biti prilagođene specifičnim karakteristikama vaše aplikacije i geografskoj distribuciji vaših korisnika. Razmislite o korištenju različitih ograničenja za različite regije ili segmente korisnika.
Granični slučajevi i potencijalne zamke
Iako je middleware moćan alat, ključno je biti svjestan njegovih ograničenja i potencijalnih zamki:
- Utjecaj na performanse: Middleware dodaje opterećenje svakom zahtjevu. Izbjegavajte izvođenje računski skupih operacija u middlewareu, jer to može značajno utjecati na performanse. Profilirajte svoj middleware kako biste identificirali i optimizirali bilo kakva uska grla u performansama.
- Složenost: Prekomjerna upotreba middlewarea može vašu aplikaciju učiniti težom za razumijevanje i održavanje. Koristite middleware promišljeno i osigurajte da svaka middleware funkcija ima jasnu i dobro definiranu svrhu.
- Testiranje: Testiranje middlewarea može biti izazovno, jer zahtijeva simulaciju HTTP zahtjeva i provjeru rezultirajućih odgovora. Koristite alate poput Jest i Supertest za pisanje sveobuhvatnih jediničnih i integracijskih testova za svoje middleware funkcije.
- Upravljanje kolačićima: Budite oprezni pri postavljanju kolačića u middlewareu, jer to može utjecati na ponašanje predmemoriranja (caching). Osigurajte da razumijete implikacije predmemoriranja temeljenog na kolačićima i konfigurirajte svoja zaglavlja predmemorije u skladu s tim.
- Varijable okruženja: Osigurajte da su sve varijable okruženja koje se koriste u vašem middlewareu pravilno konfigurirane za različita okruženja (razvojno, testno, produkcijsko). Koristite alat poput Dotenv za upravljanje varijablama okruženja.
- Ograničenja rubnih funkcija (Edge Functions): Zapamtite da se middleware izvršava kao Edge funkcije, koje imaju ograničenja u vremenu izvršavanja, korištenju memorije i veličini koda. Držite svoje middleware funkcije laganima i učinkovitima.
Najbolje prakse za korištenje Next.js Middlewarea
Da biste maksimalno iskoristili prednosti Next.js middlewarea i izbjegli potencijalne probleme, slijedite ove najbolje prakse:
- Neka bude jednostavno: Svaka middleware funkcija trebala bi imati jednu, dobro definiranu odgovornost. Izbjegavajte stvaranje previše složenih middleware funkcija koje obavljaju više zadataka.
- Optimizirajte za performanse: Minimizirajte količinu obrade u middlewareu kako biste izbjegli uska grla u performansama. Koristite strategije predmemoriranja kako biste smanjili potrebu za ponovljenim izračunima.
- Testirajte temeljito: Napišite sveobuhvatne jedinične i integracijske testove za svoje middleware funkcije kako biste osigurali da se ponašaju kako se očekuje.
- Dokumentirajte svoj kod: Jasno dokumentirajte svrhu i funkcionalnost svake middleware funkcije kako biste poboljšali održivost.
- Pratite svoju aplikaciju: Koristite alate za praćenje kako biste pratili performanse i stope pogrešaka vaših middleware funkcija.
- Razumijte redoslijed izvršavanja: Budite svjesni redoslijeda kojim se middleware funkcije izvršavaju, jer to može utjecati na njihovo ponašanje.
- Koristite varijable okruženja mudro: Koristite varijable okruženja za konfiguriranje svojih middleware funkcija za različita okruženja.
Zaključak
Next.js middleware nudi moćan način za izmjenu zahtjeva i prilagodbu ponašanja vaše aplikacije na rubu mreže (edge). Razumijevanjem naprednih uzoraka za izmjenu zahtjeva o kojima se govori u ovom vodiču, možete izgraditi robusne, performantne i globalno svjesne Next.js aplikacije. Ne zaboravite pažljivo razmotriti granične slučajeve i potencijalne zamke te slijediti najbolje prakse navedene gore kako biste osigurali da su vaše middleware funkcije pouzdane i održive. Prihvatite moć middlewarea kako biste stvorili izvanredna korisnička iskustva i otključali nove mogućnosti za svoje web aplikacije.