🚀

【Next.js和訳】Basic Features/Data Fetching

2021/10/02に公開約27,400字

この記事について

この記事は、Basic Features/Data Fetchingの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Data Fetching

Pages のドキュメントでは、Next.js には 2 つのプリレンダリングの形式があると説明しました。静的生成サーバーサイドレンダリングです。このページでは、それぞれのケースに応じたデータフェッチの方法について詳しく説明します。まだお読みになっていない方は、まずPages のドキュメントに目を通していただくことをお勧めします。

プリレンダリングのためにデータを取得する際に使用できる、Next.js 独自の 3 つの関数について説明します:

  • getStaticProps(静的生成):ビルド時にデータをフェッチします。
  • getStaticPaths(静的生成):データに基づいてページを事前レンダリングするための動的ルートを指定します。
  • getServerSideProps(サーバーサイドレンダリング):リクエストごとにデータを取得します。

さらに、クライアント側でデータをフェッチする方法について簡単に説明します。

getStaticProps(静的生成)

バージョン履歴
バージョン 変更
v10.0.0 localelocalesdefaultLocale、およびnotFoundオプションが追加されました。
v9.5.0 安定した増産のための静的再生成
v9.3.0 getStaticPropsが導入された。

ページからgetStaticPropsという非同期関数をエクスポートすると、Next.js はビルド時にgetStaticPropsが返すpropsを使ってこのページをプリレンダリングします。

export async function getStaticProps(context) {
  return {
    props: {}, // propsとしてページコンポーネントに渡されます。
  }
}

contextパラメータは次のキーを含むオブジェクトです:

  • paramsには、動的ルートを使用するページのルートパラメータが含まれます。例えば、ページ名が[id].jsの場合、params{ id: ... }のようになります。詳細については、Dynamic Routing のドキュメントをご覧ください。この機能は、後で説明するgetStaticPathsと一緒に使うべきです。
  • previewは、ページがプレビューモードになっている場合はtrue、そうでない場合はundefinedです。プレビューモードのドキュメントを参照してください。
  • previewDataには、setPreviewDataで設定されたプレビューデータが格納されます。プレビューモードのドキュメントを参照してください。
  • localeは、有効なロケールを含みます(有効な場合)。
  • localesは、サポートされているすべてのロケールを含みます(有効な場合)。
  • defaultLocaleには、設定されたデフォルトのロケールが含まれます(有効な場合)。

getStaticPropsは、次のようなオブジェクトを返す必要があります:

  • props - ページコンポーネントが受け取る props を持つオプショナルなオブジェクトです。シリアル化可能なオブジェクトである必要があります。
  • revalidate - ページの再生成が発生するまでの秒単位のオプショナルな量です。デフォルトではfalseです。revalidatefalseの場合は、再検証が行われないことを意味し、次回のビルドまでビルドされた状態でページがキャッシュされます。増産のための静的再生成の詳細はこちら。
  • notFound - ページが 404 ステータスとページを返すことを許可するオプショナルなブール値です。以下は、その動作例です:
export async function getStaticProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

  return {
    props: { data }, //プロップスとしてページコンポーネントに渡されます。
  }
}
  • redirect - 内部および外部リソースへのリダイレクトを可能にするオプショナルなリダイレクト値です。これは{ destination: string, permanent: boolean }の形と一致している必要があります。ごくまれに、古い HTTP クライアントが適切にリダイレクトするために、カスタムステータスコードを割り当てる必要がある場合があります。このような場合には、permanentプロパティの代わりにstatusCodeプロパティを使用することができますが、両方を使用することはできません。以下に、その例を示します。

www.DeepL.com/Translator(無料版)で翻訳しました。

export async function getStaticProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!data) {
    return {
      redirect: {
        destination: "/",
        permanent: false,
      },
    }
  }

  return {
    props: { data }, //プロップスとしてページコンポーネントに渡されます。
  }
}

ここでは、getStaticPropsを使って CMS(コンテンツマネジメントシステム)からブログ記事のリストを取得する例を紹介します。この例はPages のドキュメントにも掲載されています。

