Next.jsとHasuraを連携してデータを画面に出すところまでやる（ための具体的なApollo Clientの設定）...2

## はじめに

以下のゴールを達成するため、本日はついに、Next.jsとHasuraを連携してデータを画面に出すところまでやります。

特に今回は、Next.jsでApollo Clientを使ってSSR(SSG)する仕組みの部分を見て行くと思います。

![イメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/a82854a4-b7ba-4ddd-bf48-c7678f452d7f.jpeg)

Hasuraにあるデータやエンドポイントは既に用意してあります。

## 最終的なゴール

以下のような構成のアプリを作ることです。

![スクリーンショット 2021-10-18 17.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/e8a9a2e1-2723-4fec-77ef-be3b9eefdd81.png)

### 目的

- 仕事で使っている技術のキャッチアップと復習
- 使う可能性がある技術の理解度向上

### Apollo Clientの準備

以下の2つを連携させていきます。

- Frontend: Next.jsとApolloClient
- Backend: HasuraのGraphqlサーバ

その前に復習

```
Next.js は基本的にすべてのページを Pre-rendering しますが、その中でも

- ビルド時にサーバ側でデータを取得してHTMLを生成し、リクエストに対してそれを返す SG（Static Generation）
- クライアントのリクエストに対してサーバ側でデータを取得してHTMLを生成し、返す SSR(Server Side Rendering)

があることを覚えてください。
```

具体的な方法はここからパクります。

https://github.com/vercel/next.js/blob/canary/examples/with-apollo/lib/apolloClient.js

Next.js公式の実例集ですね。

ここから必要な部分だけ抜き取っていきます。

それでは、コードを書いていきます。階層やファイル名も実例集を真似て作成してみましょう。

```
lib/apolloClient.js
```

```ts:lib/apolloClient.ts
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
}
```

部分的にコードの解説をします。

```ts
// | より左がapollo client お決まりのデータ型の定義です このために NormalizedCacheObject を import したんすね
// | より右が undefined つまり未定義な可能性もなきにしもあらずでっせ。という意味
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined
```

続いて ApolloClient インスタンスを作成する関数を見てみましょう。

```ts
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つの引数を必要としていますね。１つづつみていきます。

`ssrMode`について

`window`というのは、「まあ、ブラウザで実行している」と思ってていいです。

大抵こうなるし。

![7533149b6166bf108366ee57f9ade72a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/f6adab16-a6f4-2e8a-d670-427a8c650273.gif)

そうならない

すなわち

```ts
ssrMode: true
```

になる場合（`typeof window === 'undefined'`が`TRUE`）はサーバで実行していると思ってください。

次

```ts
link: new HttpLink({uri: 'ここでは作成したHasuraのAPIのエンドポイントを入力します' })
```

環境変数から参照しても良いです。

最後に `cache` ですが、これはお決まりの形（おまじない）的に覚えてください。

```ts
cache: new InMemoryCache()
```

次に、ApolloClientインスタンスを作成に関する初期化に関する関数をみていきます

```ts
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
}
```

こちらも上から見ていきましょう。

```ts
const _apolloClient = apolloClient ?? createApolloClient()
```

`??`より左辺が `null` | `undefined` なら、右辺を実行するという意味です。

全体的な処理を見ると、`initializeApollo`関数は、最初は、右辺（`createApolloClient()`）が実行され新しいApolloClientインスタンスが生成され`_apolloClient`に代入される。

その後

それがサーバでの実行なら（（`typeof window === 'undefined'`が`TRUE`））で、即座に`_apolloClient`が返されます

変数`apolloClient`には何も代入していないので、

```ts
const _apolloClient = apolloClient ?? createApolloClient()
```

これはまた、右辺が実行され、新たなApolloClientインスタンスが生成され`_apolloClient`に代入されます。

サーバーで実行する限りそうなります。

その結果

```
// For SSG and SSR always create a new Apollo Client
```

このコメントが、その通りになった、という感じですね。

対して、クライアントサイドで、実行された場合を見ていきましょう。

どちらにせよ下記は実行されます。

```ts
const _apolloClient = apolloClient ?? createApolloClient()
```

次に、`typeof window === 'undefined'`が`FALSE`になるので、この行はSKIPされます。

次に

```ts
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient
```

最初の実行なら `apolloClient` は `undefined` で、それの`!`なので、`TRUE`になって、`apolloClient`に`_apolloClient`が代入されます。

サーバで実行する場合、毎回ApolloClientインスタンスを生成し、返す。

クライアントで実行する場合、最初はサーバから取ってくるけど、次回からは、1回目に`apolloClient`に`_apolloClient`が代入されるので、

return `_apolloClient` つまり、2回目以降は、１回目に作成した`apolloClient`が返されます。

これが、Next.js でサーバーサイドの処理とクライアントサイドの処理の切り分けをする上で重要になります。

これで、Apollo側の設定は終わります。

今日のところは以上です。

次は、pages配下の実装をみていきます。

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103