Menu
Szybki wybór
Hosting Domeny VPS SSL Kalkulator Porównania FAQ
Aktywne kody
Wszystkie kody rabatowe

Headless WordPress z WPGraphQL i Next.js 14

Opublikowano: 10 kwietnia 2026 · Kategoria: WordPress

WordPress rządzi rynkiem CMS z dobrych powodów: intuicyjny edytor Gutenberg, tysiące pluginów, znany przez redaktorów. Ale klasyczny motyw WordPress ma ograniczenia wydajnościowe i technologiczne. Rozwiązanie: headless architecture — WordPress tylko jako backend API, a nowoczesny Next.js jako frontend. Redaktorzy pracują w znajomym panelu WP, deweloperzy piszą React i TypeScript, a użytkownicy dostają błyskawicznie szybką stronę generowaną statycznie przez ISR.

REST API vs GraphQL — porównanie

Kryterium WordPress REST API WPGraphQL
Endpoint /wp-json/wp/v2/posts /graphql (jeden endpoint)
Selektywne pola Ograniczone (_fields param) Pelna kontrola (tylko to co prosisz)
Powiązane dane Wiele requestów (posts + authors + tags) Jeden request (wszystko naraz)
Typowanie Brak (JSON bez schematu) Silnie typowany schemat GraphQL
Custom Post Types Automatyczne, ograniczone register_graphql_object_type()
Cache Łatwy (URL-based) Persisted Queries lub APQ
Krzywa uczenia Niska Średnia (GraphQL query language)

Instalacja WPGraphQL

WPGraphQL to plugin do WordPressa. Instalujesz go jak każdy inny plugin — przez panel admina lub WP-CLI.

# Przez WP-CLI (SSH na hosting/VPS)
wp plugin install wp-graphql --activate

# Lub przez panel WP: Pluginy > Dodaj nowy > szukaj "WPGraphQL" > Zainstaluj

# Sprawdz ze GraphQL endpoint dziala
curl -X POST https://twojadomena.pl/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ posts { nodes { id title } } }"}'

# GraphiQL IDE (w przegladarce — panel WP)
# WP Admin > GraphQL > GraphiQL IDE
# Interaktywne testowanie zapytan

# Przykładowe zapytanie GraphQL — pobierz posty z autorami i tagami
# query {
#   posts(first: 10) {
#     nodes {
#       id
#       title
#       slug
#       date
#       excerpt
#       content
#       featuredImage {
#         node {
#           sourceUrl
#           altText
#         }
#       }
#       author {
#         node {
#           name
#           avatar {
#             url
#           }
#         }
#       }
#       tags {
#         nodes {
#           name
#           slug
#         }
#       }
#     }
#   }
# }

Next.js 14 — projekt i pobieranie danych

# Utwórz projekt Next.js
npx create-next-app@latest my-headless-wp \
  --typescript --tailwind --eslint --app
cd my-headless-wp

# Zainstaluj klienta GraphQL
npm install graphql-request graphql

# .env.local
# WORDPRESS_API_URL=https://twojadomena.pl/graphql
# WORDPRESS_PREVIEW_SECRET=twoj-tajny-token-preview

# lib/graphql.ts — klient zapytan
# import { GraphQLClient } from 'graphql-request';
#
# export const client = new GraphQLClient(
#   process.env.WORDPRESS_API_URL!
# );
#
# export async function getAllPosts() {
#   const query = `
#     query AllPosts {
#       posts(first: 20, where: { status: PUBLISH }) {
#         nodes {
#           id
#           slug
#           title
#           date
#           excerpt(format: RENDERED)
#           featuredImage {
#             node { sourceUrl altText }
#           }
#         }
#       }
#     }
#   `;
#   const data = await client.request(query);
#   return data.posts.nodes;
# }
#
# export async function getPostBySlug(slug: string) {
#   const query = `
#     query PostBySlug($slug: String!) {
#       postBy(slug: $slug) {
#         id title content date
#         author { node { name } }
#         tags { nodes { name slug } }
#       }
#     }
#   `;
#   const data = await client.request(query, { slug });
#   return data.postBy;
# }

App Router — strony z ISR

Next.js App Router pozwala na Incremental Static Regeneration przez pole revalidate. Strony są wstępnie generowane przy buildzie i odświeżane w tle co określony czas:

# app/page.tsx — strona glowna z lista postow (ISR co 60s)
# import { getAllPosts } from '@/lib/graphql';
#
# export const revalidate = 60;  // ISR: odswiezaj co 60 sekund
#
# export default async function HomePage() {
#   const posts = await getAllPosts();
#
#   return (
#     <main>
#       <h1>Blog</h1>
#       <ul>
#         {posts.map((post) => (
#           <li key={post.id}>
#             <a href={`/posts/${post.slug}`}>{post.title}</a>
#           </li>
#         ))}
#       </ul>
#     </main>
#   );
# }

# app/posts/[slug]/page.tsx — dynamiczna strona posta z ISR
# import { getPostBySlug, getAllPosts } from '@/lib/graphql';
# import { notFound } from 'next/navigation';
#
# export const revalidate = 3600;  // ISR: co godzine
#
# // Generuj sciezki statycznie przy buildzie
# export async function generateStaticParams() {
#   const posts = await getAllPosts();
#   return posts.map((post) => ({ slug: post.slug }));
# }
#
# export default async function PostPage({ params }) {
#   const post = await getPostBySlug(params.slug);
#   if (!post) notFound();
#
#   // Renderowanie treści z WordPress — pamiętaj o sanitizacji
#   // (wp_kses_post() po stronie WP czyści HTML przed zapisem do bazy)
#   return (
#     <article>
#       <h1>{post.title}</h1>
#       <time>{new Date(post.date).toLocaleDateString('pl-PL')}</time>
#       <div class="post-content" />  {/* wstaw tresc przez set innerHTML po sanitizacji */}
#     </article>
#   );
# }

