GA4 Data API w Next.js – budujemy własny dashboard analityczny
GA4 Data API w Next.js bez skrótów myślowych: service account, cache, limity, bezpieczeństwo i budowa własnego dashboardu na danych z Analytics.
Maciej Sala
Founder StriveLab
8 min czytaniaOpublikowano 31 sierpnia 2025 (Aktualizacja 27 maja 2026)
Kiedy warto budować własny dashboard GA4?
to narzędzie zaprojektowane dla analityków, a nie dla
klientów — interfejs jest gęsty, terminologia mocno techniczna, a dostęp wymaga
konta Google z uprawnieniami do właściwości. Dla większości klientów to bariera
zbyt wysoka, żeby samodzielnie z niego korzystać.
Custom dashboard w Next.js rozwiązuje kilka konkretnych problemów. Klient widzi wybrane metryki w kontekście swojego biznesu, bez konieczności logowania do GA4 i rozumienia, czym jest "sessionMedium". Możesz połączyć dane GA4 z innymi źródłami — zamówieniami z -a, leadami z CRM-a, przychodami z własnej bazy — i pokazać je razem w jednym widoku. Możesz również zbudować publiczny lub wewnętrzny panel z wbudowaną analityką, który jest integralną częścią produktu, a nie zewnętrznym narzędziem.
Warto jednak wiedzieć, kiedy to podejście nie ma sensu: jeśli potrzebujesz pełnej elastyczności eksploracji danych, zaawansowanych segmentów i porównań, GA4 (lub Looker Studio z konektorem GA4) będzie szybsze i tańsze w utrzymaniu. Custom dashboard opłaca się wtedy, gdy wiesz z góry, jakie metryki chcesz pokazywać i komu.
Krok 1: autoryzacja GA4 Data API przez Service Account
GA4 Data wymaga autoryzacji OAuth2. Dla aplikacji server-side właściwym wyborem jest Service Account — konto techniczne bez hasła, identyfikowane kluczem JSON. Nie używa interaktywnego logowania, nie wymaga odświeżania tokenów przez użytkownika i daje się bezpiecznie trzymać w zmiennych środowiskowych serwera.
Alternatywą jest OAuth2 z tokenami użytkownika, ale to ma sens tylko wtedy, gdy chcesz, żeby każdy użytkownik widział dane ze swojej własnej właściwości GA4. Przy dashboardzie dla jednej właściwości Service Account jest prostszy i bezpieczniejszy.
Ważna kwestia uprawnień: Service Account potrzebuje roli Viewer na poziomie właściwości GA4 — nie na poziomie konta Google Analytics ani projektu Google Cloud. Dodajesz go w GA4: Admin → Property Access Management. Rola Viewer wystarczy do wszystkich operacji odczytu. Nie nadawaj Editor ani Admin — zasada minimalnych uprawnień obowiązuje tak samo jak przy każdym innym kluczu API.
Utworzenie Service Account w Google Cloud
Google Cloud Console → APIs & Services → Credentials,
Create Credentials → Service Account,
Pobierz klucz JSON,
W GA4: Admin → Property Access Management → dodaj email Service Account z rolą Viewer.
Environment variables dla GA4 Data API
Klucz JSON z Google Cloud zawiera kilkanaście pól, ale do autoryzacji potrzebujesz tylko dwóch: client_email i private_key. Reszta (project_id, token_uri itp.) jest obsługiwana przez bibliotekę automatycznie.
Zwróć uwagę na private_key — w pliku JSON Google zapisuje znaki nowej linii jako \n (literalnie backslash-n). W zmiennej środowiskowej też je tak trzymamy, a w kodzie zamieniamy na prawdziwe znaki nowej linii przez .replace(/\\n/g, '\n'). Bez tej zamiany autoryzacja się wysypie z enigmatycznym błędem invalid_grant.
Instalacja klienta GA4 Data API
Code
npm install @google-analytics/data
Krok 2: GA4 Client po stronie serwera
BetaAnalyticsDataClient to główna klasa z pakietu @google-analytics/data i pomimo nazwy z "Beta" jest to stabilna, produkcyjna wersja klienta - Google po prostu nie zmienił nazwy po wyjściu z bety. Tworzymy go jako singleton, żeby nie inicjalizować połączenia i autoryzacji przy każdym requescie — to ważne szczególnie w Server Components, które mogą być wywoływane wielokrotnie w trakcie jednego page load.
Code
// lib/ga4/client.tsimport { BetaAnalyticsDataClient } from '@google-analytics/data'let client: BetaAnalyticsDataClient | null = nullexport function getGA4Client(): BetaAnalyticsDataClient { if (!client) { client = new BetaAnalyticsDataClient({ credentials: { client_email: process.env.GA4_CLIENT_EMAIL, private_key: process.env.GA4_PRIVATE_KEY?.replace(/\\n/g, '\n'), }, }) } return client}export const GA4_PROPERTY_ID = process.env.GA4_PROPERTY_ID!
Krok 3: funkcje do pobierania danych z GA4
GA4 Data API operuje na trzech pojęciach: dimensions ( — co segmentujesz, np. pagePath, sessionSource, date), metrics ( — co mierzysz, np. totalUsers, sessions, bounceRate) i dateRanges (zakres dat). Każde zapytanie to kombinacja tych trzech elementów.
Ważna gotcha: nie wszystkie kombinacje wymiarów i metryk są kompatybilne. GA4 zwróci błąd INCOMPATIBLE_DIMENSIONS_METRICS, jeśli zestawisz wymiary i metryki, których Google nie może obliczyć razem. Przed budową produkcyjnych zapytań warto je przetestować w GA4 Dimensions & Metrics Explorer.
Daty w GA4 Data API mają dwa formaty. Możesz podawać "30daysAgo", "7daysAgo", "yesterday", "today" — wygodne dla dashboardów z presetami — albo konkretne daty "2026-01-01" w formacie YYYY-MM-DD. W odpowiedzi GA4 zwraca daty jako "20260101" (bez separatorów) — stąd helper formatGA4Date poniżej.
GA4 Data API ma limity i kwoty tokenowe, więc cache nie jest tu optymalizacją "na potem", tylko częścią poprawnej architektury od pierwszego dnia.
Jak działają limity GA4 Data API? Google używa modelu tokenowego: każde zapytanie kosztuje określoną liczbę tokenów z puli przypisanej do właściwości. Prosta kwerenda kosztuje mniej niż złożona z wieloma wymiarami, dużym limit i długim zakresem dat. Pula odnawia się w ciągu doby, ale jeśli ją wyczerpiesz (np. dashboard bez cache'u odwiedzony przez wielu użytkowników jednocześnie), API zwraca 429 Resource Exhausted. Bez cache'u jeden popularny dashboard może wyczerpać dzienny limit właściwości w ciągu minut.
vs z revalidate — to dwie różne strategie cachowania, które warto rozumieć:
Route Handler (/api/analytics/...) ma sens, gdy chcesz dynamicznie zmieniać zakres dat po stronie klienta (Date Range Picker) i pobierać dane bez przeładowania strony. Cache ustawiasz przez nagłówki HTTP.
Server Component z export const revalidate = 300 jest prostszy, gdy zakres dat jest stały lub ustawiany przez URL params. Next.js sam zarządza cachowaniem — dane są pobierane raz przy pierwszym requescie i odświeżane co 5 minut w tle przez .
Code
// app/api/analytics/overview/route.tsimport { NextRequest, NextResponse } from 'next/server'import { getOverview } from '@/lib/ga4/queries'export async function GET(request: NextRequest) { const { searchParams } = request.nextUrl const startDate = searchParams.get('startDate') ?? '30daysAgo' const endDate = searchParams.get('endDate') ?? 'today' try { const data = await getOverview(startDate, endDate) return NextResponse.json(data, { headers: { // Cache na 5 minut – dane analityczne nie muszą być real-time 'Cache-Control': 's-maxage=300, stale-while-revalidate=600', }, }) } catch (error) { console.error('GA4 API error:', error) return NextResponse.json( { error: 'Failed to fetch analytics' }, { status: 500 }, ) }}
Krok 5: frontend dashboardu i wizualizacja w Recharts
Recharts to biblioteka oparta na i D3, zbudowana jako natywne komponenty React. Jej największa zaleta w kontekście Next.js to, że działa wyłącznie po stronie klienta — i to jest jednocześnie jedyne ograniczenie, o którym warto pamiętać.
W App Router oznacza to: wykresy muszą żyć w Client Components ("use client"). Dane pobierasz server-side (Server Component lub API Route), a do komponentu przekazujesz je przez props jako zwykłe tablice obiektów. To podział, który w przykładzie poniżej realizuje para DashboardPage (server) + DashboardClient (client).
ResponsiveContainer opakowuje każdy wykres i sprawia, że dopasowuje się do szerokości kontenera — bez tego Recharts renderuje wykres o sztywnych wymiarach, który się nie skaluje na ekranach mobilnych.
useSearchParams() w Next.js App Router wymaga opakowania komponentu w <Suspense>, ponieważ inaczej Next.js wymusi dynamiczne renderowanie całej strony nadrzędnej lub rzuci błąd podczas buildu. Granica Suspense izoluje dynamiczną część (Date Range Picker czytający URL) od reszty strony, która może pozostać statyczna i być serwowana z cache.
Porównanie z poprzednim okresem to jedna z najczęstszych potrzeb na dashboardach klienckich. Chodzi o znalezienie prostej odpowiedzi: "czy to lepiej czy gorzej niż miesiąc temu?". Da się to zrealizować dwoma osobnymi requestami, ale GA4 Data API obsługuje tablicę dateRanges z dwoma zakresami w jednym zapytaniu - co jest bardziej optymalne. Jest i tańsze tokenowo, i wygodniejsze w implementacji.
Kluczowym detalem jest, że przy wielu dateRanges dodajemy wymiar dateRange, bo to on pozwala później rozróżnić, który wiersz należy do okresu current, a który do previous.
Monitorowanie limitów GA4 Data API przez PropertyQuota
Każda odpowiedź z GA4 Data API zawiera pole propertyQuota z informacją o bieżącym zużyciu tokenów. Większość tutoriali to pomija — a to jeden z bardziej praktycznych mechanizmów, jeśli budujesz dashboard produkcyjny.
Pole returnPropertyQuota: true w każdym zapytaniu daje aktualny stan limitów po wykonaniu tej kwerendy. Możesz logować te wartości (np. do konsoli serwera albo własnego systemu monitoringu) i reagować zanim GA4 zwróci 429. Przykładowo: jeśli tokensPerDay.remaining spada poniżej 20%, możesz zwiększyć TTL cache'a dynamicznie albo wysłać alerta.
Graceful degradation, gdy GA4 jest niedostępny
GA4 Data API zdarza się być chwilowo niedostępne lub wolne. Bez obsługi błędów cały dashboard pada z 500. Dobra architektura zakłada, że dane analityczne to funkcja wspierająca, nie krytyczna — i odpowiednio obsługuje awarię.
Wzorzec jest prosty, ponieważ każda funkcja zwraca { data, error } zamiast rzucać wyjątek. W Server Component można wtedy pokazać z informacją o niedostępności danych zamiast białej strony:
Promise.all z safe* funkcjami zapewnia, że błąd jednego endpointa GA4 nie blokuje renderowania pozostałych części dashboardu. Użytkownik widzi to, co jest dostępne, z jasnym komunikatem o problemie.
Bezpieczeństwo i rate limits w GA4 Data API
Nigdy nie eksponuj Service Account credentials na frontendzie. Wszystkie zapytania do GA4 Data API muszą iść przez server-side (API Routes, Server Components, Server Actions).
Rate limits: GA4 Data API używa kwot tokenowych zależnych od typu zapytań i właściwości. Cache jest częścią poprawnej architektury — bez niego dashboard z kilkoma widżetami i kilkudziesięcioma równoczesnymi użytkownikami wyczerpie dobowy limit szybciej, niż się spodziewasz.
Autoryzacja dashboardu: Zabezpiecz /dashboard lub server-side auth check. Dane z GA4 — nawet agregowane — mogą ujawniać wrażliwe informacje o ruchu i przychodach.
Code
// middleware.tsimport { NextResponse } from 'next/server'import type { NextRequest } from 'next/server'export function middleware(request: NextRequest) { const token = request.cookies.get('auth-token') // Sprawdź istnienie i podstawowy format tokenu. // W produkcji zastąp tę weryfikację sprawdzeniem podpisu JWT // (np. jose.jwtVerify) lub wywołaniem własnego endpointu sesji. if (!token?.value) { return NextResponse.redirect(new URL('/login', request.url)) }}// Middleware uruchamia się TYLKO dla ścieżek /analytics-dashboard/*.// Bez tego konfiguratora Next.js odpala middleware na każdym żądaniu// (statyki, fonty, API), co jest zbędnym kosztem.export const config = { matcher: ['/analytics-dashboard/:path*'],}
Kampanie, landing page, tracking konwersji, GA4 i GTM w jednym procesie.
GA4 Data API najlepiej nadaje się do raportów, agregatów i trendów. Do realtime lub surowego event streamu zwykle potrzebne są inne mechanizmy.
Gdzie trzymać klucze Service Account w Next.js?
Klucze powinny być używane wyłącznie po stronie serwera, w zmiennych środowiskowych lub bezpiecznym sekrecie. Nie wolno wysyłać ich do klienta.
Dlaczego cache jest ważny przy GA4 Data API?
API ma limity i koszt zapytań tokenowych. Cache ogranicza liczbę requestów, przyspiesza dashboard i stabilizuje działanie przy większej liczbie użytkowników.
Czy mogę odpytywać GA4 Data API bezpośrednio z frontendu?
Nie. Klucze Service Account i dostęp do właściwości muszą zostać po stronie serwera. Frontend powinien rozmawiać wyłącznie z Twoim własnym backendem, Route Handlerem albo Server Componentem.
Czy Service Account potrzebuje roli Editor?
Zwykle nie, a do samych odczytów wystarczą minimalne uprawnienia na poziomie właściwości, najczęściej Viewer albo inna rola, która pozwala pobierać potrzebne raporty. Nadawanie zbyt szerokich uprawnień bywa niebezpieczne i nie jest też optymalne do działań operacyjnych.
Czy `keyEvents` to to samo co dawne `conversions`?
W praktyce to aktualna nazwa i model w GA4, a jeśli pracujesz na nowszej dokumentacji, raportach i Data API, myśl raczej kategorią key events. To ważne także dlatego, że starsze nazewnictwo łatwo miesza się z Google Ads i klasycznym „conversion tracking”.
Jak często cache'ować taki dashboard?
Najczęściej 10-15 minut to dobry punkt startowy, ponieważ dane analityczne rzadko wymagają twardego real-time, a agresywne cache'owanie oszczędza kwoty API i stabilizuje działanie dashboardu.
Jak zabezpieczyć dashboard przed nieautoryzowanym dostępem?
Zabezpiecz ścieżkę /dashboard middlewarem Next.js z weryfikacją sesji lub tokenu przed renderowaniem strony, ponieważ żadne dane z GA4 nie powinny być dostępne publicznie. Mam tu na myśli zarówno dane agregowane jak i surowe raporty mogą ujawniać bardzo wrażliwe informacje o biznesie.
Jakie metryki i wymiary są dostępne w GA4 Data API?
GA4 Data API udostępnia setki wymiarów i metryk, m.in. totalUsers, sessions, screenPageViews, bounceRate, averageSessionDuration oraz wymiary jak pagePath, sessionSource, deviceCategory. Pełna lista dostępna jest w dokumentacji Google Analytics Data API schema. Nie wszystkie kombinacje są kompatybilne — warto weryfikować zapytania testowo.
Czy GA4 Data API nadaje się do raportowania w czasie rzeczywistym?
Dane w standardowych raportach Data API mają opóźnienie rzędu kilku minut do kilku godzin, a do wyświetlania metryk bliskich real-time istnieje osobne Realtime API w GA4, które zwraca dane z ostatnich 30 minut, ale ma inne możliwości i ograniczenia niż główne Data API.
O autorze
Maciej Sala
Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.
Integracja Google Analytics 4 w Next.js App Router to coś więcej niż wklejenie snippetu w , ponieważ nawigacja client-side, React Server Components, Consent Mode v2 i wybór między ręcznym gtag.js , a @next/third-parties sprawiają, że łatwo wprowadzić analitykę pozornie poprawną, ale w praktyce generującą zdublowane page views albo niepełne dane - nawiasem mówiąc, obie te sytuacje zdarzyły mi się w przeszłości. Ten artykuł łączy setup GA4 z realnymi niuansami App Router i podstawami planowania zdarzeń.
Maciej Sala
Founder StriveLab
Jak wdrożyć remarketing Google Ads w aplikacji React? Dynamiczny remarketing ecommerce, listy odbiorców na podstawie zachowań użytkowników i integracja z listami odbiorców w GA4.
Maciej Sala
Founder StriveLab
Kompletny przewodnik po implementacji śledzenia konwersji Google Ads w Next.js. Od podstawowego gtag, przez enhanced conversions, po server-side tracking z Google Ads API.