// postsはビルド時にgetStaticProps()によって入力されます。
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}
// この関数はビルド時にサーバーサイドで呼ば出されます。
// クライアントサイドでは呼ば出されないので、
// 直接データベースに問い合わせることもできます。"技術的な詳細" セクションを参照してください。
export async function getStaticProps() {
  // postsを取得するために外部APIエンドポイントを呼び出します。
  // 任意のデータフェッチライブラリを使用することができます。
  const res = await fetch("https://.../posts")
  const posts = await res.json()
  // { props: { posts } } を返すことによって、Blogコンポーネントは
  // ビルド時にpropとして`posts`を受け取ります。
  return {
    props: {
      posts,
    },
  }
}
export default Blog

getStaticPropsはどのような場合に使用するのですか?

次の場合にgetStaticPropsを使用します:

  • ページを表示するために必要なデータは、ユーザーのリクエスト以前の、構築時から利用可能です。
  • データはヘッドレス CMS から取得されます。
  • データはパブリックにキャッシュできます(ユーザーに依存しません)。
  • ページは(SEO のために)事前にレンダリングされ、非常に高速である必要があります—getStaticPropsは、HTML と JSON のファイルを生成し、パフォーマンスを向上させるためにどちらも CDN によってキャッシュされます。

TypeScript:GetStaticPropsの使用

TypeScript の場合は、nextGetStaticProps型が使えます。

import { GetStaticProps } from "next"
export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}

props の推測された型を取得したい場合はInferGetStaticPropsType<typeof getStaticProps>、次のように使用できます:

import { InferGetStaticPropsType } from 'next'
type Post = {
  author: string
  content: string
}
export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()
  return {
    props: {
      posts,
    },
  }
}
function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  // ポストをPost[]型に解決します。
}
export default Blog

増産のための静的再生成

バージョン履歴
バージョン 変更
v9.5.0 ベースパスが追加されました。

Next.js を使用すると、サイトを構築した後に静的ページを作成または更新できます。増産のための静的再生成(ISR)を使用すると、サイト全体を再構築しなくても、ページごとに静的生成を使用できます。ISR を使用すると、数百万ページにスケーリングしながら、静的であることの利点を維持できます。

前のgetStaticPropsの例を考えてみましょう。ただし、revalidateプロパティを通じて ISR が有効になっています。

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}
// この関数はビルド時にサーバーサイドで呼び出されます。
// 再検証が有効になっていて、新しいリクエストが入ってきた場合、
// サーバーレス関数で再び呼び出されることがあります。
export async function getStaticProps() {
  const res = await fetch("https://.../posts")
  const posts = await res.json()
  return {
    props: {
      posts,
    },
    // Next.jsは、ページの再生成を試みます。
    // - リクエストが入ってきたとき
    // - 最大で10秒に1回
    revalidate: 10, // In seconds
  }
}
// この関数はビルド時にサーバーサイドで呼び出されます。
// パスが生成されていなければ、
// サーバーレス関数で再び呼び出されることがあります。
export async function getStaticPaths() {
  const res = await fetch("https://.../posts")
  const posts = await res.json()
  // postsに基づいてプリレンダリングしたいパスを取得します。
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
  // ビルド時にこれらのパスのみをプリレンダリングします。
  // { fallback: blocking }は、パスが存在しない場合はオンデマンドで、
  // そうでなければサーバーサイドでページをレンダリングします。
  return { paths, fallback: "blocking" }
}
export default Blog

ビルド時に事前にレンダリングされたページに対してリクエストが行われると、最初はキャッシュされたページが表示されます。

  • 最初のリクエストの後、10 秒前のページへのリクエストもキャッシュされ、瞬時に行われます。
  • 10 秒後に次のリクエストをすると、キャッシュされた(古い)ページが表示されます。
  • Next.js は、バックグラウンドでページの再生成をトリガーします。
  • ページが正常に生成されると、Next.js はキャッシュを無効にし、更新された製品ページを表示します。バックグラウンドの再生成が失敗した場合、古いページは変更されません。

生成されていないパスに対してリクエストが行われると、Next.js は最初のリクエストでページをサーバーレンダリングします。今後のリクエストでは、キャッシュから静的ファイルが提供されます。

