データ取得

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

Pagesのドキュメントにおいて、Next.js には 2 種類のプリレンダリングがあることを説明しました。静的生成サーバーサイドレンダリングです。このページでは、それぞれの場合でのデータ取得戦略について掘り下げていきます。 まず最初にPagesのドキュメントを読むことをお勧めします。

プリレンダリングのデータ取得に使える 3 つの特徴的な Next.js の関数についてお話します:

また、クライアント側でのデータ取得について簡潔にお話します。

getStaticProps(静的生成)

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

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

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

  • paramsはページが動的ルートを利用するためのルートパラメータを持ちます。たとえば、ページ名が [id].js である時、params{ id: ...} のように見えます。詳細は 動的ルーティングのドキュメントをご覧ください。後に説明する getStaticPathsと一緒に使う必要があります。
  • ページがプレビューモードになっている時は previewtrue になり、そうでない場合は false になります。プレビューモードのドキュメントをご覧ください。
  • previewDataは、setPreviewDataによって設定されたプレビューデータを含みます。プレビューモードのドキュメント をご覧ください。

簡単な例

getStaticPropsを使って CMS(コンテンツマネジメントシステム)からブログ記事の一覧を取得する例です。この例はPagesのドキュメントにもあります。

// posts はビルド時に getStaticProps() によって生成されます。
function Blog({ posts }) {
  return (
    <ul>
      {posts.map(post => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}

// この関数はサーバー側でのビルド時に呼び出されます。
// クライアント側では呼び出されないため、データベースクエリを直接実行することも可能です。
// 「技術詳細」のセクションをご覧ください。
export async function getStaticProps() {
  // 外部のAPIエンドポイントを呼び出してpostsを取得します。
  // 任意のデータ取得ライブラリを使用できます。
  const res = await fetch('https://.../posts');
  const posts = await res.json();

  // { props: posts } を返すことで、Blog コンポーネントは
  // ビルド時に`posts`を prop として受け取ります。
  return {
    props: {
      posts
    }
  };
}

export default Blog;

getStaticPropsをいつ使うべきか?

getStaticPropsを使うのはこんな時。

  • ページをレンダリングするのに必要なデータが、ビルド時にユーザーのリクエストよりも先に利用可能。
  • データがヘッドレス CMS から取得される。
  • データが公開キャッシュ(ユーザー固有ではない)。
  • ページがプリレンダリング(SEO のため)されて非常に高速。getStaticPropsは HTML と JSON ファイルを生成し、どちらもパフォーマンスのために CDN でキャッシュされる。

TypeScript: GetStaticPropsを使う

TypeScript では、GetStaticProps型を next から利用できます。

import { GetStaticProps } from 'next';

export const getStaticProps: GetStaticProps = async context => {
  // ...
};

ファイル読み込み: process.cwd()を使う

ファイルは getStaticProps でファイルシステムから直接読み込みできます。

そのためにはファイルのフルパスが必要です。

Next.js はコードを別のディレクトリにコンパイルするため、__dirnameはページディレクトリとは異なり、パスとしては使えません。

代わりに process.cwd() を使って Next.js が実行されたディレクトリを取得できます。

import 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 = fs.readdirSync(postsDirectory);

  const posts = filenames.map(filename => {
    const filePath = path.join(postsDirectory, filename);
    const fileContents = fs.readFileSync(filePath, 'utf8');

    // 通常はコンテンツをパースもしくは変換するでしょう。
    // たとえば、ここではマークダウンをHTMLに変換できます。

    return {
      filename,
      content: fileContents
    };
  });
  // { props: posts } を返すことで、Blogコンポーネントはビルド時に
  // `posts`をpropとして受け取ります。
  return {
    props: {
      posts
    }
  };
}

export default Blog;

技術詳細

ビルド時のみ実行

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

サーバー側のコードを直接記述

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

HTMLとJSONの両方を静的に生成

getStaticPropsを持つページがビルド時にプリレンダリングされると、ページの HTML ファイルだけでなく Next.js は getStaticProps の結果を持つ JSON ファイルを生成します。

この JSON ファイルは、next/link(ドキュメント)もしくは next/router (ドキュメント)経由のクライアント側のルーティングで使われます。getStaticProps でプリレンダリングされたページに遷移すると、Next.js はこの JSON ファイル(ビルド時に事前処理)を取得してページコンポーネントの props として使います。つまり、クライアント側のページ遷移は getStaticProps を呼び出さず、エクスポートされた JSON だけが使われます。

ページ内でのみ許可

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

この制約の理由の 1 つは、React がページレンダリングの前に必要なデータを全て持つ必要があるという点です。

また、export async function getStaticProps() {}を使用しなければなりません。getStaticPropsをページコンポーネントのプロパティとして追加しても機能しません。

開発中はリクエストごとに実行

開発中(next dev)は、getStaticPropsはリクエストごとに呼び出されます。

プレビューモード

一時的に静的生成を迂回してビルド時ではなくリクエスト時にレンダリングしたい時もあるでしょう。たとえば、ヘッドレス CMS を使って公開前に下書きをプレビューするような時です。

このユースケースは Next.js のプレビューモードという機能でサポートされています。詳細はプレビューモードのドキュメントをご覧ください。

getStaticPaths(静的生成)

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

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

export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // 下記の「パス」セクションをご覧ください。
    ],
    fallback: true or false // 下記の「フォールバック」セクションをご覧ください。
  };
}

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] であれば、paramspostIdcommentId を含まなければなりません。 -pages/[...slug] のようにページ名が catch-all ルートを使用していれば、paramsslug という配列を含まなければなりません。たとえば、この配列が ['foo', 'bar'] であれば、 Next.js は静的に /foo/bar というページを生成します。

