はじめに
以下のゴールを達成するため、本日はついに、Next.jsとHasuraを連携してデータを画面に出すところまでやります。
特に今回は、Next.jsでApollo Clientを使ってSSR(SSG)する仕組みの部分を見て行くと思います。
Hasuraにあるデータやエンドポイントは既に用意してあります。
最終的なゴール
以下のような構成のアプリを作ることです。
目的
- 仕事で使っている技術のキャッチアップと復習
- 使う可能性がある技術の理解度向上
Apollo Clientの準備
以下の2つを連携させていきます。
- Frontend: Next.jsとApolloClient
- Backend: HasuraのGraphqlサーバ
その前に復習
Next.js は基本的にすべてのページを Pre-rendering しますが、その中でも
- ビルド時にサーバ側でデータを取得してHTMLを生成し、リクエストに対してそれを返す SG(Static Generation)
- クライアントのリクエストに対してサーバ側でデータを取得してHTMLを生成し、返す SSR(Server Side Rendering)
があることを覚えてください。
具体的な方法はここからパクります。
Next.js公式の実例集ですね。
ここから必要な部分だけ抜き取っていきます。
それでは、コードを書いていきます。階層やファイル名も実例集を真似て作成してみましょう。
lib/apolloClient.js
import {
ApolloClient,
HttpLink,
InMemoryCache,
NormalizedCacheObject,
} from '@apollo/client'
import 'cross-fetch/polyfill'
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined
console.log('apolloClient', apolloClient)
const createApolloClient = () => {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: 'https://ryosuketter-hasura-test.hasura.app/v1/graphql',
}),
cache: new InMemoryCache(),
})
}
export const initializeApollo = (initialState = null) => {
const _apolloClient = apolloClient ?? createApolloClient()
// 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
}
部分的にコードの解説をします。
// | より左がapollo client お決まりのデータ型の定義です このために NormalizedCacheObject を import したんすね
// | より右が undefined つまり未定義な可能性もなきにしもあらずでっせ。という意味
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined
続いて ApolloClient インスタンスを作成する関数を見てみましょう。
const createApolloClient = () => {
return new ApolloClient({
// typeof window は、ブラウザで実行しているかどうか?サーバー側で実行しているか?の条件判定
// 何を実行しているかというとプリレンダリング
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: 'https://ryosuketter-hasura-test.hasura.app/v1/graphql',
}),
cache: new InMemoryCache(),
})
}
ApolloClientインスタンスを作成するのに、3つの引数を必要としていますね。1つづつみていきます。
ssrMode
について
window
というのは、「まあ、ブラウザで実行している」と思ってていいです。
大抵こうなるし。
そうならない
すなわち
ssrMode: true
になる場合(typeof window === 'undefined'
がTRUE
)はサーバで実行していると思ってください。
次
link: new HttpLink({uri: 'ここでは作成したHasuraのAPIのエンドポイントを入力します' })
環境変数から参照しても良いです。
最後に cache
ですが、これはお決まりの形(おまじない)的に覚えてください。
cache: new InMemoryCache()
次に、ApolloClientインスタンスを作成に関する初期化に関する関数をみていきます
export const initializeApollo = (initialState = null) => {
const _apolloClient = apolloClient ?? createApolloClient()
// For SSG and SSR always create a new Apollo Client
// サーバーサイドで実行されるSSG, SSR, またはISRなどの、サーバーサイドで実行される場合は、常に新しい
if (typeof window === 'undefined') return _apolloClient
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
こちらも上から見ていきましょう。
const _apolloClient = apolloClient ?? createApolloClient()
??
より左辺が null
| undefined
なら、右辺を実行するという意味です。
全体的な処理を見ると、initializeApollo
関数は、最初は、右辺(createApolloClient()
)が実行され新しいApolloClientインスタンスが生成され_apolloClient
に代入される。
その後
それがサーバでの実行なら((typeof window === 'undefined'
がTRUE
))で、即座に_apolloClient
が返されます
変数apolloClient
には何も代入していないので、
const _apolloClient = apolloClient ?? createApolloClient()
これはまた、右辺が実行され、新たなApolloClientインスタンスが生成され_apolloClient
に代入されます。
サーバーで実行する限りそうなります。
その結果
// For SSG and SSR always create a new Apollo Client
このコメントが、その通りになった、という感じですね。
対して、クライアントサイドで、実行された場合を見ていきましょう。
どちらにせよ下記は実行されます。
const _apolloClient = apolloClient ?? createApolloClient()
次に、typeof window === 'undefined'
がFALSE
になるので、この行はSKIPされます。
次に
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
最初の実行なら apolloClient
は undefined
で、それの!
なので、TRUE
になって、apolloClient
に_apolloClient
が代入されます。
サーバで実行する場合、毎回ApolloClientインスタンスを生成し、返す。
クライアントで実行する場合、最初はサーバから取ってくるけど、次回からは、1回目にapolloClient
に_apolloClient
が代入されるので、
return _apolloClient
つまり、2回目以降は、1回目に作成したapolloClient
が返されます。
これが、Next.js でサーバーサイドの処理とクライアントサイドの処理の切り分けをする上で重要になります。
これで、Apollo側の設定は終わります。
今日のところは以上です。
次は、pages配下の実装をみていきます。
アウトプット100本ノック実施中