Webhook — natychmiastowe ISR revalidation

ISR odświeża strony co określony czas. Jeśli chcesz żeby strona odświeżyła się natychmiast po publikacji posta w WordPress, użyj mechanizmu On-Demand Revalidation:

# app/api/revalidate/route.ts — endpoint do rewalidacji
# import { revalidatePath } from 'next/cache';
# import { NextRequest } from 'next/server';
#
# export async function POST(req: NextRequest) {
#   const secret = req.nextUrl.searchParams.get('secret');
#   if (secret !== process.env.WORDPRESS_PREVIEW_SECRET) {
#     return Response.json({ message: 'Invalid secret' }, { status: 401 });
#   }
#
#   const body = await req.json();
#   const slug = body?.post?.post_name;
#
#   if (slug) {
#     revalidatePath(`/posts/${slug}`);
#   }
#   revalidatePath('/');  // strona glowna
#
#   return Response.json({ revalidated: true, slug });
# }

# WordPress — dodaj w functions.php do motywu lub pluginie
# function send_nextjs_revalidation_webhook($post_id) {
#   $post = get_post($post_id);
#   $payload = json_encode(array('post' => array('post_name' => $post->post_name)));
#   wp_remote_post(
#     'https://twoj-nextjs.pl/api/revalidate?secret=TWOJ_SECRET',
#     array(
#       'body'    => $payload,
#       'headers' => array('Content-Type' => 'application/json')
#     )
#   );
# }
# add_action('publish_post', 'send_nextjs_revalidation_webhook');

Deployment na VPS — Next.js z PM2

# Na VPS — instalacja Node.js LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install nodejs -y
npm install -g pm2

# Build i uruchomienie
cd /var/www/my-headless-wp
npm install
npm run build

# PM2 ekosystem (ecosystem.config.js)
# module.exports = {
#   apps: [{
#     name: 'nextjs-blog',
#     script: 'node_modules/.bin/next',
#     args: 'start',
#     env: {
#       NODE_ENV: 'production',
#       PORT: 3000,
#       WORDPRESS_API_URL: 'https://wp.example.com/graphql'
#     }
#   }]
# };

pm2 start ecosystem.config.js
pm2 save
pm2 startup   # autostart po restarcie serwera

# Nginx reverse proxy do Next.js
# server {
#     listen 443 ssl;
#     server_name blog.example.com;
#     location / {
#         proxy_pass http://localhost:3000;
#         proxy_http_version 1.1;
#         proxy_set_header Upgrade $http_upgrade;
#         proxy_set_header Connection 'upgrade';
#         proxy_set_header Host $host;
#         proxy_cache_bypass $http_upgrade;
#     }
# }

# Deploy bez downtime
git pull
npm ci
npm run build
pm2 reload nextjs-blog   # zero-downtime reload

Najczęstsze pytania

Czym jest headless WordPress i kiedy warto go stosować? +
Headless WordPress oznacza użycie WordPressa wyłącznie jako CMS (zarządzanie treścią, panel admina, baza danych), podczas gdy frontend (to co widzi użytkownik) jest osobną aplikacją (Next.js, Astro, Gatsby, Nuxt). WordPress udostępnia treść przez API (REST lub GraphQL). Warto stosować gdy chcesz szybkości nowoczesnego frontendu (React, SSR, ISR) z wygodą redakcyjną WordPress, gdy masz zespół frontendowy preferujący React, lub gdy potrzebujesz serwować treść na wielu platformach (web, mobile app, newsletter).
Dlaczego WPGraphQL zamiast WordPress REST API? +
REST API WordPress pobiera z góry określony zestaw pól niezależnie od potrzeb — często za dużo danych (over-fetching) lub za mało (under-fetching, wymaga wielu requestów). GraphQL pozwala zapytać dokładnie o te pola, których potrzebujesz. Jeden request GraphQL może zastąpić 5-10 requestów REST. WPGraphQL jest oficjalnie wspieranym pluginem, ma bogatą dokumentację i jest powszechnie stosowany w ekosystemie headless WordPress (Gatsby, Next.js).
Czym jest ISR (Incremental Static Regeneration) w Next.js? +
ISR to funkcja Next.js łącząca zalety generowania statycznego (szybkość, bez serwera dla każdego żądania) z możliwością aktualizacji treści. Definiujesz revalidate: 60 — strona jest generowana statycznie, ale raz na 60 sekund Next.js regeneruje ją w tle jeśli były żądania. Stara wersja jest serwowana do chwili gotowości nowej. Idealne dla bloga WordPress: artykuły są statyczne i szybkie, ale nowe treści pojawiają się bez pełnego rebuild.
Gdzie hostować headless WordPress — WP osobno od Next.js? +
Tak, WP i Next.js to dwie oddzielne aplikacje. WordPress (PHP+MySQL) najlepiej na klasycznym hostingu WordPress lub VPS z PHP — nie potrzebuje dużo zasobów, bo obsługuje tylko requesty API (brak publicznego frontendu). Next.js (Node.js) wymaga VPS lub platformy obsługującej Node (Vercel, Netlify, self-hosted). Tanie rozwiązanie: WordPress na shared hostingu (LH.pl, CyberFolks) + Next.js na VPS lub Vercel free tier.

Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.