fallbackキー(必須)

getStaticPathsで返されるオブジェクトはブール値の fallback キーを含まなければなりません。

fallback: false

fallbackfalse であれば、getStaticPathsで返されないパスは全て404 ページとなります。プリレンダリングの必要なパスが少ない時に使えます。つまり、ビルド時に全てのパスが静的に生成されます。新しいページがあまり追加されないような時にも役立ちます。データソースに項目を増やして新しいページをレンダリングしたい場合には、再度ビルドする必要があります。

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

// pages/posts/[id].js

function Post({ post }) {
  // 記事をレンダリングします...
}

// この関数はビルド時に呼び出されます。
export async function getStaticPaths() {
  // 外部APIエンドポイントを呼び出して記事を取得します。
  const res = await fetch('https://.../posts');
  const posts = await res.json();

  // 記事に基づいてプリレンダリングしたいパスを取得します
  const paths = posts.map(post => ({
    params: { id: post.id }
  }));

  // ビルド時にこれらのパスだけをプリレンダリングします。
  // { fallback: false } は他のルートが404になることを意味します。
  return { paths, fallback: false };
}

// ビルド時にも呼び出されます。
export async function getStaticProps({ params }) {
  // paramsは記事の`id`を含みます。
  // ルートが/posts/1のような時、params.id は1です。
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();

  // 記事データをprops経由でページに渡します。
  return { props: { post } };
}

export default Post;

fallback: true

fallbacktrue の時、getStaticPropsの振る舞いは変わります。

  • getStaticPathsが返すパスはビルド時に HTML でレンダリングされます。
  • ビルド時に生成されなかったパスは 404 ページにはなりません。代わりに、Next.js はそのようなパス(「フォールバックページ」をご覧ください)への最初のリクエストに対して「フォールバック」版のページを提供します。
  • バックグラウンドで、Next.js はリクスエストされたパスの HTML と JSON を静的に生成します。これは getStaticProps の実行を含みます。
  • 完了したら、ブラウザは生成されたパスの JSON を受け取ります。これは必須の props を使ってページを自動的にレンダリングするのに使われます。
  • 同時に、Next.js はこのパスをプリレンダリングされたページの一覧に追加します。同じパスに対する後続のリクエストは、ビルド時にプリレンダリングされた他のページと同じように生成されたページを提供します。

フォールバックページ

「フォールバック」版ページの特徴は下記のとおりです。

  • ページの props は空になります。
  • ルーターを使うと、フォールバックがレンダリングされたことを検知でき、route.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>;
  }

  // 記事をレンダリングします...
}

// この関数はビルド時に呼び出されます。
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 は記事の`id`を含みます。
  // ルートが/posts/1のような時、params.idは1です。
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();

  // 記事データをprops経由でページに渡します。
  return { props: { post } };
}

export default Post;

fallback: trueがいつ役立つか?

fallback: trueは、アプリケーションがデータに依存する多数の静的ページ(巨大な E コマースサイトなど)を持つ時に役立ちます。全商品のページをプリレンダリングしたくても、ビルドに膨大な時間がかかってしまうような時です。

