microCMSのサンプルコードからNext.jsを学ぶの巻
こんにちは、Spacemarketのフロントエンドエンジニアの8zkです。
前回はmicroCMSの導入について触れましたが、肝心なNext.jsのtemplateのコードについては全く触れていなかったので、それについて学びつつアウトプットしたいと思い、ブログを書いています。
使用ライブラリ
ではさっそくですが シンプルなブログ
で使われているライブラリがこちらです。
"dependencies": {
"@types/cheerio": "^0.22.31",
"@types/node": "18.15.3",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"cheerio": "^1.0.0-rc.12",
"date-fns": "^2.29.3",
"date-fns-tz": "^2.0.0",
"encoding": "^0.1.13",
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
"eslint-config-prettier": "^8.7.0",
"highlight.js": "^11.7.0",
"microcms-js-sdk": "^2.3.2",
"next": "^13.4.4",
"prettier": "^2.8.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "4.9.5"
}
この中で特に注目すべきライブラリは microcms-js-sdk
です。
これはmicroCMSのAPIをcallするためのclientや型定義が含まれており、これなしにmicroCMSを使うのは中々難しいと思います。
microcms-js-sdkの使い方
コードの中ではこのように使われています。
import { createClient } from 'microcms-js-sdk';
import type {
MicroCMSQueries,
MicroCMSImage,
MicroCMSDate,
MicroCMSContentId,
} from 'microcms-js-sdk';
import { notFound } from 'next/navigation';
...
export type Blog = {
title: string;
description: string;
content: string;
thumbnail?: MicroCMSImage;
tags?: Tag[];
writer?: Writer;
};
...
// Initialize Client SDK.
export const client = createClient({
serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
apiKey: process.env.MICROCMS_API_KEY,
});
// ブログ一覧を取得
export const getList = async (queries?: MicroCMSQueries) => {
const listData = await client
.getList<Blog>({
endpoint: 'blog',
queries,
})
.catch(notFound);
return listData;
};
// ブログの詳細を取得
export const getDetail = async (contentId: string, queries?: MicroCMSQueries) => {
const detailData = await client
.getListDetail<Blog>({
endpoint: 'blog',
contentId,
queries,
})
.catch(notFound);
return detailData;
};
基本的には createClient
を使ってclientを作成し、それをwrapした関数を作るイメージです。
getList
は引数に queries?: MicroCMSQueries
を取ります。
これは一般的なoptionである limit
や offset
などを設定することができます。
そのため引数次第で様々なデータを取得することが可能になります。
また client
には複数のデータを取得する client.getList
と単一のデータを取得する client.getListDetail
があります。
これらのメソッドの引数に endpoint
を指定し、対象となるリソースを確定させます。
また client.getListDetail
に限り contentId
を指定することで対象となるcontentを取得します。
microCMSのAPIをcallしてみた(getList編)
では実際にmicroCMSのAPIをcallしているコードを見てみましょう。
// app/page.tsx
import { getList } from '@/libs/microcms';
import { LIMIT } from '@/constants';
import Pagination from '@/components/Pagination';
import ArticleList from '@/components/ArticleList';
export const revalidate = 60;
export default async function Page() {
const data = await getList({
limit: LIMIT,
});
return (
<>
<ArticleList articles={data.contents} />
<Pagination totalCount={data.totalCount} />
</>
);
}
こちらのファイルはNext.jsの app
ディレクトリ直下の page.tsx
です。
そして use client
を使用していないのでRSC(React Server Components)として扱われます。
なのでこのコンポーネントはserver側で getList
をcallすることになります。
data.contents
が実際のdataの実体です。
data.totalCount
は取得できた data.contents
のlengthではなく、全てのデータの数を表しているため、Paginationを作る時にとても便利です。
microCMSのAPIをcallしてみた(getListDetail編)
こちらのファイルは先ほどとは違い、app/articles/[slug]/page.tsx
です。
...
import { getDetail } from '@/libs/microcms';
import Article from '@/components/Article';
type Props = {
params: {
slug: string;
};
searchParams: {
dk: string;
};
};
...
export default async function Page({ params, searchParams }: Props) {
const data = await getDetail(params.slug, {
draftKey: searchParams.dk,
});
return <Article data={data} />;
}
microCMSではドラフト機能があります。
この機能は名前の通り、下書き機能です。
現状の画面がこちらとなります。(URL: http://localhost:3000/articles/{slug}
)
管理画面からこの投稿を確認します。
この投稿を以下のように変更し、 公開
の代わりに 下書きを追加
をclickします。
元のURLではUIに特に変化はありません。
しかし下書きにしたことによってdraftKey
が生成されたので、http://localhost:3000/articles/{slug}?dk={draftKey}
にアクセスすると・・・
ちゃんと下書き状態の画面が確認できます。
この機能のすごいところは投稿後でも下書きを追加でき、それらを別のURLで管理しているところです。とても良い!
コード的には getDetail
に draftKey: searchParams.dk,
を渡すだけですね!
検索機能 on microCMS
このサイトには検索機能があります。
UIとしては以下になります。
こちらに検索文字列を入力し、Enterを押すと http://localhost:3000/search?q={keyword}
なURLに遷移します。
そして検索結果に応じた投稿が取得できます。
コードは以下になります。
import { getList } from '@/libs/microcms';
import ArticleList from '@/components/ArticleList';
import Pagination from '@/components/Pagination';
type Props = {
searchParams: {
q?: string;
};
};
export const revalidate = 60;
export default async function Page({ searchParams }: Props) {
const data = await getList({
q: searchParams.q,
});
return (
<>
<ArticleList articles={data.contents} />
<Pagination totalCount={data.totalCount} basePath="/search" q={searchParams.q} />
</>
);
}
getList
には q
というパラメーターがあり、ここに searchParams.q
を渡すことで検索機能を実装しています。
このように getList
や getListDetail
には様々なoptionがあるので、データ取得をより便利にさせています。
感想
個人的には普段テンプレートの中身を見ることなんて殆どありませんが、たまに見てみると結構勉強になったのでよかったです。
またブログサービスはログイン情報と紐づかないことが多いのでRSCとは相性が良いなと感じました。
さいごに
スペースマーケットでは、一緒にサービスを成長させていく仲間を探しています。
話を聞いてみたい、ちょっとだけ興味がある、などでも大歓迎です!
ご興味ありましたら是非ご連絡ください!
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion