WordPress jako headless CMS i Next.js - konfiguracja projektu
Damian Wróblewski - lipiec 2021
Spis treści
O tym, czym jest headless CMS, co zyskasz wybierając WordPress'a w tej formie oraz kiedy nie warto wybierać tego rozwiązania, pisałem już w artykule opublikowanym na LinkedIn, który traktować można jako wprowadzenie do dzisiejszego tematu. W tym wpisie natomiast skonfigurujemy już konkretne rozwiązanie jakim będzie połączenie headless'owego WordPress'a z framework'iem Next.js. To właśnie Next będzie generował statyczne strony na podstawie informacji pobranych z WordPress'a.
Co będzie nam potrzebne?
- Instancja WordPress'a wraz z odpowiednimi wtyczkami umożliwiającymi komunikację za pomocą zapytań GraphQL,
- Odpowiednio skonfigurowany projekt Next.js.
Do dzieła!
Instalacja i konfiguracja WordPress'a jako headless CMS
Pierwszym krokiem będzie instalacja WordPress'a. Możesz to zrobić np. lokalnie za pomocą XAMPP (tutaj znajdziesz instrukcję) lub na serwerze obsługującym PHP i MySQL (instrukcja tutaj).
Jeśli WP jest już zainstalowany, możemy przejść do instalacji i konfiguracji wtyczek.
Instalacja wtyczek
Do komunikacji z headless'owym WP można wykorzystać RestAPI lub GraphQL. My skorzystamy z tego drugiego rozwiązania, które zapewnia w tym przypadku lepszą wydajność. A wydajność to przecież jeden z głównych powodów, dla których warto rozważyć wykorzystanie wersji headless WordPress'a.
Pierwszą wtyczką jaką musimy zainstalować jest WPGraphQL. Wtyczkę instalujemy poprzez przesłanie pliku zip:
- Pobieramy plik zip wtyczki z repozytorium,
- W panelu administratora WP przechodzimy do zakładki Wtyczki > Dodaj nową > Wyślij wtyczkę na serwer i przesyłamy plik zip z naszą wtyczką,
- Kiedy wtyczka zostanie przesłana, włączamy ją z poziomu zakładki Wtyczki > Zainstalowane wtyczki klikając "Włącz"
Kiedy wtyczka jest już uruchomiona, przechodzimy do ustawień. Najważniejszą pozycją na ten moment jest GraphQL Endpoint. To tutaj defiuniujemy endpoint, na który będziemy się komunikować z naszym API.
Już z pomocą samego WPGraphQL możemy się łączyć i pobierać treści z naszego WordPress'a. Jednak dopiero dodanie wtyczki Advanced Custom Fields pozwoli nam w pełni wykorzystać potencjał tego rozwiązania. ACF to popularny plugin, który umożliwia dodawanie do stron i wpisów dodatkowych pól z treścią.
By zainstalować wtyczkę:
- Przechodzimy do Wtyczki > Dodaj nową i wyszukujemy wtyczkę wpisując w pasku wyszukiwania "Advanced Custom Fields"
- Instalujemy i włączamy wtyczkę
By skorzystać z ACF w naszym projekcie oprócz samej wtyczki, będziemy musieli zainstalować jeszcze jeden plugin - WPGraphQL for Advanced Custom Fields. Instalujemy podobnie jak WPGraphQL - plik zip pobieramy z tego repozytorium.
Dodatkowe pola ACF
Uzbrojeni w wszelkie niezbędne narzędzia, możemy dodać na naszej stronie jakieś testowe treści i sprawdzić zwracane dane za pomocą wbudowanego w WPGraphQL narzędzia GraphiQL IDE. Spróbujemy dodać do strony głównej dodatkowe pola i zawrzeć w nich treść, która docelowo będzie wyświetlana w topbarze strony.
W celu utworzenia nowych pól, w panelu WordPress'a przechodzimy do Własne pola > Grupy pól i wybieramy "Dodaj nową". Naszą grupę pól nazywamy np. "Topbar" i w ustawieniach grupy pól określamy, że grupa ma być widoczna dla strony głównej:
Musimy również włączyć widoczność grupy dla GraphQL i nadać nazwę, którą posługować się będziemy w zapytaniach.
Teraz możemy przejść do dodawania pól. Dodamy dwa pola: Phone number oraz Email o typach odpowiednio Tekst i E-mail:
Czas na dodanie treści. Przechodzimy więc do edycji strony (w naszym przypadku to Strona główna) i dodajemy treść do pól:
Możemy także w edytorze dodać testową treść dla strony:
Zapytania GraphQL
Za pomocą narzędzia GraphiQL IDE możemy teraz sprawdzić czy nasze treści są dostępne, tworząc odpowiednie zapytanie GraphQL. GraphiQL to kreator, dzięki któremu w prosty sposób możemy tworzyć i testować zapytania GraphQL. Wystarczy, że w oknie Explorer po lewej stronie wybierzemy interesujące nas dane, a w środkowym oknie zobaczymy odpowiadające tym danym zapytanie. Dodatkowo klikając przycisk ► możemy przetestować zapytanie i w prawym oknie podejrzeć zwracane dane. W naszym przykładzie zbudujemy zapytanie, które zwróci nam tytuł strony, jej treść oraz informacje zawarte w utworzonej przez nas grupie pól topBar. Jako że interesują nas dane związane tylko ze Stroną główną, stosując filtr, wybierzemy tylko stronę o konkretnym ID. ID strony w WordPress sprawdzić możemy np. w URL kiedy edytujemy daną stronę.
/wp-admin/post.php?post=25&action=edit
W naszym przypadku ID to 25.
Sprawdźmy jak wyglądać będzie zatem całe zapytanie i co nam zwróci:
Jak widać nasz WordPress jest poprawnie skonfigurowany, więc możemy przejść do drugiej części, czyli utworzenia projektu Next.js.
Utworzenie i konfiguracja projektu Next.js
Tworzymy nowy projekt
W celu utworzenia nowego projektu wykorzystamy narzędzie create-next-app. W katalogu docelowym uruchamiamy:
npx create-next-app headless-wp-website
Jeśli chcemy korzystać z Typescript'a dodajemy flagę --ts lub --typescript:
npx create-next-app headless-wp-website --ts
Po utworzeniu, jeśli wszystko przebiegło pomyślnie możemy przejść do katalogu projektu:
cd headless-wp-website
I uruchomić nasz projekt w wersji developerskiej:
npm run dev
Kiedy projekt się zbuduje, możesz go podejrzeć uruchamiając w przeglądarce http://localhost:3000
Jeśli wszystko przebiegło pomyślnie, możemy przejść do konfiguracji projektu.
Konfiguracja projektu Next.js
Na początek dodajmy nową zmienną środowiskową, która zawierać będzie adres naszego endpoint'a. W tym celu w głównym katalogu projektu tworzymy plik .env, w którym dodajemy:
WP_API_URL=http://naszadomena.pl/graphql
Więcej na temat zmiennych środowiskowych w Next możesz przeczytać tutaj.
Kolejnym krokiem będzie utworzenie pliku api.js, w którym znajdować się będą wszelkie funkcje pobierające dane oraz zapytania GraphQL. Plik dla porządku umieścimy w katalogu /lib/api.js.
Na początku pliku dodajemy utworzoną wcześniej zmienną środowiskową:
const API_URL = process.env.WP_API_URL;
Następnie dodajemy główną funkcję pobierającą dane:
1const API_URL = process.env.WP_API_URL;24async function fetchAPI (query, { variables } = {}) {5 const headers = { 'Content-Type': 'application/json' };6 const res = await fetch(API_URL, {7 method: 'POST',8 headers,9 body: JSON.stringify({ query, variables })10 });1112 const json = await res.json();13 if (json.errors) {14 console.log(json.errors);15 console.log('error details', query, variables);16 throw new Error('Failed to fetch API');17 }18 return json.data;19}
I wykorzystujemy ją wstawiając jako argument query nasze zapytanie skopiowane z GraphiQL:
1const API_URL = process.env.WP_API_URL;23async function fetchAPI (query, { variables } = {}) {4 const headers = { 'Content-Type': 'application/json' };5 const res = await fetch(API_URL, {6 method: 'POST',7 headers,8 body: JSON.stringify({ query, variables })9 });1011 const json = await res.json();12 if (json.errors) {13 console.log(json.errors);14 console.log('error details', query, variables);15 throw new Error('Failed to fetch API');16 }17 return json.data;18}1921export async function getHomePageData() {22 const data = await fetchAPI(23 `24 {25 page(id: "25", idType: DATABASE_ID) {26 topBar {27 email28 phoneNumber29 }30 }31 }32 `33 );34 return data?.page;35}
Z pomocą zdefiniowanych właśnie funkcji możemy pobrać dane, a następnie przekazać je do odpowiedniego komponentu w pliku pages/index.js, który odpowiada za stronę główną naszej aplikacji. Najpierw utworzymy prosty komponent TopBar który będzie wyświetlał nasze dane. Tworzymy plik components/TopBar.js:
1import styles from '../../../styles/TopBar.module.scss';23function TopBar({ phone, email }) {4 return (5 <div className={styles.Topbar}>6 <div className={styles.Container}>7 <div className={styles.InfoElement}>{email}</div>8 <div className={styles.InfoElement}>{phone}</div>9 </div>10 </div>11 );12}1314export default TopBar;
Dodamy jeszcze plik z podstawowymi stylami dla komoponentu:
1.Topbar {2 width: 100vw;3 background-color: #000;4 margin: auto;5 .Container {6 max-width: 1200px;7 display: flex;8 flex-direction: row;9 justify-content: flex-end;10 }11 .InfoElement {12 padding-right: 20px;13 }14}
I możemy teraz przejść do pliku pages/index.js. Domyślną zawartość pliku, dodaną podczas tworzenia projektu możesz usunąć.
Najpierw zaimportujemy funkcję pobierającą dane oraz komponent TopBar:
import { getHomePageData } from '../lib/api';import TopBar from '../components/organisms/TopBar/TopBar';
Następnie utworzymy komponent naszej strony:
1import { getHomePageData } from '../lib/api';2import TopBar from '../components/organisms/TopBar/TopBar';35function IndexPage() {67 return (8 <>9 <TopBar phone={} email={} />10 </>11 );12}1314export default IndexPage;
Kolejnym krokiem będzie utworzenie funkcji asynchronicznej getStaticProps. Jest to specjalna funkcja, którą Next.js uruchamia w trakcie budowania aplikacji. Pobrane za jej pomocą dane mogą być następnie przekazane jako propsy do komponentu naszej strony. W ten sposób Next.js może na etapie budowania aplikacji wygenerować wszystkie strony statyczne na podstawie pobranych z zewnątrz danych. Więcej na temat pobierania danych w Next możesz przeczytać w dokumentacji.
1import { getHomePageData } from '../lib/api';2import TopBar from '../components/organisms/TopBar/TopBar';34function IndexPage() {56 return (7 <>8 <TopBar phone={} email={} />9 </>10 );11}1213export default IndexPage;1416export const getStaticProps = async () => {17 const homePageData = await getHomePageData();18 return {19 props: {20 homePageData21 }22 };23};
Zwrócone przez funkcję getStaticProps() propsy przekażemy teraz do komponentu strony, dokonamy destrukturyzacji przypisując do zmiennej topBar odpowiednie dane i następnie przekażemy te dane do komponentu TopBar.
1import { getHomePageData } from '../lib/api';2import TopBar from '../components/organisms/TopBar/TopBar';35function IndexPage({ homePageData }) {6 const { topBar } = homePageData;78 return (9 <>10 <TopBar phone={topBar.phoneNumber} email={topBar.email} />11 </>12 );13}1516export default IndexPage;1718export const getStaticProps = async () => {19 const homePageData = await getHomePageData();20 return {21 props: {22 homePageData23 }24 };25};
Możemy teraz sprawdzić czy wszystko poszło zgodnie z planem uruchamiając:
npm run dev
Gotowe! Nasz komponent poprawnie wyświetla informacje pobrane z naszej headless'owej instancji WordPress'a.
Takie połączenie daje wysoką wydajność i bezpieczeństwo stron statycznych oraz przyjazne i dobrze znane środowisko do tworzenia i edycji treści.
Bądź ze mną w kontakcie na Twitterze lub LinkedIn, jeśli interesuje Cię świat aplikacji webowych 😉
Dołącz do dyskusji