グローバルにキャッシュを保持し、ロールバックを処理する方法については、ISRを参照してください。

ファイルの読み取り:process.cwd()の使用

ファイルはgetStaticPropsでファイルシステムから直接読み取ることができます。

そのためには、ファイルへのフルパスを取得する必要があります。

Next.js はコードを別のディレクトリにコンパイルするため、__dirnameを使用することはできません。返されるパスは、pages ディレクトリとは異なります。

その代わり、process.cwd()を使うと、Next.js が実行されているディレクトリを知ることができます。

import { promises as fs } from "fs"
import path from "path"
// postsはビルド時にgetStaticProps()によって入力されます。
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>
          <h3>{post.filename}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  )
}
// この関数はビルド時にサーバーサイドで呼び出されます。
// クライアントサイドでは呼ば出されないので、
// 直接データベースに問い合わせることもできます。"技術的な詳細" セクションを参照してください。
export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), "posts")
  const filenames = await fs.readdir(postsDirectory)
  const posts = filenames.map(async (filename) => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = await fs.readFile(filePath, "utf8")
    // 通常、コンテンツを解析/変換します。
    // 例えば、以下のようにマークダウンをHTMLに変換することができます。
    return {
      filename,
      content: fileContents,
    }
  })
  // { props: { posts } } を返すことで、Blogコンポーネントは
  // ビルド時にpropとして`posts`を受け取ります。
  return {
    props: {
      posts: await Promise.all(posts),
    },
  }
}
export default Blog

技術的な詳細

ビルド時にのみ実行

getStaticPropsはビルド時に実行されるため、静的な HTML を生成する際のクエリパラメータや HTTP ヘッダなど、リクエスト時にのみ利用可能なデータは受け取りません

サーバー側のコードを直接書き込む

getStaticPropsは、サーバーサイドでのみ実行されることに注意してください。クライアント側で実行されることはありません。また、ブラウザの JS バンドルにも含まれません。つまり、ブラウザに送信せずに、直接データベースクエリなどのコードを記述できます。
getStaticPropsからAPI ルートを取得してはいけません。代わりに、サーバーサイドのコードを直接getStaticPropsに書いてください。

このツールを使用して、Next.js がクライアント側バンドルから何を削除するかを確認できます。

HTML と JSON 両方の静的生成

ビルド時にgetStaticPropsを使用したページがプリレンダリングされると、 そのページの HTML ファイルに加えて、Next.js はgetStaticPropsの実行結果を保持する JSON ファイルを生成します。

この JSON ファイルは、next/linkドキュメント)やnext/routerドキュメント)によるクライアントサイドのルーティングで使用されます。getStaticPropsを使ってプリレンダリングされたページに移動すると、Next.js はこの JSON ファイル(ビルド時にあらかじめ計算されたもの)を取得し、ページコンポーネントの props として使用します。これは、エクスポートされた JSON のみが使用されるため、クライアントサイドのページ遷移ではgetStaticPropsが呼び出されないことを意味します。

ISR を使用すると、getStaticPropsが帯域外で実行され、クライアントサイドのナビゲーションに必要な JSON が生成されます。これは、同じページに対して複数のリクエストが行われるという形で表示される場合がありますが、これは意図されたものであり、エンドユーザーのパフォーマンスには影響しません。

ページでのみ許可

getStaticPropsは、ページからのみエクスポートできます。ページ以外のファイルからはエクスポートできません。

この制限の理由の 1 つは、ページがレンダリングされる前に React が必要なすべてのデータを持っている必要があるためです。

また、export async function getStaticProps() {}を使用する必要があります。getStaticPropsをページコンポーネントのプロパティとして追加しても動作しません

開発中のすべてのリクエストで実行

開発中(next dev)では、すべてのリクエストでgetStaticPropsが呼び出されます。

プレビューモード

場合によっては、静的生成を一時的にバイパスして、ビルド時ではなくリクエスト時にページをレンダリングしたい場合があります。たとえば、ヘッドレス CMS を使用していて、ドラフトを公開する前にプレビューしたい場合があります。

このユースケースは、プレビューモードと呼ばれる機能によって Next.js によってサポートされています。詳細については、プレビューモードのドキュメントをご覧ください。

