アルパカログ

📅  2021-10-25

Notion Blogで過去の記事を遡れるようにページネーションを実装する


Notion Blog (というより Next.js) はユーザーがブログにアクセスした際、事前にビルドしておいた静的なページを返します。

Notion Blog で過去の記事の一覧ページを作ろうとすると、いくつか検討しなければならない問題があります。

このエントリでは Notion Blog で過去の記事一覧ページを実装する方法を紹介します。

過去記事一覧ページの仕様と検討事項

まず過去記事一覧ページの仕様から見ていきます。

  • トップページの記事一覧から過去記事一覧に遷移する
  • 過去記事一覧ページは記事が追加されるごとに内容が変わる(1件ずつずれていく)

このとき考えなければならない問題としては以下の点があります。

  • ページを静的生成するか?動的生成するか?
  • 事前に全てビルドしておくか?フォールバックを使うか?

動的生成(Server Side Rendering: SSR) であれば ?before=2021-09-25 のようなURLパラメータから必要な記事を動的に取得して表示すれば済みます。

しかしせっかく Next.js を使っているのに SSR を使うのはもったいない話です。どうせなら静的生成したページを使って高速に表示したいですよね。

ページを静的生成するとして、事前に全てビルドしておく(ページを決めておく)か、フォールバックを使うかも検討する必要があります。

フォールバックを使う方法とは fallback: true もしくは fallback: 'blocking' を指定し、ユーザーの初回リクエスト時にページを静的生成するやり方です。

2回目以降のリクエストではビルドしたページが返されます。ISR と併用することもできます。

ちなみに @chibicode さんによるとフォールバックを使う方法は Incremental Static Generation(ISG) で ISR とは異なるとのことです。

一方、事前にページを全てビルドしておくという方法も考えられますが、過去記事一覧ページはそもそも他のページに比べてアクセスされる頻度が少ないこと、パスを決めるために全ての日付を網羅するのは現実的ではないことから、フォールバックを使って都度生成するのが妥当に思えます。

詳しくは Next.js の fallback の説明を参照してください。

実装

まずはAPIクライアントに指定した日付よりも古い記事を取得するメソッドを実装していきましょう。

Notion API の Query a database を使います。実装は下記のようになります。

メソッドの最初でキャッシュを参照している箇所については、下記の記事を参照してください。

export async function getPostsBefore(date: string, pageSize: number = 10) {
  if (blogIndexCache.exists()) {
    const allPosts = await getAllPosts()
    return allPosts.filter(post => post.Date < date).slice(0, pageSize)
  }

  const params = {
    database_id: DATABASE_ID,
    filter: {
      and: [
        {
          property: 'Published',
          checkbox: {
            equals: true,
          },
        },
        {
          property: 'Date',
          date: {
            before: date,
          },
        },
      ],
    },
    sorts: [
      {
        property: 'Date',
        timestamp: 'created_time',
        direction: 'descending',
      },
    ],
    page_size: pageSize,
  }

  const data = await client.databases.query(params)

  return data.results.map(item => _buildPost(item))
}

次に過去記事一覧ページを実装します。日付を動的に受け取るために src/pages/blog/before/[date].tsx ファイルを追加します。実装は下記のようになります。

getStaticPaths() から見ていきましょう。

戻り値の fallback: 'blocking' でフォールバックを有効にしていることがわかります。

同じく戻り値の paths には1つのパス path = getBeforeLink(posts[posts.length - 1].Date) だけが指定されています。

これは、過去記事一覧ページの1ページ目だけは、アクセスされる頻度が高いということでビルド時に生成しているとうことです。

次に getStaticProps() を見ていきます。

ここで注目すべきは最初に date パラメータのバリデーションを行なっている点です。

Date.parse(date) によって日付として正当な文字列であること、 /\d{4}-\d{2}-\d{2}/.test(date)2021-09-25 のようなフォーマットであることを確認しています。

もし前者がなければ 2021-99-99 のようなありえない日付も通ってしまうので、どちらも必要になります。

メイン関数の「次のページ」リンクに注目してください。

{firstPost.Date !== posts[posts.length - 1].Date && (
  <div className={blogStyles.nextContainer}>
    <hr />
    <Link
      href="/blog/before/[date]"
      as={getBeforeLink(posts[posts.length - 1].Date)}
      passHref
    >
      <a className={blogStyles.nextButton}>次のページ ></a>
    </Link>
  </div>
)}

ここでは記事が最も古い記事かどうかを見てリンクを出し分けています。

リンクを表示する場合、今表示している最後の記事の Date[date] になるようにリンクしています。

これで、過去記事一覧ページの2ページ目以降のページは、最初のアクセス時にフォールバックにより生成されて paths に追加され、以降は ISR の対象となります。

フォールバックをうまく用いることでビルド時間の短縮や API Rate limits の回避にも役立ちます。

一方、意図しないパラメータが入力される可能性が出てくるので、きっちりバリデーションしておくことが大切です。

最後に Next.js の静的生成については下記の記事が詳細かつわかりやすいので参考にしてください。

以上です。

このエントリでは Notion Blog で過去の記事一覧ページを実装する方法を紹介しました。

同じカテゴリーの記事


おすすめ記事


最新記事


カテゴリー