Upstash Redis rozwiązuje bardzo konkretny problem nowoczesnych aplikacji Next.js: potrzebujesz współdzielonego stanu między requestami, ale nie chcesz utrzymywać własnej instancji Redis.
Problem w tym, że tradycyjny Redis wymaga serwera, zarządzania połączeniami i pilnowania kosztów. Upstash zmienia tę grę — to serverless Redis z HTTP API, czyli Application Programming Interface, definiuje sposób komunikacji między aplikacjami lub modułami., idealnie dopasowany do architektury Next.js i Vercel.
W tym artykule pokażę trzy najczęstsze zastosowania Upstash Redis w produkcyjnych aplikacjach Next.js i zatrzymam się na poziomie, który wystarcza w większości projektów: sensowny setup, działające przykłady i najważniejsze decyzje architektoniczne bez rozbudowywania osobnego deep dive dla każdego use case'u.
Krótka odpowiedź: Upstash Redis to Serverless to model uruchamiania kodu, w którym nie zarządzasz ręcznie serwerem, a płacisz zwykle za wykonania lub użycie. Redis z HTTP API, który idealnie pasuje do architektury Next.js i Vercel — nie wymaga stałego połączenia TCP ani własnej infrastruktury. Najczęstsze zastosowania to: sesje server-side z natychmiastowym unieważnianiem, cache odpowiedzi zewnętrznych API oraz rate limiting chroniący endpointy przed nadużyciami. Warto po niego sięgać, gdy potrzebujesz współdzielonego stanu między requestami i instancjami, a nie tylko lokalnego cache Next.js.
Czym jest Upstash Redis?
Upstash to serverless Redis z kilkoma kluczowymi różnicami:
HTTP API — nie potrzebujesz stałego połączenia TCP
Pay-per-request — płacisz za operacje, nie za czas działania
Global replication — dane blisko użytkowników
Kompatybilność z Redis — większość komend działa tak samo
Code
npm install @upstash/redis
Po utworzeniu bazy w Upstash Console (lub przez Vercel Marketplace) dostajesz dwie zmienne środowiskowe:
// lib/redis.tsimport { Redis } from '@upstash/redis'export const redis = new Redis({ url: process.env.UPSTASH_REDIS_REST_URL!, token: process.env.UPSTASH_REDIS_REST_TOKEN!,})// Lub jeszcze prościej — automatycznie czyta z envexport const redis = Redis.fromEnv()
Od tego momentu masz dostęp do pełnego API Redis.
Zastosowanie 1: Sesje użytkowników
Domyślne sesje w Next.js (np. przez NextAuth) używają JWT, czyli JSON Web Token, to podpisany token używany często do autoryzacji i przekazywania tożsamości użytkownika. lub bazy danych. Redis oferuje trzecią opcję — szybkie sesje server-side z możliwością natychmiastowego unieważnienia.
Prosta implementacja sesji
Code
// lib/session.tsimport { redis } from './redis'import { cookies } from 'next/headers'import { nanoid } from 'nanoid'const SESSION_TTL = 60 * 60 * 24 * 7 // 7 dni w sekundachinterface Session { userId: string email: string createdAt: number}export async function createSession( userId: string, email: string,): Promise<string> { const sessionId = nanoid(32) const session: Session = { userId, email, createdAt: Date.now(), } await redis.setex(`session:${sessionId}`, SESSION_TTL, session) ;(await cookies()).set('session_id', sessionId, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: SESSION_TTL, }) return sessionId}export async function getSession(): Promise<Session | null> { const sessionId = (await cookies()).get('session_id')?.value if (!sessionId) return null const data = await redis.get<Session>(`session:${sessionId}`) if (!data) return null return data}export async function destroySession(): Promise<void> { const sessionId = (await cookies()).get('session_id')?.value if (!sessionId) return await redis.del(`session:${sessionId}`) ;(await cookies()).delete('session_id')}
Użycie w Server Component
Code
// app/dashboard/page.tsximport { getSession } from '@/lib/session'import { redirect } from 'next/navigation'export default async function DashboardPage() { const session = await getSession() if (!session) { redirect('/login') } return ( <main> <h1>Witaj, {session.email}</h1> </main> )}
Wylogowanie przez Server Action
Code
// app/actions/auth.ts'use server'import { destroySession } from '@/lib/session'import { redirect } from 'next/navigation'export async function logout() { await destroySession() redirect('/login')}
Zalety sesji w Redis:
Natychmiastowe wylogowanie — usuń klucz i sesja znika
Wylogowanie ze wszystkich urządzeń — usuń wszystkie klucze session:* dla użytkownika
Brak rozmiaru JWT — sesja może przechowywać więcej danych
TTL — sesje automatycznie wygasają
Zastosowanie 2: Cache danych API
Zewnętrzne API są wolne i często mają limity requestów. Redis pozwala cachować odpowiedzi i serwować je błyskawicznie.
Ważna uwaga produkcyjna: unikałbym masowego KEYS pattern jako standardowej strategii invalidacji. W małym projekcie to przejdzie, ale przy większej skali lepiej trzymać jawne klucze, wersjonować namespace albo utrzymywać własne mapy tagów do usuwania.
Cachowanie odpowiedzi API
Code
// lib/api/products.tsimport { cached, invalidate } from '@/lib/cache'interface Product { id: string name: string price: number}export async function getProducts(): Promise<Product[]> { return cached( 'products:all', async () => { const res = await fetch('https://api.example.com/products') return res.json() }, { ttl: 300 }, // 5 minut )}export async function getProduct(id: string): Promise<Product | null> { return cached( `products:${id}`, async () => { const res = await fetch(`https://api.example.com/products/${id}`) if (!res.ok) return null return res.json() }, { ttl: 600 }, // 10 minut )}// Po aktualizacji produktuexport async function invalidateProductCache(id: string): Promise<void> { await invalidate(`products:${id}`) await invalidate('products:all')}
Rate limiting chroni API przed nadużyciami. Upstash oferuje dedykowaną bibliotekę @upstash/ratelimit, która implementuje sprawdzone algorytmy.
Code
npm install @upstash/ratelimit
Konfiguracja rate limitera
Code
// lib/ratelimit.tsimport { Ratelimit } from '@upstash/ratelimit'import { redis } from './redis'// 10 żądań na 10 sekund (sliding window)export const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(10, '10 s'), analytics: true, // opcjonalne statystyki w Upstash Console prefix: 'ratelimit',})// Warianty dla różnych endpointówexport const strictRatelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, '1 m'), // 5 na minutę prefix: 'ratelimit:strict',})export const authRatelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, '15 m'), // 5 prób logowania na 15 minut prefix: 'ratelimit:auth',})
Jaki algorytm wybrać?
Nie musisz analizować całej teorii kolejek, żeby wdrożyć sensowny limiter:
Sliding window to najlepszy domyślny wybór dla większości projektów, bo daje przewidywalne zachowanie bez dużych skoków na granicach okna.
Fixed window jest najprostszy, ale potrafi przepuścić nienaturalny burst na styku dwóch okien czasowych.
Token bucket ma sens, gdy chcesz pozwolić na krótkie piki ruchu, ale nadal ograniczać średnią intensywność zapytań.
Jeśli nie masz bardzo specyficznych wymagań, zacznij od slidingWindow.
Middleware z rate limitingiem
Code
// middleware.tsimport { NextResponse } from 'next/server'import type { NextRequest } from 'next/server'import { ratelimit } from '@/lib/ratelimit'export async function middleware(request: NextRequest) { // Tylko dla API routes if (!request.nextUrl.pathname.startsWith('/api')) { return NextResponse.next() } const ip = request.headers.get('x-real-ip') ?? request.headers.get('x-forwarded-for')?.split(',')[0] ?? 'anonymous' const { success, limit, reset, remaining } = await ratelimit.limit(ip) if (!success) { return NextResponse.json( { error: 'Too many requests' }, { status: 429, headers: { 'X-RateLimit-Limit': limit.toString(), 'X-RateLimit-Remaining': remaining.toString(), 'X-RateLimit-Reset': reset.toString(), }, }, ) } const response = NextResponse.next() response.headers.set('X-RateLimit-Limit', limit.toString()) response.headers.set('X-RateLimit-Remaining', remaining.toString()) response.headers.set('X-RateLimit-Reset', reset.toString()) return response}export const config = { matcher: '/api/:path*',}
IP, user ID czy oba?
Najprostszy identyfikator to IP, ale nie zawsze jest wystarczający. Za load balancerem albo na Vercelu musisz uważać na nagłówki proxy, zwykle korzystając z x-forwarded-for lub podobnego źródła adresu klienta. Dla endpointów po zalogowaniu lepszym kluczem bywa user:${userId}, bo chroni to konkretną tożsamość, a nie tylko adres sieciowy współdzielony z innymi użytkownikami. W praktyce często najlepiej działa układ mieszany: IP dla tras publicznych i formularzy anonimowych, user ID dla akcji wykonywanych po autoryzacji.
Rate limiting w Server Action
Code
// app/actions/contact.ts'use server'import { headers } from 'next/headers'import { strictRatelimit } from '@/lib/ratelimit'export async function sendContactForm(formData: FormData) { const ip = (await headers()).get('x-forwarded-for') ?? 'anonymous' const { success } = await strictRatelimit.limit(ip) if (!success) { return { error: 'Zbyt wiele wiadomości. Spróbuj później.' } } // ... logika wysyłania formularza return { success: true }}
Bonus: Liczniki i statystyki
Redis świetnie nadaje się do liczników w czasie rzeczywistym:
Takie liczniki sprawdzają się do prostych metryk technicznych albo paneli admina. Do klasycznego trackingu pageview nie wrzucałbym inkrementacji bezpośrednio w render Server Component, bo render może wykonać się więcej niż raz. Do analityki użytkownika lepsze są eventy wysyłane jawnie z klienta albo przez route handler.
Kiedy Redis nie jest potrzebny
To ważny kontrapunkt, bo Redis łatwo dodać zbyt wcześnie.
Jeśli potrzebujesz tylko:
cache'owania fetch() w App Router,
rewalidacji po tagach,
prostego ISR, czyli Incremental Static Regeneration, pozwala odświeżać strony statyczne po czasie bez pełnego rebuildu. i odświeżania stron po webhooku,
to często wystarczy wbudowany model cache Next.js. Redis ma sens wtedy, gdy naprawdę potrzebujesz współdzielonego stanu między requestami i instancjami: sesji server-side, rate limitingu, liczników, kolejki albo wspólnego cache dla wielu procesów.
Koszty i limity
Upstash ma darmowy tier i model pay-as-you-go, ale konkretne limity i ceny warto zawsze sprawdzić w aktualnym cenniku. Dla większości projektów developmentowych i małych aplikacji próg wejścia jest niski, ale przy intensywnym cachowaniu i rate limitingu dobrze policzyć liczbę operacji, nie tylko rozmiar danych.
FAQ
Czym Upstash Redis różni się od zwykłego Redisa?
Upstash używa HTTP API zamiast stałego połączenia TCP, co czyni go kompatybilnym ze środowiskami serverless (Vercel Edge, AWS Lambda). Rozliczenia są per-request, a nie za czas działania instancji, więc idealnie skaluje się do zera. Większość standardowych komend Redis działa tak samo, ale część wzorców opartych o długie lub blokujące połączenia wymaga innego podejścia niż w klasycznym kliencie TCP. Upstash obsługuje np. pub/sub przez SSE, czyli Server-Sent Events, pozwala serwerowi przesyłać strumień aktualizacji do przeglądarki przez jedno połączenie HTTP., ale nie każdy scenariusz znany z tradycyjnego Redisa będzie równie wygodny w architekturze serverless.
Jak skonfigurować Upstash Redis w projekcie Next.js?
Zainstaluj pakiet @upstash/redis, utwórz bazę w Upstash Console lub przez Vercel Marketplace i dodaj do .env.local dwie zmienne: UPSTASH_REDIS_REST_URL oraz UPSTASH_REDIS_REST_TOKEN. Następnie inicjalizuj klienta przez Redis.fromEnv() — biblioteka automatycznie odczyta zmienne środowiskowe. Klient działa zarówno w Server Components, jak i w Server Actions oraz Route Handlers.
Kiedy używać Upstash do sesji zamiast JWT?
Sesje Redis mają trzy kluczowe zalety nad JWT: można je natychmiast unieważnić (wystarczy usunąć klucz), nie mają ograniczenia rozmiaru (JWT ma limit ~8 KB), a dane sesji nigdy nie trafiają do klienta. JWT sprawdza się w systemach rozproszonych bez wspólnego backendu, ale dla standardowych aplikacji Next.js sesje Redis dają lepszą kontrolę.
Jak działa rate limiting z Upstash Ratelimit?
Biblioteka @upstash/ratelimit implementuje gotowe algorytmy (sliding window, fixed window, token bucket). Konfigurujesz limiter z Redisem i parametrami okna, a następnie wywołujesz ratelimit.limit(identifier) — zwraca obiekt { success, limit, remaining, reset }. Możesz użyć go w middleware Next.js dla wszystkich tras API lub bezpośrednio w Server Action.
Czy Upstash Redis nadaje się do cache'owania w Next.js App Router?
Tak, szczególnie gdy potrzebujesz cache współdzielonego między wieloma instancjami lub procesami. Do prostego cachowania pojedynczych requestów fetch() wystarczy wbudowany mechanizm Next.js (next: { revalidate: 60 }). Redis ma sens, gdy cache musi być spójny między wieloma równoległymi instancjami serwera lub gdy potrzebujesz jawnej invalidacji z zewnętrznego systemu (np. webhooka CMS, czyli Content Management System, to system do zarządzania treścią bez ręcznej edycji kodu.).
Jak liczyć wyświetlenia strony z Upstash Redis?
Użyj komendy redis.incr('pageviews:slug') — atomicznie zwiększa licznik i zwraca nową wartość. Uważaj jednak, żeby nie wywoływać inkrementacji bezpośrednio w render Server Component, bo komponent może renderować się więcej niż raz. Bezpieczniejsze podejście to osobny Route Handler lub Server Action wyzwalany jawnie po stronie klienta.
Ile kosztuje Upstash Redis i czy jest darmowy tier?
Upstash oferuje darmowy tier z podstawowymi limitami — wystarczającymi do projektów deweloperskich i małych aplikacji. Płatne plany rozliczane są per-request, więc koszt rośnie proporcjonalnie do ruchu. Przed wdrożeniem na produkcję warto oszacować liczbę operacji Redis (nie tylko transferu danych), bo rate limiting i cache mogą generować dużą ich liczbę. Aktualne limity i ceny zawsze sprawdzaj w cenniku Upstash.
Podsumowanie
Upstash Redis to szwajcarski scyzoryk dla aplikacji Next.js:
Sesje — szybkie, bezpieczne, z natychmiastowym unieważnianiem
Cache — przyspieszenie API i redukcja kosztów zewnętrznych usług
Rate limiting — ochrona przed nadużyciami
Liczniki — real-time analytics bez zewnętrznych narzędzi
Serverless model Upstash oznacza, że nie płacisz za idle time i nie musisz zarządzać infrastrukturą. Integracja z Vercel Marketplace sprawia, że setup zajmuje minuty.
Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.
Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.
Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.
Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?
Fachowe porównanie Astro.js i Next.js z perspektywy developera pracującego na co dzień w Next.js. Architektura, wydajność, SEO, DX, koszty i konkretne use case — z benchmarkami i przykładami kodu.
WordPress → Next.js — migracja treści, redirecty 301 i zachowanie pozycji SEO
Jak przenieść stronę z WordPress na Next.js bez utraty pozycji w Google? Eksport treści, mapowanie URL, redirecty 301, migracja obrazów i weryfikacja indeksacji.
Google Search Console + Next.js — indeksacja, błędy, performance i co z nimi robić
Jak korzystać z Google Search Console dla strony Next.js? Weryfikacja, sitemap, indeksacja, Core Web Vitals, crawl budget i najczęstsze problemy — praktyczny poradnik.