NewtとNuxt3を利用してブログを作成する

このチュートリアルでは、Newtと Nuxt3 を利用して、ブログを作成する手順を紹介します。
具体的には、Newtで管理しているコンテンツの一覧ページと詳細ページを作る手順を紹介します。

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

  • Nuxt(nuxt): 3.2.0
  • newt-client-js(newt-client-js): 3.2.4

概要

Nuxt3でプロジェクトを作成し、Newtのコンテンツ情報を取得できるようにします。
コンテンツの一覧ページ(パス: /)と詳細ページ(パス: /articles/:slug 。slugがarticle-1の場合は /articles/article-1)を作成し、ローカル環境で表示を行うまでを説明します。

また、ここでは(クライアントサイドレンダリングではなく)ユニバーサルレンダリングを利用し、かつプリレンダリングを行う方法を紹介します。これはビルド時に静的にHTMLを生成する方法となります。

1. Nuxt3のセットアップ

1-1. プロジェクトの作成

はじめに、Nuxt3のセットアップを行います。以下のコマンドでプロジェクトを作成します。
<project-name> には好きな名前を入力しましょう。

npx nuxi init <project-name>

ここでは nuxt3-blog という名前で作成します。

npx nuxi init nuxt3-blog

作成したプロジェクトに移動して、依存関係をインストールした後、開発サーバーを立ち上げます。yarnを利用する場合は以下のようになります。

$ cd nuxt3-blog
$ yarn install
$ yarn dev

http://localhost:3000 にアクセスして、以下のような画面が表示されることを確認します。
nuxt3-blog1.png

1-2. TypeScriptの設定

開発環境でも型のチェックを行うために、以下の設定を行います。
※ 開発環境で型のチェックを行わない場合は、1-2のステップを飛ばして構いません。

まず @types/nodevue-tsctypescript をdevDependenciesとしてインストールします。

yarn add -D @types/node vue-tsc typescript

さらに、nuxt.config.ts ファイルで typescript.typeCheck オプションを有効にします。

export default defineNuxtConfig({
  typescript: {
    typeCheck: true
  }
})

これで開発環境でも型のチェックができるようになりました。
TypeScriptの設定について、詳細はNuxtの TypeScript のドキュメントをご確認ください。

2. Newtのセットアップ

次にNewtにコンテンツとAPIトークンを用意し、コンテンツの取得を行うための準備を行います。

2-1. Appを追加する

「Appを追加」をクリックして「テンプレートから追加」を選択します。

Appを追加する

表示されるテンプレートの中から「Blog」を選択して、「このテンプレートを追加」をクリックします。
Appテンプレート

テンプレートが追加されると、「投稿データ」「カテゴリデータ」「著者データ」が追加されます。

quick-start03.jpg

2-2. スペースUID・App UID・モデルUIDを確認する

スペースUIDは「スペース設定」から確認できます。

quick-start0402.jpgquick-start0503.jpg

上記の例だと、スペースUIDは sample-for-docs となります。
この値は3-1で環境変数として定義します。

また「Blog」テンプレートを追加した場合、App UIDは blog、「投稿データ」モデルUIDは article となります。
これらの値は、4-4や5-1で投稿情報を取得する際に利用します。

2-3. Newt CDN API Tokenを作成する

続いて、APIリクエストに必要なトークンを発行します。
スペース設定 > APIキー のページからNewt CDN API Tokenを作成します。

quick-start06.jpg

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

quick-start07.jpg

ここで作成したトークンの値は3-1で環境変数として定義します。

3. リクエストの準備

Newtの SDK を利用することで、NewtのAPIをより簡単に利用できます。
ここではSDKを利用して、NewtのAPIクライアントを作成します。

3-1. 環境変数の設定

Nuxtの Runtime Config を利用して、環境変数を利用できるようにします。
まず、.env ファイルを作成し、2-2で確認したスペースUID、2-3で作成したトークンの値を定義します。以下を、実際の値で置き換えて定義してください。

NUXT_NEWT_SPACE_UID=sample-for-docs
NUXT_NEWT_CDN_API_TOKEN=xxxxxxxxxxxxxxx

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

export default defineNuxtConfig({
+  runtimeConfig: {
+    newt: {
+      spaceUid: '',
+      cdnApiToken: ''
+    }
+  },
  typescript: {
    typeCheck: true
  }
})

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

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

