Nuxt3でプレビュー環境を作成する

最終更新日:

Table of contents

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

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

  • Nuxt(nuxt): 3.7.4
  • newt-client-js(newt-client-js): 3.2.7

概要

Nuxt3で静的生成(SSG)されているブログサイトに、プレビュー用のページを追加し、サーバーサイドレンダリング(SSR)でプレビューデータを取得できるようにします。
また、Newtのコンテンツ編集画面から、作成したプレビュー環境にアクセスできるようにします。ホスティングには Vercel を利用します。

nuxt3-preview1.jpg

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

  • Newtの管理画面から「プレビュー」ボタンをクリックする
  • プレビュー用のページ /articles/preview に、secretslug のクエリパラメータをつけてアクセスする
  • secretslug の値を検証し、問題なければプレビューデータ(下書きデータ)を取得して表示する

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

Nuxt3でプレビューをどう実装するか

具体的な方法の紹介に入る前に、Nuxt3で静的生成(SSG)されているサイトにプレビューを追加する場合、どのようなパターンがあるか改めて確認しておきましょう。
まず、Nuxt2の時にあった Preview Mode ですが、Nuxt3では記載がありません(詳細については こちら のissueに記載があります)。

そこで、以下のような方法の中から、サイトの特性や要件に応じて選択する必要があります。

  • プレビュー用の環境を用意して、環境に応じてレンダリング方法を制御する
  • サイトをサーバーサイドレンダリング(SSR)に変更して、クエリに応じて公開情報を表示するか、プレビューを表示するか決定する
  • プレビュー専用ページを用意して、そのページだけサーバーサイドレンダリング(SSR)で表示する

ここでは、サイト自体はプリレンダリングを利用して静的生成したまま、プレビュー用の環境を用意する必要もない「プレビュー専用ページを用意して、そのページだけサーバーサイドレンダリング(SSR)で表示する」方法について紹介します。

通常ページと、プレビュー専用ページの設定が同一となるよう、コードの共通化を行う手間は発生しますが、以下の内容については満たせる方法となっています。

  • 通常ページはプリレンダリングを行い、静的に生成される
  • NewtのTokenが漏洩しない
  • プレビューページが検索エンジンにインデックス登録されない

それでは、具体的なプレビュー環境の作成に進みましょう。

1. Newt API Tokenを作成する

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

nextjs_preview2.jpg

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

nextjs_preview3.jpg

2. プレビュー用のクライアントを作成する

2-1. 環境変数の設定

Nuxtの Runtime Config を設定して、環境変数を利用できるようにします。
まず、.env ファイルを作成し、以下を実際の値で置き換えて定義してください。
NUXT_NEWT_SPACE_UID には、プレビューデータの取得対象となるスペースUIDの値を設定します。
NUXT_NEWT_CDN_API_TOKENNUXT_NEWT_API_TOKEN にはNewtの管理画面で作成したTokenの値を設定します。
NUXT_NEWT_PREVIEW_SECRET はプレビューリクエストが有効なものであるか検証するために利用します。ご自身で定めたシークレットを入力してください。

.env
1NUXT_NEWT_SPACE_UID=sample-for-docs
2NUXT_NEWT_CDN_API_TOKEN=xxxxxxxxxxxxxxx
3NUXT_NEWT_API_TOKEN=xxxxxxxxxxxxxxx
4NUXT_NEWT_PREVIEW_SECRET=hogehoge

あわせて、nuxt.config.ts に以下のように runtimeConfig の設定を追加します。

nuxt.config.ts
export default defineNuxtConfig({
  (省略)
  runtimeConfig: {
    newt: {
      spaceUid: '',
      cdnApiToken: '',
      apiToken: '',
      previewSecret: ''
    }
  },
  (省略)
})