getStaticPaths (静的生成)

バージョン履歴
バージョン 変更
v9.5.0 安定した増産のための静的再生成
v9.3.0 getStaticPathsが導入された。

ページが動的なルート(ドキュメント)を持ち、getStaticPropsを使用する場合、ビルド時に HTML にレンダリングされなければならないパスのリストを定義する必要があります。

動的なルートを使用しているページから、getStaticPathsというasync関数をエクスポートすると、Next.js はgetStaticPathsで指定されたすべてのパスを静的にプリレンダリングします。

export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // 以下の"paths"セクションをご覧ください
    ],
    fallback: true, false, or 'blocking' // 以下の"fallback"セクションをご覧ください。
  };
}

pathsキー(必須)

pathsキーは、どのパスがプリレンダリングされるかを決定します。例えば、pages/posts/[id].jsという動的なルートを使用するページがあるとします。このページからgetStaticPathsをエクスポートして、pathsに対して次のように返す場合:

return {
  paths: [
    { params: { id: '1' } },
    { params: { id: '2' } }
  ],
  fallback: ...
}

そして、Next.js は、pages/posts/[id].jsのページコンポーネントを使って、ビルド時にposts/1posts/2を静的に生成します。

なお、各 params の値は、ページ名に使われているパラメータと一致する必要があります:

  • ページ名がpages/posts/[postId]/[commentId]の場合、paramsにはpostIdcommentIdを含める必要があります。
  • ページ名がpages/[...slug]のように可変長なルートを使用している場合、paramsには配列であるslugを含める必要があります。例えば、この配列が['foo', 'bar']であれば、Next.js は/foo/barにページを静的に生成します。
  • ページがオプショナルで可変長なルートを使用している場合は、null[]undefined、またはfalseを指定して、最も根元的なルートをレンダリングします。たとえば、pages/[[...slug]]slug: falseを指定すると、Next.js は / というページを静的に生成します。

fallbackキー(必須)

getStaticPathsが返すオブジェクトには、boolean のfallbackキーを含める必要があります。

fallback: false

fallbackfalseの場合、getStaticPathsで返されなかったパスは404 ページになります。プリレンダリングするパスの数が少ない場合は、ビルド時にすべてのパスを静的に生成することができます。また、新しいページが頻繁に追加されない場合にも有効です。データソースにアイテムを追加し、新しいページをレンダリングする必要がある場合は、再度ビルドを実行する必要があります。

ここでは、pages/posts/[id].jsというページごとに 1 つのブログ記事をプリレンダーする例を紹介します。ブログ記事のリストは CMS から取得され、getStaticPathsで返されます。そして、各ページごとに、getStaticPropsを使って CMS から投稿データを取得します。この例は、Pages のドキュメントにもあります。

pages/posts/[id].js
function Post({ post }) {
  // Render post...
}
// この関数はビルド時に呼び出されます
export async function getStaticPaths() {
  // postsを取得するために外部APIエンドポイントを呼び出します
  const res = await fetch('https://.../posts')
  const posts = await res.json()
  // postsに基づいてプリレンダリングを行いたいパスを取得する
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
  // ビルド時にこれらのパスのみをプリレンダリングします。
  // `{ fallback: false }`は他のルートが404になることを意味します。
  return { paths, fallback: false }
}
// これはビルド時にも呼び出されます。
export async function getStaticProps({ params }) {
  // paramsはpostの`id`を含みます。
  // ルートが/posts/1のような場合、params.idは1です。
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
  // postデータをpropsでページに渡す
  return { props: { post } }
}
export default Post

fallback: true

fallbacktrueの場合は、getStaticPropsの動作が変わります。

  • getStaticPathsから返されたパスは、ビルド時にgetStaticPropsによって HTML にレンダリングされます。
  • ビルド時に生成されていないパスは、404 ページにはなりません。その代わり、Next.js は、そのようなパスへの最初のリクエストに対して、「フォールバック」バージョンのページを提供します(詳細は後述の「フォールバックページ」を参照)。
  • バックグラウンドでは、Next.js がリクエストされたパスの HTML と JSON を静的に生成します。これにはgetStaticPropsの実行も含まれます。
  • これが完了すると、ブラウザは生成されたパスの JSON を受け取ります。これは、必要な props でページを自動的にレンダリングするために使用されます。 ユーザーの視点では、フォールバックページからフルページへとページが入れ替わることになります。
  • 同時に、Next.js はこのパスを事前にレンダリングされたページのリストに追加します。同じパスへの後続のリクエストは、ビルド時に事前にレンダリングされた他のページと同様に、生成されたページを提供します。