3-2. newt-client-jsのインストール

次に newt-client-js をインストールします。

npm install newt-client-js
# or
yarn add newt-client-js

3-3. プラグインの作成

プラグインを作成し、CDN APIを利用するためのクライアントを定義します。
プラグインはNuxtアプリケーションの初期化時に実行されます。デフォルトではサーバーサイド、クライアントサイド双方で実行されます。

Nuxtは plugins ディレクトリにあるファイルを自動的に読み込んで、Vueアプリケーションの作成時にロードします。ファイル名に .server または .client というサフィックスを付けると、サーバー側またはクライアント側でのみプラグインを読み込むことができます。

ここではサーバーサイドでのみ読み込めれば良いので、plugins ディレクトリを作成し、その中に newt.server.ts というファイルを作成します。

// plugins/newt.server.ts

import { createClient } from 'newt-client-js'

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const newtClient = createClient({
    spaceUid: config.newt.spaceUid,
    token: config.newt.cdnApiToken,
    apiType: 'cdn'
  })
  return {
    provide: {
      newtClient
    }
  }
})

これで、Vueコンポーネント内から、Newtのクライアントを利用できるようになりました。
プラグインの詳細については、Nuxtの Plugins Directory のドキュメントをご確認ください。

4. 一覧ページの作成

4-1. 言語の設定をする

nuxt.config.tslang 属性を設定します。ここでは日本語 ja を指定します。

export default defineNuxtConfig({
+ app: {
+   head: {
+     htmlAttrs: {
+       lang: 'ja'
+     }
+   }
+ },
  runtimeConfig: {
    newt: {
      spaceUid: '',
      cdnApiToken: ''
    }
  },
  typescript: {
    shim: false,
    typeCheck: true
  }
})

4-2. 投稿の型を定義する

投稿の型 Article を定義しておきます。
このチュートリアルでは、_idtitleslugbody のみを使うため、以下のように定義しておきます。

// types/article.ts

export interface Article {
  _id: string
  title: string
  slug: string
  body: string
}

4-3. NuxtPageを設定する

この後に作成する pages ディレクトリ配下のページを表示できるように、app.vue ファイルを修正します。NuxtWelcomeNuxtPage に変更します。

<template>
  <div>
-    <NuxtWelcome />
+    <NuxtPage />
  </div>
</template>

4-4. 投稿一覧を取得する

Nuxtではファイルシステムベースのルーティングを採用しており、pages ディレクトリの配下にファイルを作成すると、自動的にルートとして利用できるようになります。
例えば、以下のようにルーティングされます。

  • pages/blog/index.vue/blog
  • pages/blog/first-post.vue/blog/first-post

※ ルーティングの詳細については、Nuxtの Routing のドキュメントをご確認ください。

ここではトップページ(パス: /)で投稿一覧を表示したいので、pages ディレクトリを作成し、その中に index.vue というファイルを作成します。

まず、script タグに以下の内容を記載します。
useAsyncData を利用してNewtから投稿一覧を取得し、articles という変数で定義することで、template の中で参照できるようにしています。
また、useHead を利用して、タイトルとディスクリプションを設定しています。

<!-- pages/index.vue -->

<script lang="ts" setup>
import type { Article } from '~/types/article'

const { data } = await useAsyncData('articles', async () => {
  const { $newtClient } = useNuxtApp()
  return await $newtClient.getContents<Article>({
    appUid: 'blog',
    modelUid: 'article',
    query: {
      select: ['_id', 'title', 'slug', 'body']
    }
  })
})
const articles = data.value?.items

useHead({
  title: 'Newt・Nuxtブログ',
  meta: [
    { name: 'description', content: 'NewtとNuxtを利用したブログです' }
  ]
})
</script>

データの取得について詳細を説明します。
まず、useNuxtApp を利用して、3-3で登録した newtClient を利用できるようにします。
次に、SDKが提供している getContents メソッドを利用して、投稿一覧を取得します。getContentsのパラメータに Article の型を渡すことで、返却される items の型として Article[] が指定されます。
また、selectパラメータを利用して、取得するフィールドを _idtitleslugbody のみに制限します。
最後に articles として data.value?.items を定義することで、getContentsで返却されたオブジェクトの items 要素を articles として参照できるようにします。