代わりに、ページの小さなサブセットを静的に生成し、残りは fallback: true にできます。未生成のページがリクエストされると、ユーザーにはローディングインジケーターが表示されます。その直後に、getStaticPropsが完了して、ページはリクエストされたデータでレンダリングされます。それ以降は、同じページがリクエストされると静的にプリレンダリングされたページが表示されます。

これにより、ユーザーは高速なビルドと静的生成の利点を保ったまま、常に高速な体験を得ることができます。

getStaticPathsをいつ使うべきか?

動的ルートを使ったページを静的にプリレンダリングするときに getStaticPaths を使うと良いでしょう。

TypeScript: GetStaticPathsを使う

TypeScript では、nextから GetStaticPaths 型を利用できます。

import { GetStaticPaths } from 'next';

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
};

技術詳細

getStaticPropsと一緒に使う

動的ルートパラメータを持つページで getStaticProps を使う時は getStaticPaths を使わなければなりません。

getStaticPathsgetServerSideProps は併用できません。

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

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

ページ内でのみ許可

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

また、export async function getStaticPaths() {}を使わなければなりません。これは getStaticPaths をページコンポーネントのプロパティとして追加しても動作しません

開発中はリクエストごとに実行

開発中(next dev)は、getStaticPathsはリクエストごとに呼び出されます。

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

getServerSidePropsという async 関数をエクスポートすると Next.js はリクエストごとに getServerSideProps から返されるデータでプリレンダリングします。

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

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

簡単な例

getServerSidePropsを使ってリクエスト時にデータを取得してプリレンダリングする例を示します。この例は ページのドキュメントにもあります。

function Page({ 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は、リクエスト時にデータを取得するページのプリレンダリングが必要な時のみ使うべきです。最初のバイトの所要時間(TTFB)は getStaticProps よりも遅くなります。サーバーはリクエストごとに演算しなければならず、その結果は追加設定なしで CDN にキャッシュされないためです。

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

TypeScript: GetServerSidePropsを使う

TypeScript では、GetServerSideProps型を next から利用できます。

import { GetServerSideProps } from 'next';

export const getServerSideProps: GetServerSideProps = async context => {
  // ...
};

技術詳細

サーバー側でのみ実行

getServerSidePropsはサーバー側でのみ実行され、ブラウザでは決して実行されません。ページが getServerSideProps を使うと次のような挙動になります。

  • このページを直接リクエストすると、getServerSidePropsはリクエスト時に実行され、返された props でプリレンダリングされます。
  • next/link(ドキュメント) や next/router (ドキュメント) でのクライアント側のページ遷移をリクエストすると Next.js は API リクエストをサーバーに送信します。続けて getServerSideProps を実行します。この挙動は全て自動的に Next.js によって処理されるため、getServerSidePropsを定義しさえすれば他にやることはありません。

ページ内でのみ許可

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

また、export async function getStaticPaths() {}を使わなければなりません。これは getServerSideProps をページコンポーネントのプロパティとして追加しても動作しません

クライアント側でのデータ取得

ページが頻繁に更新されるデータを持ち、データをプリレンダリングする必要がないようであれば、クライアント側でデータ取得もできます。ユーザー固有のデータなどが該当します。次のように機能します。

  • 最初に、データなしのページを直ちに表示します。ページのパーツは静的生成によりプリレンダリングもできます。データがない箇所にはローディング状態を表示できます。
  • 次に、クライアント側でデータを取得して準備ができたら表示します。

このアプローチは、ユーザーダッシュボードのようなページに有効です。ダッシュボードはプライベートかつユーザー固有のページであり、SEO も不要なのでページをプリレンダリングする必要もないためです。データは頻繁に更新され、リクエスト時のデータ取得を必要とします。

SWR

Next.js の開発チームはデータ取得用の SWR という React フックを作成しました。クライアント側でデータ取得する際にはその利用を強くお勧めします。キャッシュ、再バリデーション、フォーカスの追跡、一定間隔での再取得などを処理できます。次のように使用します。

import useSWR from 'swr';

function Profile() {
  const { data, error } = useSWR('/api/user', fetch);

  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;
  return <div>hello {data.name}!</div>;
}

詳しくはSWRのドキュメントをご覧ください

もっと詳しく

次のセクションを読むことをお勧めします。