フォールバックページ

ページの「フォールバック」バージョン:

  • ページの props は空になります。
  • routerを使用すると、フォールバックがレンダリングされているかどうかを検出することができ、router.isFallbacktrueになります。

isFallbackを使用する例を次に示します。

pages/posts/[id].js
import { useRouter } from 'next/router'
function Post({ post }) {
  const router = useRouter()
  // ページがまだ生成されていない場合、これが表示されます。
  // 最初はgetStaticProps()の実行が終わるまで表示されます。
  if (router.isFallback) {
    return <div>Loading...</div>
  }
  // Render post...
}
// この関数はビルド時に呼び出されます
export async function getStaticPaths() {
  return {
    // ビルド時に`/posts/1`と`/posts/2`のみが生成されます
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // 追加ページを静的に生成可能
    // 例: `/posts/3`
    fallback: true,
  }
}
// これもビルド時に呼び出されます
export async function getStaticProps({ params }) {
  // paramsはpostの`id`を含みます。
  // ルートが/posts/1の場合、params.idは1です
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
  // postデータをpropsでページに渡す
  return {
    props: { post },
    // リクエストが来れば、
    // 最大で1秒に1回、ポストを再生成する。
    revalidate: 1,
  }
}
export default Post

fallback: trueはどのような場合に有効ですか?

fallback: trueは、データに依存する静的なページが非常に多いアプリの場合に有効です(大規模な e コマースサイトなど)。すべての商品ページをプリレンダリングしたいのですが、そうするとビルドに時間がかかってしまいます。

その代わりに、ページの小さなサブセットを静的に生成し、残りのページにはfallback: trueを使用することができます。誰かがまだ生成されていないページをリクエストすると、ユーザーにはローディング・インジケータ付きのページが表示されます。その後すぐに、getStaticPropsが終了し、リクエストされたデータでページがレンダリングされます。今後は、同じページをリクエストしたすべての人が、静的にプリレンダリングされたページを得ることができます。

これにより、高速なビルドと静的生成の利点を維持しながら、ユーザーが常に高速な体験をすることができます。

fallback: trueの場合、生成されたページは更新されません。これについては、ISRをご覧ください。

fallback: 'blocking'

fallback'blocking'の場合、getStaticPathsで返されなかった新しいパスは、SSR と同じように HTML が生成されるのを待ちます(だから blocking なのです)。そして、今後のリクエストのためにキャッシュされるので、パスごとに 1 回しか発生しません。

getStaticPropsは次のように動作します。

  • getStaticPathsから返されたパスは、ビルド時にgetStaticPropsによって HTML にレンダリングされます。
  • ビルド時に生成されていないパスは、404 ページにはなりません。その代わり、Next.js は最初のリクエストで SSR を行い、生成された HTML を返します。
  • それが完了すると、ブラウザは生成されたパスの HTML を受け取ります。ユーザーの視点では、「ブラウザがページを要求している」状態から「フルページが読み込まれた」状態に遷移します。読み込み/フォールバック状態のフラッシュはありません。
  • 同時に、Next.js はこのパスを事前にレンダリングされたページのリストに追加します。同じパスへの後続のリクエストは、ビルド時に事前にレンダリングされた他のページと同様に、生成されたページを提供します。

fallback: 'blocking' は、デフォルトでは生成されたページを更新しません。生成されたページを更新するには、fallback: 'blocking' と併せてISRを使用してください。

どのような場合にgetStaticPathsを使用するのですか?

ダイナミックルートを使用しているページを静的にプリレンダリングする場合は、getStaticPathsを使用してください。

TypeScript:GetStaticPathsの使用

TypeScript の場合は、nextGetStaticPaths型が使えます。