Runtime Confingは実行時にマッチする環境変数に自動的に置き換えられるため、上記のように定義しておくと、runtimeConfig.newt.apiToken の値は .env ファイルの NUXT_NEWT_API_TOKEN の値に置き換えられ、runtimeConfig.newt.previewSecret の値は .env ファイルの NUXT_NEWT_PREVIEW_SECRET の値に置き換えられます。
また、これらの変数はサーバーサイドでのみ参照できる、プライベートな変数となります。

Runtime Configの詳細については、Nuxtの Runtime Config のドキュメントをご確認ください。

2-2. プレビュー用のクライアントを作成する

プラグインに、Newt API用のクライアントを追加します。
ここでは newtPreviewClient という名前で追加しています。

プラグインでは、ファイル名に .server または .client というサフィックスを付けると、サーバー側またはクライアント側でのみプラグインを読み込むことができます。
ここではサーバーサイドでのみ読み込めれば良いので、plugins ディレクトリを作成し、その中に newt.server.ts というファイルを作成します。

plugins/newt.server.ts
1import { createClient } from 'newt-client-js'
2
3export default defineNuxtPlugin(() => {
4  const config = useRuntimeConfig()
5
6  const newtClient = createClient({
7    spaceUid: config.newt.spaceUid,
8    token: config.newt.cdnApiToken,
9    apiType: 'cdn'
10  })
11
12  const newtPreviewClient = createClient({
13    spaceUid: config.newt.spaceUid,
14    token: config.newt.apiToken,
15    apiType: 'api'
16  })
17
18  return {
19    provide: {
20      newtClient,
21      newtPreviewClient
22    }
23  }
24})

3. プレビュー用のページを作成する

通常の詳細ページとして、以下が定義されているとします。

pages/articles/[slug].vue
1<script lang="ts" setup>
2import type { Article } from '~/types/article'
3
4const route = useRoute()
5const { slug } = route.params
6
7const { data } = await useAsyncData(`article-${slug}`, async () => {
8  const { $newtClient } = useNuxtApp()
9  return await $newtClient.getFirstContent<Article>({
10    appUid: 'blog',
11    modelUid: 'article',
12    query: {
13      slug,
14      select: ['_id', 'title', 'slug', 'body']
15    }
16  })
17})
18const article = data.value
19
20useHead({
21  title: article?.title,
22  meta: [
23    { name: 'description', content: '投稿詳細ページです' }
24  ]
25})
26</script>
27
28<template>
29  <main class="main">
30    <h2>{{ article?.title }}</h2>
31    <!-- eslint-disable-next-line vue/no-v-html -->
32    <div v-html="article?.body" />
33  </main>
34</template>

この内容を元に、プレビューページを作成しましょう。
パスを /articles/preview とする場合、pages/articles/preview.vue を作成します。

クエリパラメータの slugsecret の検証を行い、問題がなければ $newtPreviewClient を利用して、プレビューデータ(下書きデータ)の取得を行います。

検索エンジンによるページのインデックスを防ぐために useHeadmeta{ name: 'robots', content: 'noindex,nofollow' } を設定するのも忘れないようにしましょう。

pages/articles/preview.vue
1<script lang="ts" setup>
2import type { Article } from '~/types/article'
3
4const config = useRuntimeConfig()
5const route = useRoute()
6const { slug, secret } = route.query
7
8// slugパラメータの有無を検証する
9if (!slug) {
10  throw createError({ statusCode: 400, statusMessage: 'Invalid slug' })
11}
12
13// secretを検証する
14if (config.newt && secret !== config.newt.previewSecret) {
15  throw createError({ statusCode: 401, statusMessage: 'Invalid secret' })
16}
17
18const { data } = await useAsyncData(`article-${slug}-preview`, async () => {
19  const { $newtPreviewClient } = useNuxtApp()
20  return await $newtPreviewClient.getFirstContent<Article>({
21    appUid: 'blog',
22    modelUid: 'article',
23    query: {
24      slug: slug?.toString(),
25      select: ['_id', 'title', 'slug', 'body']
26    }
27  })
28})
29const article = data.value
30
31useHead({
32  title: article?.title,
33  meta: [
34    { name: 'description', content: '投稿詳細ページです' },
35    { name: 'robots', content: 'noindex,nofollow' }
36  ]
37})
38</script>
39
40<template>
41  <main class="main">
42    <h2>{{ article?.title }}</h2>
43    <!-- eslint-disable-next-line vue/no-v-html -->
44    <div v-html="article?.body" />
45  </main>
46</template>

