Next.js 14のApp RouterでhacoCMSを使う方法を、簡易ブログサイトの構築を例に順を追って説明します。
本チュートリアルでは、hacoCMSを使ってNext.js 14のApp Routerで簡易ブログサイトを構築する手順を説明します。
ただし、下記の内容については既知として詳しい説明を省略します。
また、Next.jsの詳細についてはNext.jsの公式ドキュメントをご確認ください。
本チュートリアルで作成するソースコードの完成形をGitHubに公開していますのでご活用ください。
お持ちのhacoCMSアカウントにプロジェクトを作成し、ブログ記事のAPIを以下のような設定で作成してください。APIの作成方法についてはhacoCMSのドキュメントをご確認ください。
記事(任意)entries
リスト形式
| # | フィールドタイプ | フィールド名(任意) | フィールド ID |
|---|---|---|---|
| 1 | テキストフィールド | タイトル | title |
| 2 | リッチテキスト | 本文 | body |
記事APIを作成できたら、適当な記事をいくつか作成し、公開してください。コンテンツの作成方法についてはhacoCMSのドキュメントをご確認ください。
簡易ブログサイトのNext.jsプロジェクトを作成します。下記のコマンドを実行してください。
$ npx create-next-app@14 tiny-blog --use-npm --example hello-world
※本チュートリアルはNext.js公式のサンプルプロジェクトhello-worldをベースにしています。
上記のコマンドを実行するとtiny-blogという名前のディレクトリが生成されます。説明のため、このディレクトリをプロジェクトディレクトリと呼ぶことにします。
プロジェクトディレクトリに移動し、下記のコマンドを実行してサイトを生成し、確認用のウェブサーバを起動してサイトの表示を確認してみましょう。
$ npm run build $ npm run start
npm run buildはサイトをビルドするコマンドです。npm run startはビルドしたサイトをホストするウェブサーバを起動するコマンドです。
npm run startを実行した状態で、ブラウザを開き http://localhost:3000/ にアクセスしてください。「Hello, Next.js!」と書かれたページが表示されることを確認してください。
本チュートリアルではhacoCMSのAPIをTypeScriptから呼び出すために、公式に提供しているSDK「hacocms-js-sdk」を使用します。下記のコマンドを実行してSDKをインストールしてください。
$ npm install hacocms-js-sdk
hacoCMS SDKでAPIコンテンツを扱うためには、APIスキーマをクラス化する必要があります。
Step 1で作成した記事APIの場合は、次のようなEntryクラスとして定義できます。プロジェクトディレクトリにlibディレクトリを作成して、その中に下記のコードをentry.tsの名前でファイル保存してください。
import { ApiContent, type JsonType } from 'hacocms-js-sdk'
/**
* 記事(entries)APIのコンテンツ
*/
export class Entry extends ApiContent {
/** タイトル */
title: string
/** 本文 */
body: string
constructor(json: JsonType<News>) {
super(json)
this.title = json.title
this.body = json.body
}
}
APIスキーマのクラスは、SDKで定義されているApiContentクラスを継承する必要があります。ApiContentクラスには、APIに共通して存在するコンテンツID(id)や公開日時(publishedAt)などのフィールドが定義されており、日時はパースされてDate型に変換されるようになっています。
コンストラクタの引数はJsonType<定義するクラス名>とする必要があります(JsonTypeもSDKで定義されています)。
なお、このステップ(クラス化)は、利用するAPI毎に行う必要があります。
SDKにはhacoCMSのAPIを使用するためのクライアントが用意されています。下記のコードをlibディレクトリの中にclient.tsという名前でファイル保存してください。
import { HacoCmsClient } from 'hacocms-js-sdk'
export const client = new HacoCmsClient(`https://${process.env.PROJECT_SUBDOMAIN}.hacocms.com`, process.env.PROJECT_ACCESS_TOKEN!)
上記のコードは、プロジェクトのサブドメインとアクセストークンを環境変数で渡すようになっています。
Next.jsには、プロジェクトディレクトリの.envという名前のファイルを環境変数として読み込む機能があります。下記を参考に、お使いのプロジェクトのサブドメインとアクセストークンを環境変数PROJECT_SUBDOMAINとPROJECT_ACCESS_TOKENの名前で設定する.envファイルを作成してください。
PROJECT_SUBDOMAIN=プロジェクトのサブドメイン PROJECT_ACCESS_TOKEN=アクセストークン
例えば、プロジェクトのサブドメインがdemo-testでアクセストークンがYourProjectAccessTokenである場合は、.envファイルの中身は以下のようになります。
PROJECT_SUBDOMAIN=demo-test PROJECT_ACCESS_TOKEN=YourProjectAccessToken
※アクセストークンはAPIを利用するためのパスワードに相当するため、公開してしまわないようにご注意ください。
Step 4とStep 5で準備したものを使って、簡易ブログサイトのコーディングをしていきます。
まず、サイトのトップページに最新記事をいくつか表示するようにします。app/page.tsxを開いて、次のように書き換えてください。
import { SortQuery } from 'hacocms-js-sdk'
import { client } from '../lib/client'
import { Entry } from '../lib/entry'
export default async function Page() {
const { data: recentEntries } = await client.getList<Entry>(Entry, '/entries', { s: SortQuery.build(['updatedAt', 'desc']), limit: 5 })
return (
<>
<h1>Tiny Blog</h1>
<ol>
{recentEntries.map(({ id, title, updatedAt }) => (
<li key={id}>
{`${title} at ${updatedAt.toString()}`}
</li>
))}
</ol>
</>
);
}
APIコンテンツの一覧を取得するためには、Step 5で定義したclientのgetListメソッドを呼び出します。その際、Step 4で定義したEntryクラスを第1引数と型引数に与えます。第2引数にはAPIのエンドポイントを、第3引数にはコンテンツ一覧取得APIのクエリパラメータのオブジェクトを必要に応じて指定します。この例では第3引数には更新日時(updatedAt)の降順(desc)にソートし、最大5件取得するように指定しています。
返り値のオブジェクトは、dataフィールドに記事コンテンツ(第1引数に指定したコンストラクタで構築されたEntryオブジェクト)のリストと、metaフィールドに該当件数等の情報が入ったオブジェクトで構成されています。
APIから取得したデータを利用して、トップページのコンポーネントを構築します(return文参照)。
ここで一度サイトをビルドし直して確認サーバを再起動し、hacoCMS上で公開した記事コンテンツがトップページに表示されることを確認しましょう。(Step 2で実行したコマンドは終了して)次のコマンドを実行してください。
$ npm run build $ npm run start
ブラウザを開き http://localhost:3000/ にアクセスし(あるいはStep 2で開いたものを再読み込みし)、hacoCMS上で公開した記事コンテンツのタイトルと更新日時が表示されていることを確認してください。
次に、記事ページを作成しましょう。記事ページのパスはコンテンツIDを使って/entry/[id]の形式とします。
appディレクトリの中にentryディレクトリを作成し、さらにその中に[id]ディレクトリを作成して、その中にpage.tsxを下記の内容で作成してください(作成したファイルのプロジェクトディレクトリからのパスはapp/entry/[id]/page.tsxとなります)。
import { client } from '../../../lib/client'
import { Entry } from '../../../lib/entry'
import Link from 'next/link'
/**
* 記事ページのパスに含まれるパラメータ `[id]` のリストを生成します。
*/
export async function generateStaticParams() {
return await client.getList<Entry>(Entry, '/entries').then(({ data }) => data.map(({ id }) => ({ id })))
}
export default async function Page({ params }: { params: Awaited<ReturnType<typeof generateStaticParams>>[number] }) {
const entry = await client.getContent<Entry>(Entry, '/entries', params.id)
return (
<>
<h1>{entry.title}</h1>
<div dangerouslySetInnerHTML={{ __html: entry.body }} />
<Link href="/">Back to Home</Link>
</>
)
}
generateStaticParams関数でページのパスに含まれるコンテンツIDのリストを生成しています。
コンテンツIDを指定してコンテンツを取得するにはclientのgetContentメソッド(コンテンツ取得API)を使います。型引数と第1引数にStep 4で定義したEntryクラスを、第2引数にAPIのエンドポイントを、第3引数に取得するコンテンツのコンテンツIDを指定します。
リッチテキストフィールドのHTMLをページに使用するには、上記のようにdangerouslySetInnerHTML属性に設定する必要があります。リッチテキストフィールドには任意のHTMLが挿入され得ることにご留意ください。
記事ページの表示を確認するために、hacoCMSの管理画面で記事APIを開き、公開状態のいずれかのコンテンツのコンテンツIDを控えてください。なおコンテンツIDは、コンテンツ一覧画面の「コンテンツID」の列や、コンテンツ編集画面の上部(公開ステータスの右隣)で確認できます。
再度ビルドを実行し、サーバを再起動してください。今度はhttp://localhost:3000/entry/ の直後に先ほど控えたコンテンツIDを貼り付けてアクセスし、当該記事のページが表示されることを確認してください。
最後に、トップページの記事一覧から各記事ページへリンクしましょう。
app/page.tsxを開き、Next.jsのLinkコンポーネントを以下のようにインポートしてください。
import Link from 'next/link'
続いて、記事のリスト要素(<li>)の中身を以下のように書き換えてください。
<Link href={`/entry/${id}`}>{title}</Link>
{` at ${updatedAt.toString()}`}
再度ビルドを実行し、サーバを再起動してトップページhttp://localhost:3000/ にアクセスしてください。記事コンテンツのタイトル部分にリンクが張られており、そのリンクをクリックすると当該記事ページに遷移することを確認してください。
以上で、hacoCMSを使った簡易ブログサイトが完成しました。なお、hacoCMSでのコンテンツ追加・変更・削除を反映させるためには、都度サイトをビルドし直す必要があります。
Next.jsのApp RouterでhacoCMSのSDKを使う基本的な方法を説明しました。
Next.jsのより詳しい使い方については、Next.jsの公式ドキュメントやチュートリアルをご覧ください。