🚀

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

30 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

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

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

Data Fetching

このドキュメントは、Next.js バージョン 9.3 以降を対象としています。古いバージョンの Next.js を使用している場合は、以前のドキュメントを参照してください。

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 }, //プロップスとしてページコンポーネントに渡されます。
  }
}

注意
fallback: falseモードでは、getStaticPathsから返されたパスのみがプリレンダリングされるため、notFoundは必要ありません。

注意
notFound: trueの場合、以前に正常に生成されたページがあったとしても、ページは404を返します。これは、ユーザーが作成したコンテンツが作者によって削除された場合などに対応するためのものです。

  • 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 }, //プロップスとしてページコンポーネントに渡されます。
}
}

:ビルド時のリダイレクトは現在許可されていません。ビルド時にリダイレクトを行うことが明らかな場合は、next.config.jsで追加する必要があります。

:トップレベルのスコープでモジュールをインポートして、getStaticPropsで使用することができます。getStaticPropsで使用されるインポートは、クライアントサイドにバンドルされません

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

getStaticPropsでは、fetch()を使ってAPIルートを呼び出してはいけません。代わりに、APIルート内で使用されるロジックを直接インポートします。この方法では、コードを若干リファクタリングする必要があるかもしれません。

外部APIからのフェッチは問題ありません!

ここでは、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はこのパスを事前にレンダリングされたページのリストに追加します。同じパスへの後続のリクエストは、ビルド時に事前にレンダリングされた他のページと同様に、生成されたページを提供します。

fallback: trueは、next exportを使用する際にはサポートされていません。

フォールバックページ

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

  • ページの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を使用してください。

fallback: 'blocking'next exportを使用する場合はサポートされません。

どのような場合に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で使用されたインポートは、クライアントサイドにバンドルされません。

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

getServerSidePropsでAPIルートを呼び出すのにfetch()を使うべきではありません。代わりに、APIルート内で使用されるロジックを直接インポートします。この方法では、コードを若干リファクタリングする必要があるかもしれません。

外部APIからの取得は問題ありません。

ここでは、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

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