import { GetStaticPaths } from "next"
export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

技術的な詳細

getStaticProps との併用

動的なルートパラメータを持つページでgetStaticPropsを使用する場合は、getStaticPathsを使用する必要があります。

getStaticPathsgetServerSidePropsを併用することはできません。

サーバー側でビルド時にのみ実行

getStaticPathsはサーバー側でビルド時にのみ実行されます。

ページでのみ許可

getStaticPathsページからのみエクスポートできます。ページ以外のファイルからエクスポートすることはできません。

また、export async function getStaticPaths() {}を使用する必要があります。getStaticPathsをページコンポーネントのプロパティとして追加しても動作しません。

開発中のすべてのリクエストで実行

開発中(next dev)では、getStaticPathsはすべてのリクエストで呼び出されます。

getServerSideProps(サーバーサイドレンダリング)

バージョン履歴
バージョン 変更
v10.0.0 localelocalesdefaultLocale、およびnotFoundオプションが追加されました。
v9.3.0 getServerSidePropsが導入された。

ページからgetServerSidePropsというasync関数をエクスポートすると、Next.js はリクエストごとにgetServerSidePropsから返されたデータを使ってこのページをプリレンダリングします。

export async function getServerSideProps(context) {
  return {
    props: {}, // propsとしてページコンポーネントに渡されます。
  }
}

contextパラメーターは、以下のキーを含むオブジェクトです:

  • params: このページが動的なルートを使用している場合、paramsにはルートのパラメータが含まれます。ページ名が [id].js であれば、params{ id: .... }です。詳細については、動的ルーティングのドキュメントをご覧ください。
  • req:HTTP IncomingMessage オブジェクト。
  • res:HTTP 応答オブジェクト。
  • query:クエリ文字列を表すオブジェクトです。
  • preview:preview は、ページがプレビューモードの場合はtrue、そうでない場合はfalseです。プレビューモードのドキュメントを参照してください。
  • previewData: setPreviewDataで設定したプレビューデータです。プレビューモードのドキュメントを参照してください。
  • resolvedUrl:クライアントの移行のために_next/dataプレフィックスを除去し、オリジナルのクエリ値を含む、リクエスト URL の正規化バージョンです。
  • localeは、有効なロケールを含みます(有効な場合)。
  • localesは、サポートされているすべてのロケールを含みます(有効な場合)。
  • defaultLocaleは、設定されたデフォルトのロケールを含みます(有効な場合)。

getServerSidePropsは、次のようなオブジェクトを返す必要があります:

  • props - ページコンポーネントが受け取る props を持つオプショナルなオブジェクトです。シリアル化可能なオブジェクトである必要があります。
  • notFound - ページが 404 ステータスとページを返すことを許可するオプショナルなブール値です。以下は、その動作例です:
    export async function getServerSideProps(context) {
      const res = await fetch(`https://...`)
      const data = await res.json()
      if (!data) {
        return {
          notFound: true,
        }
      }
      return {
        props: {}, // propsとしてページコンポーネントに渡されます。
      }
    }
    
  • redirect - 内部および外部リソースへのリダイレクトを可能にするオプショナルなリダイレクト値です。destination: string, permanent: boolean } の形と一致していなければなりません。 稀なケースですが、古い HTTP クライアントが適切にリダイレクトするために、カスタムのステータスコードを割り当てる必要がある場合があります。このような場合には、permanentプロパティの代わりにstatusCodeプロパティを使用することができますが、両方を使用することはできません。以下に、その例を示します:
    export async function getServerSideProps(context) {
      const res = await fetch(`https://.../data`)
      const data = await res.json()
      if (!data) {
        return {
          redirect: {
            destination: "/",
            permanent: false,
          },
        }
      }
      return {
        props: {}, // propsとしてページコンポーネントに渡されます。
      }
    }
    

つまり、getServerSidePropsサーバーサイドのコードを直接書くことができるのです。これには、ファイルシステムやデータベースからの読み込みも含まれます。
:::

ここでは、getServerSidePropsを使ってリクエスト時にデータを取得し、それを事前にレンダリングする例を紹介します。この例はPages のドキュメントにも掲載されています。

