NewtとNext.js (App Router) を利用してブログを作成する
Table of contents
- 記事内で使用している主なソフトウェアのバージョン
- 概要
- 1. Next.jsのセットアップ
- 2. Newtのセットアップ
- 2-1. Appを追加する
- 2-2. スペースUID・App UID・モデルUIDを確認する
- 2-3. Newt CDN API Tokenを作成する
- 3. リクエストの準備
- 3-1. 環境変数の設定
- 3-2. newt-client-jsのインストール
- 3-3. APIクライアントの作成
- 3-4. サーバー上でのみ実行されるよう制限する
- 4. 一覧ページの作成
- 4-1. 言語の設定をする
- 4-2. 投稿の型を定義する
- 4-3. 投稿一覧の取得メソッドを作成する
- 4-4. 投稿一覧を表示する
- 5. 詳細ページの作成
- 5-1. 投稿詳細の取得メソッドを作成する
- 5-2. 投稿詳細を表示する
- 次のステップ
このチュートリアルでは、Newtと Next.js を利用して、ブログを作成する手順を紹介します。
具体的には、Newtで管理しているコンテンツの一覧ページと詳細ページを作る手順を紹介します。
Next.jsはApp Routerを使用します。
記事内で使用している主なソフトウェアのバージョン
- Next.js(
next
): 14.2.3 - newt-client-js(
newt-client-js
): 3.3.2
概要
Next.jsでプロジェクトを作成し、Newtのコンテンツ情報を取得できるようにします。
コンテンツの一覧ページ(パス: /
)と詳細ページ(パス: /articles/:slug
。slugがarticle-1の場合は /articles/article-1
)を作成し、ローカル環境で表示を行うまでを説明します。
また、ここではビルド時にプリレンダリングする、静的レンダリング(Static Rendering)の方法を紹介します。
1. Next.jsのセットアップ
はじめに、Next.jsのセットアップを行います。Create Next App を利用することで、簡単にNext.jsのプロジェクトを作成できます。
以下のどちらかのコマンドを入力します。
npx create-next-app@latest
# or
yarn create next-app
コマンドを入力すると、以下の質問を聞かれるので、お好きな設定を選びましょう。
- プロジェクト名(ここでは
nextjs-blog
としました) - TypeScriptを利用するか(ここでは
Yes
を選択) - ESLintを利用するか(ここでは
Yes
を選択) - Tailwind CSSを利用するか(ここでは
No
を選択) - src/ ディレクトリを使用するか(ここでは
No
を選択) - App Routerを使用するか(ここでは
Yes
を選択) - デフォルトのインポートエイリアスの設定をカスタマイズするか(ここでは
Yes
を選択) - インポートエイリアスの設定をどうするか(ここでは
@/*
を選択)
yarnを利用した場合、以下のように表示されます。
$ yarn create next-app
yarn create v1.22.19
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Installed "create-next-app@14.2.3" with binaries:
- create-next-app
✔ What is your project named? … nextjs-blog
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /Users/foo/bar/nextjs-blog.
作成したプロジェクトに移動して、開発サーバーを立ち上げます。
$ cd nextjs-blog
$ yarn dev
http://localhost:3000
にアクセスして、以下のような画面が表示されることを確認します。
2. Newtのセットアップ
次にNewtにコンテンツとAPIトークンを用意し、コンテンツの取得を行うための準備を行います。
2-1. Appを追加する
「Appを追加」をクリックして「テンプレートから追加」を選択します。
表示されるテンプレートの中から「Blog」を選択して、「このテンプレートを追加」をクリックします。
テンプレートが追加されると、「投稿データ」「タグデータ」「著者データ」が追加されます。
2-2. スペースUID・App UID・モデルUIDを確認する
スペースUIDは「スペース設定」から確認できます。
上記の例だと、スペースUIDは sample-for-docs
となります。
この値は3-1で環境変数として定義します。
また「Blog」テンプレートを追加した場合、App UIDは blog
、「投稿データ」モデルUIDは article
となります。
これらの値は、4-3や5-1で投稿情報を取得する際に利用します。
2-3. Newt CDN API Tokenを作成する
続いて、APIリクエストに必要なトークンを発行します。
スペース設定 > APIキー のページからNewt CDN API Tokenを作成します。
名前と取得対象を決めて「作成」を押します。
ここで作成したトークンの値は3-1で環境変数として定義します。
3. リクエストの準備
Newtの SDK を利用することで、NewtのAPIをより簡単に利用できます。
ここではSDKを利用して、NewtのAPIクライアントを作成します。
3-1. 環境変数の設定
Next.jsには環境変数のビルトインサポートがあり、.env.local
を使用して、環境変数をロードできます。Next.jsの環境変数について、詳細は Environment Variables のドキュメントをご確認ください。
ここでは、.env.local
ファイルを作成し、2-2で確認したスペースUID、2-3で作成したトークンの値を定義します。以下を、実際の値で置き換えて定義してください。
1NEWT_SPACE_UID=sample-for-docs
2NEWT_CDN_API_TOKEN=xxxxxxxxxxxxxxx
上記のように定義しておくと、process.env.NEWT_SPACE_UID
や process.env.NEWT_CDN_API_TOKEN
として利用できるようになります。
3-2. newt-client-jsのインストール
次に newt-client-js をインストールします。
npm install newt-client-js
# or
yarn add newt-client-js
3-3. APIクライアントの作成
CDN APIを利用するためのクライアントを作成します。
spaceUid
と token
のところには3-1で設定した環境変数を入力します。
ここではCDN APIを利用するので、apiType
には cdn
を指定しましょう。
1import { createClient } from 'newt-client-js'
2
3const client = createClient({
4 spaceUid: process.env.NEWT_SPACE_UID + '',
5 token: process.env.NEWT_CDN_API_TOKEN + '',
6 apiType: 'cdn',
7})
3-4. サーバー上でのみ実行されるよう制限する
このAPIクライアントはサーバー上でのみ実行することを想定しています。
そこで、server-only を利用して、誤ってクライアントコンポーネントにインポートした場合、エラーを発生させるようにします。
詳細はNext.jsの server-only のドキュメントをご確認ください。
まずインストールします。
npm install server-only
# or
yarn add server-only
次に lib/newt.ts
のコードを修正します。
import 'server-only'
import { createClient } from 'newt-client-js'
const client = createClient({
spaceUid: process.env.NEWT_SPACE_UID + '',
token: process.env.NEWT_CDN_API_TOKEN + '',
apiType: 'cdn',
})
これで、NewtにAPIリクエストを送るための準備ができました。
4. 一覧ページの作成
4-1. 言語の設定をする
app/layout.tsx
にある lang 属性を修正します。ここでは日本語 ja
を指定します。
(省略)
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<html lang="ja">
<body className={inter.className}>{children}</body>
</html>
)
}
4-2. 投稿の型を定義する
投稿の型 Article
を定義します。
このチュートリアルでは、_id
・title
・slug
・body
のみを使うため、以下のように定義しておきます。
1export interface Article {
2 _id: string
3 title: string
4 slug: string
5 body: string
6}
4-3. 投稿一覧の取得メソッドを作成する
投稿一覧を取得するために、lib/newt.ts
のファイルに getArticles
メソッドを追加します。
SDKが提供している getContents メソッドはNewtのコンテンツ一覧を取得するためのメソッドです。getContentsのパラメータに Article
の型を渡すことで、返却される items
の型として Article[]
が指定されます。
また、selectパラメータを利用して、取得するフィールドを _id
・title
・slug
・body
のみに制限します。
import 'server-only'
import { createClient } from 'newt-client-js'
import type { Article } from '@/types/article'
const client = createClient({
spaceUid: process.env.NEWT_SPACE_UID + '',
token: process.env.NEWT_CDN_API_TOKEN + '',
apiType: 'cdn',
})
export const getArticles = async () => {
const { items } = await client.getContents<Article>({
appUid: 'blog',
modelUid: 'article',
query: {
select: ['_id', 'title', 'slug', 'body'],
},
})
return items
}
1でインポートエイリアスに @/*
を指定したので、Article
のパスに @/types/article
と指定しています。
また、余計なリクエストを送らないように、Reactの cache()
でラップしておきます。
詳細はNext.jsの React cache() のドキュメントをご確認ください。
import 'server-only'
import { createClient } from 'newt-client-js'
import { cache } from 'react'
import type { Article } from '@/types/article'
const client = createClient({
spaceUid: process.env.NEWT_SPACE_UID + '',
token: process.env.NEWT_CDN_API_TOKEN + '',
apiType: 'cdn',
})
export const getArticles = async () => {
export const getArticles = cache(async () => {
const { items } = await client.getContents<Article>({
appUid: 'blog',
modelUid: 'article',
query: {
select: ['_id', 'title', 'slug', 'body'],
},
})
return items
}
})
※ NewtのCDN APIでは認証に Authorization
ヘッダを利用していますが、Next.jsの fetch() は Authorization
ヘッダを利用する場合はキャッシュされないため、ここではReactの cache()
を利用します。
4-4. 投稿一覧を表示する
Next.jsではファイルシステムベースのルーティングを採用しており、app
ディレクトリの配下にフォルダを作成して、ルートを定義します。そしてpageファイルを作成することで、ルートを公開します。
例えば、以下のようにpageファイルとURLが対応します。
app/dashboard/page.tsx
→/dashboard
app/dashboard/settings/page.tsx
→/dashboard/settings
※ ルーティングの詳細については、Next.jsの Routing Fundamentals のドキュメントをご確認ください。
ここではトップページ(パス: /
)で投稿一覧を表示したいので、app/page.tsx
のファイルを修正します。
4-3で作成した getArticles
を利用して、以下のように書き換えます。
1import Link from 'next/link'
2import { getArticles } from '@/lib/newt'
3import styles from '@/app/page.module.css'
4import type { Metadata } from 'next'
5
6export const metadata: Metadata = {
7 title: 'Newt・Next.jsブログ',
8 description: 'NewtとNext.jsを利用したブログです',
9}
10
11export default async function Home() {
12 const articles = await getArticles()
13 return (
14 <main className={styles.main}>
15 <ul>
16 {articles.map((article) => {
17 return (
18 <li key={article._id}>
19 <Link href={`articles/${article.slug}`}>{article.title}</Link>
20 </li>
21 )
22 })}
23 </ul>
24 </main>
25 )
26}
ここでは、getArticles
で取得した投稿一覧を表示しています。
また、メタデータを設定するために metadata
をエクスポートしています。詳細についてはNext.jsの Metadata のドキュメントをご確認ください。
http://localhost:3000/
にアクセスして、以下のように投稿一覧が表示されれば成功です。
5. 詳細ページの作成
5-1. 投稿詳細の取得メソッドを作成する
投稿詳細を取得するために、lib/newt.ts
のファイルに getArticleBySlug
メソッドを追加します。
SDKが提供している getFirstContent メソッドはクエリに該当するコンテンツのうち、最初の1件を返却するメソッドです。指定したスラッグのコンテンツを取得したい場合は、このメソッドを利用します。
こちらも getArticles
と同様に、cache()
でラップしておきます。
import 'server-only'
import { cache } from 'react'
import { createClient } from 'newt-client-js'
import type { Article } from '@/types/article'
const client = createClient({
spaceUid: process.env.NEWT_SPACE_UID + '',
token: process.env.NEWT_CDN_API_TOKEN + '',
apiType: 'cdn',
})
export const getArticles = cache(async () => {
const { items } = await client.getContents<Article>({
appUid: 'blog',
modelUid: 'article',
query: {
select: ['_id', 'title', 'slug', 'body'],
},
})
return items
})
export const getArticleBySlug = cache(async (slug: string) => {
const article = await client.getFirstContent<Article>({
appUid: 'blog',
modelUid: 'article',
query: {
slug,
select: ['_id', 'title', 'slug', 'body'],
},
})
return article
})
5-2. 投稿詳細を表示する
Next.jsでは [param]
のようにしてページ名に角括弧を使うことで動的なルーティング(Dynamic Routes)を作成できます。ここでは、/articles/:slug
(/articles/article-1
など)のパスで投稿の詳細を表示したいので、app/articles/[slug]/page.tsx
のファイルを作成します。
以下のように記載します。
1import { getArticles, getArticleBySlug } from '@/lib/newt'
2import styles from '@/app/page.module.css'
3import type { Metadata } from 'next'
4import type { Article } from '@/types/article'
5
6type Props = {
7 params: {
8 slug: string
9 }
10}
11
12export async function generateStaticParams() {
13 const articles = await getArticles()
14 return articles.map((article) => ({
15 slug: article.slug,
16 }))
17}
18export const dynamicParams = false
19
20export async function generateMetadata({ params }: Props): Promise<Metadata> {
21 const { slug } = params
22 const article = await getArticleBySlug(slug)
23
24 return {
25 title: article?.title,
26 description: '投稿詳細ページです',
27 }
28}
29
30export default async function Article({ params }: Props) {
31 const { slug } = params
32 const article = await getArticleBySlug(slug)
33 if (!article) return
34
35 return (
36 <main className={styles.main}>
37 <h1>{article.title}</h1>
38 <div dangerouslySetInnerHTML={{ __html: article.body }} />
39 </main>
40 )
41}
以下のことを行っています。
- generateStaticParams を利用して、リクエスト時に動的にルートを生成するのではなく、ビルド時に静的にルートを生成しています。今回の場合は、全投稿のスラッグを定義します
- dynamicParams を
false
に設定して、もしgenerateStaticParams
で定義されていないパスにアクセスされた場合、404
を返すようにします - 投稿一覧と異なり、ここではメタデータを動的に生成しています。そのため generateMetadata を利用して、
title
とdescription
を設定しています getArticleBySlug
で取得した投稿から、詳細ページを表示しています
※ bodyの表示で利用されている dangerouslySetInnerHTML はXSSの危険性があるため、利用には注意が必要です。ここでは、Newtで管理している投稿情報を表示するものであり、不特定多数のユーザーが入力できるものを表示するわけではないため、安全なものとして利用しています。
これで、投稿詳細についての設定も完了です。
http://localhost:3000/articles/article-3
にアクセスして、以下のように投稿詳細が表示されれば成功です。
次のステップ
このチュートリアルを行うことで、Next.jsのプロジェクトを作成し、開発環境でコンテンツの取得・表示を行う方法を学習しました。
更に深く学習したい方は、以下のチュートリアルもおすすめです。
Vercelと接続してホスティングを行いたい方
→ GitHubのリポジトリとVercelを接続して、ホスティングする
プレビュー環境を作成したい方
→ Next.jsのプレビューモードを利用して、プレビュー環境を作成する
問い合わせフォームを作成したい方
→ NewtとNext.jsを利用して、問い合わせフォームを作成する
その他にも様々なチュートリアルを用意しているので、ぜひ チュートリアル のページもご確認ください。