4-5. 投稿一覧を表示する

4-4で定義した articles を利用して、template内で投稿一覧を表示できるようにします。
pages/index.vue に以下の部分を追加します。

<template>
  <div>
    <ul>
      <li v-for="article in articles" :key="article._id">
        <NuxtLink :to="`/articles/${article.slug}`">
          {{ article.title }}
        </NuxtLink>
      </li>
    </ul>
  </div>
</template>

http://localhost:3000/ にアクセスして、以下のように投稿一覧が表示されれば成功です。

nuxt3-blog2.png

5. 詳細ページの作成

5-1. 投稿詳細を取得する

Nuxtでは [param] のようにしてページ名に角括弧を使うことで動的なルーティング(Dynamic Routes)を作成できます。ここでは、/articles/:slug/articles/article-1 など)のパスで投稿の詳細を表示したいので、pages/articles/[slug].vue のファイルを作成します。

まず、script タグに以下の内容を記載します。
投稿一覧の取得と同様に、useAsyncData を利用します。article という変数で定義することで、template の中で参照できるようにしています。

<!-- pages/articles/[slug].vue -->

<script lang="ts" setup>
import type { Article } from '~/types/article'

const route = useRoute()
const { slug } = route.params

const { data } = await useAsyncData(`article-${slug}`, async () => {
  const { $newtClient } = useNuxtApp()
  return await $newtClient.getFirstContent<Article>({
    appUid: 'blog',
    modelUid: 'article',
    query: {
      slug,
      select: ['_id', 'title', 'slug', 'body']
    }
  })
})
const article = data.value

useHead({
  title: article?.title,
  meta: [
    { name: 'description', content: '投稿詳細ページです' }
  ]
})
</script>

投稿詳細を取得するメソッドとして、SDKが提供している getFirstContent を利用しています。このメソッドはクエリに該当するコンテンツのうち、最初の1件を返却するメソッドです。指定したスラッグのコンテンツを取得したい場合は、このメソッドを利用します。

5-2. 投稿詳細を表示する

5-1で定義した article を利用して、template内で投稿詳細を表示できるようにします。
pages/articles/[slug].vue に以下の部分を追加します。

<template>
  <main class="main">
    <h2>{{ article?.title }}</h2>
    <!-- eslint-disable-next-line vue/no-v-html -->
    <div v-html="article?.body" />
  </main>
</template>

※ bodyの表示で利用されている v-html はXSSの危険性があるため、利用には注意が必要です。ここでは、Newtで管理している投稿情報を表示するものであり、不特定多数のユーザーが入力できるものを表示するわけではないため、安全なものとして利用しています。
また、v-htmlを利用すると、eslintの警告が出る場合があるため <!-- eslint-disable-next-line vue/no-v-html --> を記載しています。

これで、投稿詳細についての設定も完了です。
http://localhost:3000/articles/article-3 にアクセスして、以下のように投稿詳細が表示されれば成功です。

nuxt3-blog3.png

注意点

4-5で投稿詳細へのリンクとして、Nuxtの推奨している NuxtLink を利用しています。
本番環境で nuxt generate を利用して、プリレンダリングを行えば問題なくページが作成されますが、開発環境の場合は一覧ページから詳細ページに遷移しても、ページが表示されません。

これは開発環境の場合、初期ロード時にサーバーサイドレンダリングが行われ、NuxtLink での遷移時はクライアントサイドレンダリングが実行されるためです。
このチュートリアルでは、スペースUIDやトークンの値をクライアントサイドから隠すために、Newtのクライアントを newt.server.ts ファイルで作成し、クライアントサイドでの読み込みを許可していません。そのため、ページ遷移をした時に投稿情報を取得できず、ページが表示されなくなってしまいます。

ページ遷移後にリロードを行えば問題なくページが表示されますが、ご注意ください。

次のステップ

このチュートリアルを行うことで、Nuxt3のプロジェクトを作成し、開発環境でコンテンツの取得・表示を行う方法を学習しました。
更に深く学習したい方は、以下のチュートリアルもおすすめです。

ホスティングを行いたい方

問い合わせフォームを作成したい方

その他にも様々なチュートリアルを用意しているので、ぜひ チュートリアル のページもご確認ください。

Newt Made in Newt