Next.js + Netlify + GraphCMSでJAMStackことはじめ

[Ateam Finergy Inc. Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ateam-finergy)の13日目は、[@murajun1978](https://qiita.com/murajun1978)が担当します

## はじめに
ちょっと前に、「NetlifyがNext.jsのPreview modeをフルサポートしたぜ！」って[アナウンス](https://www.netlify.com/blog/2020/10/27/preview-mode-for-next.js-now-fully-supported-on-netlify/)がありました
Preview modeといえば、Headless CMS？なので、GraphCMSと一緒に試してみます

ご注意:
このサンプルでは、Netlify Functionsを使います
Netlifyのフリープランで利用可能ですが、125k/per monthを超えると課金対象になるのでご注意ください

ちなみに、Netlify Functionsは、AWS Lambdaで動いてます
料金体系は違いますが、実行時間の制限などは同じです

## GraphCMSとは
[GraphCMS](https://graphcms.com/)
APIサーバがGraphQLなCMSです
ダッシュボードにはGraphQLのPlaygroundもあったりします

![graphcms_playground.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/a9c57258-dc27-c4a0-1b16-d2a41ad370f3.png)

## やってみる
### 環境構築
Next.jsの環境を構築します

```bash
$ npx create-next-app my-app
```

### next-on-netlify
Next.jsをNetlifyにデプロイするためのパッケージをインストールします

```bash
$ npm install next-on-netlify
```

Next.jsをexperimental-serverless-traceモードに変更
最初はserverlessモードに設定していたのですが、Preview modeでは動作しませんでした
FYI: https://github.com/netlify/next-on-netlify/issues/99

```next.config.js
module.exports = {
  target: 'experimental-serverless-trace',
};
```

```netlify.toml
[build]
  command   = "npm run build"
  functions = "out_functions"
  publish   = "out_publish"
```

### graphql-request
いつもApollo Clientですが、たまには違うものをと探してみつけたGraphQL Client
[graphql-request](https://github.com/prisma-labs/graphql-request)

```bash
$ npm add graphql-request graphql
```

でも、僕はやっぱりApolloが使いやすい

### GraphCMS
GraphCMSでProjectを作成して、API Endpointの設定をします

- `Public API permissions`
パブリックアクセスの設定です
デフォルトではQuery,Mutationsともに許可されていません

- `Permanent Auth Tokens`
トークンを払い出して、トークン毎にpermisionの設定を行います
このサンプルではこちらを使います
Draft, Publishedどちらも許可して、デフォルトはPublishを取得するように設定しました

![graphcms_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/98eff482-b22f-cb8c-46ef-89d437d1b851.png)

### Netlify
Netlifyにデプロイしてみます

siteを作成して、指定のブランチを設定するだけです(簡単！
あと、GraphCMSのAPI ENDPOINTとtokenを環境変数にセットします

deploy settingsのenvironmentに追加します

![graphcms_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/97cbd7a4-78f5-5fe3-0e3f-f31f41bb0353.png)

あとはremoteブランチにpushすれば、自動でbuildが実行されます
buildに成功すれば、siteにアクセスすると表示きるはずです

![blog_list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/b9226762-8a7f-667e-e3ec-7866a8fd50f1.png)

ちなみにblogページを実装しただけのLighthouseのスコア

![lighthouse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/0cc9bd1f-9dcb-f026-8976-c34645cc37ac.png)

### Preview mode
公開前に記事をプレビューモードで表示してみます

まずは、Preview modeのエンドポイントを作成します

```preview.js
export default async (req, res) => {
  if (req.query.secret !== process.env.PREVIEW_MODE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  res.setPreviewData({})
  res.redirect('/blog')
}
```

トークンをチェックしてOKであれば、`/blog`にリダイレクトします
今回はトークンでチェックしてますが、OAuthで認証するのが正しいかとおもいます

Preview modeでアクセスが来たときに、Draftの記事も表示できるようにします

```blog.js
export const getStaticProps = async () => {
  const client = new GraphQLClient(process.env.GRAPHCMS_API, {
    headers: {
      authorization: `Bearer ${process.env.GRAPHCMS_TOKEN}`
    }
  })
  const { posts } = await client.request(query)

  return {
    props: {
      posts
    }
  }
}
```

まずはPreview modeではなく、普通にアクセスしてみます

![blog_not_preveiw_mode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/c5f2449a-d677-3b14-665a-20e66234e0b9.png)

Preview modeでアクセスしてみます

`/api/preview?secret=xxx` にアクセスします

一番下に`Sample Post`ってDraftの記事も表示されていますね

![blog_preview_mode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6470/57adb434-d3f7-516f-2fcd-13d63bfbe75c.png)

あと、`__next_preview_data`ってcookieがセットされていますよね？
これがPreview modeである印です
なので、このCookieを削除してリロードすれば、Draftの記事は表示されません

## まとめ
記事を作成、編集すると、ビルドはNext.jsがいい感じにやってくれます
なにより、デプロイって工程がなくなります(メリットは他にもたくさんあります)
デプロイは、GitHub Actionsとかで自動化することはできると思いますが

Next.jsのPreview modeが面白いのは、本番環境と同じURLでプレビューできるってとこです
Stagingで記事を書いてプレビューして、本番環境にコピーしてなんてこともしなくてよくなりますし、
Preview用のエンドポイントの作成も不要です

あと、OSSのCMSもたくさんあるみたいなので、こちらも試してみたいと思います