Next.jsのプレビューモードを利用して、プレビュー環境を作成する

このチュートリアルでは、Next.jsの Preview Mode と、Newtのプレビュー設定を利用して、プレビュー環境を作成する手順を紹介します。

記事内で使用している主なソフトウェアのバージョン

  • Next.js(next): 13.3.0
  • newt-client-js(newt-client-js): 3.2.4

前提条件

  1. Next.jsの v9.3 以上を利用して、サイトを作成していること
  2. 作成したサイトがデプロイ済みであること
  3. Next.jsの API Routes について理解していること
  4. NewtのJS SDKである newt-client-js の基本的な利用方法について理解していること
  5. newt-client-jsの v3.1.0 以上を利用していること

Next.jsのセットアップについて知りたい場合は、以下のドキュメントをご確認ください。

概要

Next.jsのPreview Modeを利用して、プレビュー用のAPIルートを作成し、プレビューデータを取得できるようにします。
また、Newtのコンテンツ編集画面から、作成したプレビュー環境にアクセスできるようにします。

nextjs_preview.jpg

ここでは、以下の流れでプレビュー処理を行うものとして、実装を進めていきます。

  • Newtの管理画面から「プレビュー」ボタンをクリックする
  • プレビュー用のAPIルート /api/preview に、secretslug のクエリパラメータをつけてアクセスする
  • secretslug の値を検証し、問題なければコンテンツ詳細ページ /articles/{slug} にリダイレクトする
  • コンテンツ詳細ページの getStaticProps でプレビューデータを取得して表示する

ここでは NewtとNext.jsを利用してブログを作成する で作成したブログに対して、プレビュー設定を追加する方法を紹介します。
もし設定したいパスが異なる場合は、適宜読み替えながらチュートリアルを進めてください。

1. Newt API Tokenを作成する

はじめに、Newtの管理画面に入り、スペース設定 > APIキー のページからNewt API Tokenを作成します。
※ 下書き中のコンテンツを取得するためには、Newt APIを利用します。

nextjs_preview2.jpg

名前と取得対象を決めて「作成」を押します。

nextjs_preview3.jpg

2. プレビューデータの取得メソッドを作成する

2-1. 環境変数の設定

Next.jsには環境変数のビルトインサポートがあり、.env.local を使用して、環境変数をロードできます。Next.jsの環境変数について、詳細は Environment Variables のドキュメントをご確認ください。

.env.local ファイルを作成しましょう。以下を実際の値で置き換えて定義してください。
NEWT_CDN_API_TOKEN・NEWT_API_TOKENにはNewtの管理画面で作成したTokenの値を設定します。
NEWT_SPACE_UID・NEWT_APP_UID・NEWT_MODEL_UIDには、プレビューデータの取得対象となるspaceUid・appUid・modelUidの値を設定します。
NEWT_PREVIEW_SECRETはプレビューリクエストが有効なものであるか検証するために利用します。ご自身で定めたシークレットを入力してください。

NEWT_CDN_API_TOKEN=xxxxxxxxxxxxxxx
NEWT_API_TOKEN=xxxxxxxxxxxxxxx
NEWT_SPACE_UID=your-space-uid
NEWT_APP_UID=blog
NEWT_MODEL_UID=article
NEWT_PREVIEW_SECRET=hogehoge

上記のように定義しておくと、process.env.NEWT_CDN_API_TOKENprocess.env.NEWT_API_TOKEN として利用できるようになります。

2-2. プレビューデータの取得メソッドを作成する

Newt CDN API用のクライアントとNewt API用のクライアントをそれぞれ作成します。
token には2-1で設定した環境変数をそれぞれ入力します。

プレビューデータを取得する getArticleBySlug では、引数に preview を渡し、true の場合は apiClient を利用して下書きを含む全コンテンツを取得、false の場合は cdnClient を利用して公開コンテンツのみを取得するようにします。
newt-client-js を利用します。

// lib/newt.ts

import { createClient } from 'newt-client-js'
import type { Article } from '@/types/article'

