Day9 在 Next.js 安裝 apollo-graphql,串接 WordPress GraphQL API(上)

Published on
Currently displaying Chinese version content. This article doesn't have a English version yet. Please stay tuned!

上一篇文章我們成功在 WordPress 安裝 WPGraphQL plugin,啟動了 GraphQL API,現在我們要來在 Next.js 部落格前端串接它,抓取文章列表資料,並呈現在首頁。

參照 Next.js 官方範例

這部分實作我們會參照 Next.js 官方這 2 套 sample code:

  1. cms-wordpress
  2. with-apollo

第一個 cms-wordpress 是 demo 如何在 Next.js 串接 WordPress GraphQL API,裡面使用 fetch function 呼叫 GraphQL API 抓取文章資料,用來顯示首頁的文章列表、及文章細節頁的文章內容。我們可以參考這範例裡用到哪些 GraphQL 欄位,我們也將用差不多的欄位來實作各頁面。

而因為第一個範例是使用單純 fetch 函式來呼叫 GraphQL API,功能上稍嫌不足,後續若要加入更多判斷或功能,像是 pagination 分頁功能(一頁顯示 10 篇文章之類)的話,需要自己實作重造輪子,所以這部分我們會參考第二個 with-apollo 範例,改安裝 apollo-client 來執行 GraphQL API call,裡面就幫我們處理了很多常見功能,像是 loading state、error state、client-side cache、fetch more function 等等,讓我們能用更多元的方式使用 GraphQL API。

實作:安裝 apollo client

這部分我們參照 with-apollo 範例來安裝,主要安裝 @apollo/clientgraphql 這兩個套件當作 GraphQL client,並且額外安裝 deepmergelodash 來處理資料

yarn add @apollo/client graphql deepmerge lodash

接著新建 /lib/apolloClient.js 檔案,它提供了 useApollo function,讓我們等等能在整個 Next.js 專案套上 ApolloClient:

// Mainly follow this example // https://github.com/vercel/next.js/tree/canary/examples/with-apollo import { useMemo } from 'react' import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' import { concatPagination } from '@apollo/client/utilities' import merge from 'deepmerge' import isEqual from 'lodash/isEqual' import { NEXT_PUBLIC_GRAPHQL_ENDPOINT } from '../constants/envValues' export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__' let apolloClient function createApolloClient() { return new ApolloClient({ ssrMode: typeof window === 'undefined', link: new HttpLink({ uri: NEXT_PUBLIC_GRAPHQL_ENDPOINT, // Server URL (must be absolute) credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` }), cache: new InMemoryCache({ typePolicies: { Query: { fields: { posts: concatPagination(), }, }, }, }), }) } export function initializeApollo(initialState = null) { const _apolloClient = apolloClient ?? createApolloClient() // If your page has Next.js data fetching methods that use Apollo Client, the initial state // gets hydrated here if (initialState) { // Get existing cache, loaded during client side data fetching const existingCache = _apolloClient.extract() // Merge the existing cache into data passed from getStaticProps/getServerSideProps const data = merge(initialState, existingCache, { // combine arrays using object equality (like in sets) arrayMerge: (destinationArray, sourceArray) => [ ...sourceArray, ...destinationArray.filter((d) => sourceArray.every((s) => !isEqual(d, s))), ], }) // Restore the cache with the merged data _apolloClient.cache.restore(data) } // For SSG and SSR always create a new Apollo Client if (typeof window === 'undefined') return _apolloClient // Create the Apollo Client once in the client if (!apolloClient) apolloClient = _apolloClient return _apolloClient } export function addApolloState(client, pageProps) { if (pageProps?.props) { pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract() } return pageProps } export function useApollo(pageProps) { const state = pageProps[APOLLO_STATE_PROP_NAME] const store = useMemo(() => initializeApollo(state), [state]) return store }

接著修改 /pages/_app.js,用剛剛的 useApollo 以及 @apollo/client package 提供的 ApolloProvider 包住整個 Next.js app,這樣之後才能在各個 page 或 component 內呼叫 GraphQL API:

import { ApolloProvider } from '@apollo/client' import { useApollo } from '../lib/apolloClient' import '../styles/globals.css' export default function App({ Component, pageProps }) { const apolloClient = useApollo(pageProps) return ( <ApolloProvider client={apolloClient}> <Component {...pageProps} /> </ApolloProvider> ) }

在先前的 apolloClient.js 裡,建立 client 時需指定 GraphQL API endpoint,常規做法是會把路徑抽成環境變數,在這個專案我把它命名成 NEXT_PUBLIC_GRAPHQL_ENDPOINT。

注意它有 NEXT_PUBLIC_ 這個前綴,這是因為我們希望這個環境變數在瀏覽器端也能看到,因為在後面幾篇文章我們要實作分頁功能時,抓取第二頁的文章列表,這動作是做在 client-side 的,因此瀏覽器也要知道我們 API_ENDPOINT 為何。在 Next.js 就是透過前綴決定瀏覽器是否看得到,可參閱相關文件,類似 Create-react-app 的 REACT_APP_ 前綴

我習慣將環境變數集中到一支 JavaScript 檔案,集中 export 出去,這樣方便我們一眼看出專案有哪些環境變數,在這裡我們建立 /constants/envValues.js

export const NEXT_PUBLIC_GRAPHQL_ENDPOINT = process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT

再來在 Next.js local 開發過程,環境變數設定通常是用建立環境變數檔方式設定的,啟動 Next.js dev server 時會自動來讀取特定名稱的檔案,通常叫做 .env.local,所以讓我們建立 /.env.local,後面 endpoint 網址替換成你自己的網址,通常用 WPGraphQL 啟用的話都會是在你的 WordPress domain 裡的 /graphql 路徑:

NEXT_PUBLIC_GRAPHQL_ENDPOINT=https://xxxxxx.mybluehost.me/graphql

下一篇:抓取文章列表資料

到此我們成功在 Next.js 安裝 ApolloClient 了,不過還沒完,下一篇文章我們會繼續使用剛安裝的 ApolloClient 從 WordPress 抓取文章資料,在首頁顯示文章列表!

關於這篇文章的改動可以參考這支 commit

本文同步發佈在 iT 邦幫忙 2021 iThome 鐵人賽