また、このままだと pages/articles/[slug].vue を更新する時に pages/articles/preview.vue も更新することになるため、必要であれば共通のコンポーネントを利用するなどして、コードの共通化を行うといいでしょう。

4. レンダリングの設定を行う

続いてレンダリングの設定を行います。
もともと静的生成されているサイトの場合、nuxi generate を用いている場合が多いかと思いますが、プレビューではサーバーサイドレンダリング(SSR)を行うため、設定を変更します。

プレビュー以外のパスはプリレンダリング、プレビュー用のパスはプリレンダリングなしでビルドを行います。
ビルドコマンドに nuxi build を利用し、configファイルの nitro から nitro.prerender を設定します。

プリレンダリングしたいページが、すべてクロール可能である場合、crawlLinks のオプションを true にするのが簡単です。
nuxt.config.ts に、以下を追加しましょう。

nuxt.config.ts
export default defineNuxtConfig({
  (省略)
  nitro: {
    prerender: {
      crawlLinks: true
    }
  },
  (省略)
})

crawlLinks 以外に、手動でページ追加したい場合は routes オプションを利用することも可能です。詳細はNuxtの Selective Pre-rendering のドキュメントをご確認ください。

もし、nitro.prerender で用意されているオプションでは不十分な場合、hooks オプションを利用して、より詳細な設定を行うことも可能です。

例えば、blog モデルの全ての article について詳細ページをプリレンダリングする場合は、以下のようになります。

nuxt.config.ts
1import { createClient } from 'newt-client-js'
2import type { Article } from './types/article'
3
4const newtClient = createClient({
5  spaceUid: process.env.NUXT_NEWT_SPACE_UID + '',
6  token: process.env.NUXT_NEWT_CDN_API_TOKEN + '',
7  apiType: 'cdn'
8})
9
10export default defineNuxtConfig({
11  (省略)
12  hooks: {
13    async 'nitro:config' (nitroConfig) {
14      if (nitroConfig.dev || !nitroConfig.prerender) {
15        return
16      }
17
18      const { items: articles } = await newtClient.getContents<Article>({
19        appUid: 'blog',
20        modelUid: 'article',
21        query: {
22          select: ['_id', 'title', 'slug', 'body']
23        }
24      })
25
26      // 全記事の詳細ページを追加
27      nitroConfig.prerender.routes = articles.map((article) => {
28        return `/articles/${article.slug}`
29      })
30
31      // 一覧ページを追加
32      nitroConfig.prerender.routes.push('/')
33    }
34  },
35  (省略)
36})
上記の実装は、NuxtのGitHubリポジトリにある コメント を参考に記載しています。変更の可能性もあるため、ご注意ください。

5. デプロイの設定を行う

Vercelへのデプロイを設定します。

まず、GitHubのリポジトリを作成し、これまで作成したコードをプッシュします。
詳細はGitHubの リポジトリを作成する のドキュメントをご確認ください。

続いて、作成したリポジトリとVercelを接続します。
Vercel のアカウントを持っていない方は、登録をお願いします。

接続方法の詳細については、GitHubのリポジトリとVercelを接続して、ホスティングする のチュートリアルを参考にしてください。

「Framework Preset」に「Nuxt.js」が設定されていることを確認し、「Build Command」に「nuxt build」を指定しましょう。「nuxt generate」ではないのでご注意ください。
また「Environment Variables」に追加した環境変数を設定しましょう。
nuxt3-preview2.jpg

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

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

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

https://nuxt-preview.vercel.app/articles/preview?secret=hogehoge&slug={slug}

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

nuxt3-preview3.jpg

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

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

NewtMade in Newt