Next.js + Contentful のサイトにプレビュー機能をつける

Next.js はプレビューの機能を備えているので、簡単にプレビュー機能を実装できます。プレビュー機能を使用すると、`getStaticProps`を使用し静的に生成したページで下書き中の文章がどのような表示になるかを確認することができます。

機能を実装する手順は以下の通りです。

  1. Next.jsの修正

    1. プレビューで表示を行うためのクッキーを発行するAPIを作る

    2. プレビュー表示用に、プレビューする対象の詳細ページを修正する

  2. Contentfulの設定を変更

    1. Contentful にプレビューURLを設定する

プレビュー表示用のクッキーを発行するAPIを作る

Next.jsは、プレビュー用クッキーが設定されていることを確認しプレビューモードかどうかを判断するので、まずはプレビュー用クッキーを設定するルートを用意します。

/api/preview.js のようなAPIのルートを作成し、res.setPreviewData({}) を呼び出すことでプレビュー用クッキーを発行します。

export default function handler(req, res) {
  // ...
  res.setPreviewData({})
  // ...
}

全てのリクエストにプレビュー用のクッキーを発行してしまうと、誰でもプレビューできてしまうので、シークレットトークン文字列をツールなどを使い生成し、シークレットトークンを持つリクエストのみを許可するように修正します。環境変数 CONTENTFUL_PREVIEW_SECRET にシークレットトークンを設定した上で、secretパラメータに同様のGETパラメータを設定することでシークレットトークンを知っている人のみ正常に処理を行うようにします。

export default async function handler(req, res) {
    const { secret } = req.query
    if (secret !== process.env.CONTENTFUL_PREVIEW_SECRET) {
        return res.status(401).json({ message: 'Invalid token' })
    }
    res.setPreviewData({})
    res.end()
}

最後にプレビューを行う投稿が存在するのかを確認し、存在している場合はそのページにリダイレクトするように修正します。slugパラメータに設定した値を元に投稿の存在チェックとリダイレクトURLを設定します。

import { NextApiRequest, NextApiResponse } from "next"
import { getPreviewPostBySlug } from "../../lib/contentful"

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { secret, slug } = req.query

  if (secret !== process.env.CONTENTFUL_PREVIEW_SECRET || !slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  const post = await getPreviewPostBySlug(slug as string)

  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }

  res.setPreviewData({})

  // url は、サイトごとに修正します
  const url = `/articles/${slug}`
  res.setHeader('Content-Type', 'text/html')
  res.write(
    `<!DOCTYPE html><html><head><meta http-equiv="Refresh" content="0; url=${url}" />
    <script>window.location.href = '${url}'</script>
    </head>
    </html>`
  )
  res.end()
}

プレビュー対象の詳細ページにプレビュー表示用の修正をする

プレビュー用のクッキーが発行された状態で getStaticProps が設定されたページにアクセスすると、引数でpreview=trueを設定してくれます。この値を使用し、previewモードでアクセスしてきた場合、プレビュー用のデータを取得するように修正します。

export async function getStaticProps({ params, preview = false }: any) {
  const postId = params.postId
  const res = await fetcher(postId, preview)

  return { props: {...res, postId},  }
}

Contentful にプレビューURLを設定する

プレビューのURLは、 Settings > Content Preview で設定できます。このサイトではidをURLで使用しているので、下記の通りに設定しました。

https://www.superarai.com/api/preview?secret=[シークレットトークン]&slug={entry.sys.id}

以上で、記事にプレビューボタンが設定されプレビューができるようになります。