function Page({ data }) {
  // Render data...
}
// これはリクエストごとに呼び出されます
export async function getServerSideProps() {
  // 外部APIからのデータ取得
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  // propsを介してページにデータを渡す
  return { props: { data } }
}
export default Page

getServerSidePropsはどのような場合に使用するのですか?

getServerSidePropsは、リクエスト時にデータを取得しなければならないページを事前にレンダリングする必要がある場合にのみ使用してください。Time to first byte(TTFB)は、サーバーがリクエストごとに結果を計算しなければならないため、getStaticPropsよりも遅くなります。また、CDN では追加設定なしに結果をキャッシュすることができません。

もし、データをプリレンダリングする必要がないのであれば、クライアント側でデータを取得することを検討すべきでしょう。詳細はこちら

TypeScript:GetServerSidePropsの使用

TypeScript の場合は、nextGetServerSideProps型が使えます:

import { GetServerSideProps } from "next"
export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}

props の推論された型付けを取得したい場合は、以下のようにInferGetServerSidePropsType<typeof getServerSideProps>を使用できます。

import { InferGetServerSidePropsType } from "next"
type Data = { ... }
export const getServerSideProps = async () => {
  const res = await fetch("https://.../data")
  const data: Data = await res.json()
  return {
    props: {
      data,
    },
  }
}
function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // postsの型データを取得
}
export default Page

技術的な詳細

サーバー側でのみ実行

getServerSidePropsはサーバーサイドでのみ実行され、ブラウザでは決して実行されません。ページがgetServerSidePropsを使用している場合:

  • このページを直接リクエストすると、リクエスト時にgetServerSidePropsが実行され、返された props でこのページがプリレンダリングされます。
  • クライアントサイドのページ遷移でnext/linkドキュメント)やnext/routerドキュメント)でこのページをリクエストすると、Next.js はサーバーに API リクエストを送り、getServerSidePropsを実行します。getServerSidePropsを実行した結果を含む JSON が返され、その JSON を使ってページがレンダリングされていきます。これらの作業はすべて Next.js によって自動的に処理されるため、getServerSidePropsを定義している限り、余計なことをする必要はありません。

このツールを使って、Next.js がクライアント側のバンドルから何を排除しているかを確認することができます。

ページでのみ許可

getServerSidePropsは、ページからのみエクスポートできます。ページ以外のファイルからはエクスポートできません。

また、export async function getServerSideProps() {}を使用する必要があります - getServerSidePropsをページコンポーネントのプロパティとして追加した場合は動作しません

クライアント側でデータを取得する

ページに頻繁に更新されるデータが含まれていて、そのデータを事前にレンダリングする必要がない場合、クライアントサイドでデータを取得することができます。この例として、ユーザー固有のデータが挙げられます。その方法を紹介します:

  • まず、データのないページをすぐに表示します。ページの一部は、静的生成を使ってプリレンダリングすることができます。データがないときのローディング状態を表示することができます。
  • そして、クライアント側でデータを取得し、準備ができたら表示します。

このアプローチは、例えばユーザーのダッシュボードページにも有効です。ダッシュボードはユーザー専用のプライベートなページであるため、SEO は関係なく、ページにプリレンダリングを施す必要もありません。また、データは頻繁に更新されるため、リクエスト時ににデータを取得する必要があります。

SWR

Next.js を開発したチームは、SWRというデータフェッチ用の React フックを作成しました。クライアントサイドでデータを取得している場合は、SWR を強くお勧めします。SWR は、キャッシング、再検証、フォーカストラッキング、等間隔での再取得などの機能を備えています。そして、次のように使用できます:

import useSWR from "swr"
const fetcher = (url) => fetch(url).then((res) => res.json())
function Profile() {
  const { data, error } = useSWR("/api/user", fetcher)
  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

詳細については、SWR のドキュメントを確認してください。

もっと詳しく知る

次に、以下のセクションを読むことをお勧めします。
https://nextjs.org/docs/advanced-features/preview-mode
https://nextjs.org/docs/routing/introduction
https://nextjs.org/docs/basic-features/typescript#pages

Discussion

ログインするとコメントできます