// Newt CDN APIのクライアント(公開コンテンツのみ取得)
const cdnClient = createClient({
  spaceUid: process.env.NEWT_SPACE_UID + '',
  token: process.env.NEWT_CDN_API_TOKEN + '',
  apiType: 'cdn',
})

// Newt APIのクライアント(全コンテンツ取得)
const apiClient = createClient({
  spaceUid: process.env.NEWT_SPACE_UID + '',
  token: process.env.NEWT_API_TOKEN + '',
  apiType: 'api',
})

(省略)

export const getArticleBySlug = async (slug: string, preview: boolean) => {
  const client = preview ? apiClient : cdnClient
  const article = await client.getFirstContent<Article>({
    appUid: process.env.NEWT_APP_UID + '',
    modelUid: process.env.NEWT_MODEL_UID + '',
    query: {
      slug,
      select: ['_id', 'title', 'slug', 'body'],
    },
  })
  return article
}

3. プレビュー用のAPIルートを作成する

プレビュー用のAPIルートを作成します。ここではエンドポイントが /api/preview となるように、pages/api/preview.ts というファイルを用意します。この処理では、以下のことを行います。

  • リクエストが有効なものか、クエリパラメータのsecretの値で検証する(ここでは2-1で定義した環境変数 NEWT_PREVIEW_SECRET を利用する)
  • slugと対応するコンテンツがあるか検証する
  • Cookieを設定し、プレビューモードを有効にする
  • 取得した情報からパスを指定してリダイレクトする
// pages/api/preview.ts

import type { NextApiRequest, NextApiResponse } from 'next'
import { getArticleBySlug } from '@/lib/newt'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // secretを検証する、slugパラメータの有無を検証する
  if (req.query.secret !== process.env.NEWT_PREVIEW_SECRET || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  // slugと対応するコンテンツがあるか検証する
  const article = await getArticleBySlug(req.query.slug + '', true)
  if (!article) {
    return res.status(401).json({ message: 'Invalid slug' })
  }

  // Cookieを設定し、プレビューモードを有効にする
  res.setPreviewData({})

  // 取得した情報からパスを指定してリダイレクトする
  res.redirect(`/articles/${article.slug}`)
}

4. getStaticPropsを更新する

次に、コンテンツ詳細ページの getStaticProps を更新します。
プレビューモードのCookieが設定されている場合、getStaticProps がビルド時ではなく、リクエスト時に呼び出されます。
また、引数として渡される context オブジェクトの context.preview の値が true となります。
2で作成した getArticleBySlug を利用して、以下のように実装します。

// pages/articles/[slug].tsx

(省略)

type Context = {
  params: {
    slug: string
  }
  preview?: boolean
}

export const getStaticProps = async (context: Context) => {
  const { params, preview = false } = context
  const { slug } = params
  const article = await getArticleBySlug(slug, preview)
  return {
    props: {
      article,
    },
  }
}

これでNext.jsの設定は終了です。変更をコミットして、デプロイしておきましょう。

5. プレビュー設定を行う

最後に、Newtの管理画面に入り、プレビュー設定を行います。
モデル設定の右上から「プレビュー設定」に進みます。

プレビュー用のAPIルート /api/previewsecretslug のクエリパラメータをつけてアクセスするよう、プレビューURLを指定します。
サイトのドメインが https://nextjs-preview.newt.so、secretが hogehoge の場合、プレビューURLは以下のように指定します。

https://nextjs-preview.newt.so/api/preview?secret=hogehoge&slug={slug}

モデルが slug というフィールドを持つ場合、{slug} のように記載することで、各コンテンツのslugの値がプレビューURLに展開されます。

nextjs_preview4.jpg

以上ですべての設定ができました。
コンテンツ編集画面からプレビューが見れるか確認しましょう。

もし、プレビューが見れない場合は、プレビューURLが正しく指定されているか、tokenやsecretの値が正しいか確認してみてください。

また、Next.jsのプレビュー機能について、より詳しく確認したい方は、Vercelの Preview Mode をご確認ください。

Newt Made in Newt