データ取得
データベースの作成と初期データ投入が完了したので、アプリケーションでデータを取得するさまざまな方法について説明し、ダッシュボード概要ページを構築していきましょう。
データ取得方法の選択
APIレイヤー
APIはアプリケーションコードとデータベースの中間層です。APIを使用するケースは以下の通りです:
- サードパーティサービスがAPIを提供している場合
- クライアントからデータを取得する場合、データベースの秘密情報をクライアントに公開しないようにサーバー上で実行されるAPIレイヤーが必要
Next.jsでは、Route Handlersを使用してAPIエンドポイントを作成できます。
データベースクエリ
フルスタックアプリケーションを作成する際には、データベースとやり取りするロジックも記述する必要があります。Postgresのようなリレーショナルデータベースでは、SQLまたはORMを使用してこれを行えます。
データベースクエリを記述する必要があるケース:
- APIエンドポイントを作成する際、データベースとやり取りするロジックを記述する必要がある
- Reactサーバーコンポーネント(サーバー上でデータを取得)を使用している場合、APIレイヤーをスキップし、データベースの秘密情報をクライアントに公開するリスクなしに直接データベースをクエリできる
Reactサーバーコンポーネントについてさらに学びましょう。
サーバーコンポーネントを使用したデータ取得
デフォルトでNext.jsアプリケーションはReactサーバーコンポーネントを使用します。サーバーコンポーネントでのデータ取得は比較的新しいアプローチで、以下の利点があります:
- サーバーコンポーネントはJavaScriptのPromiseをサポートし、データ取得のような非同期タスクに対してネイティブな解決策を提供。
useEffect
、useState
や他のデータ取得ライブラリなしでasync/await
構文を使用可能 - サーバーコンポーネントはサーバー上で実行されるため、負荷の高いデータ取得やロジックをサーバー上に保ち、結果のみをクライアントに送信できる
- サーバーコンポーネントはサーバー上で実行されるため、追加のAPIレイヤーなしで直接データベースをクエリ可能。これにより追加コードの記述とメンテナンスが不要になる
SQLの使用
ダッシュボードアプリケーションでは、postgres.jsライブラリとSQLを使用してデータベースクエリを記述します。SQLを使用する理由:
- SQLはリレーショナルデータベースをクエリする業界標準(ORMも内部でSQLを生成)
- SQLの基本的な理解はリレーショナルデータベースの基礎理解に役立ち、他のツールにも知識を応用可能
- SQLは汎用性が高く、特定のデータを取得・操作できる
postgres.js
ライブラリはSQLインジェクションに対する保護を提供
SQLを使用したことがなくても心配ありません - クエリは提供されています。
/app/lib/data.ts
に移動してください。ここでpostgres
を使用しているのがわかります。sql
関数によりデータベースをクエリできます:
sql
はサーバーコンポーネントのようにサーバー上のどこでも呼び出せます。ただし、コンポーネント間の移動を容易にするため、すべてのデータクエリをdata.ts
ファイルに保持し、コンポーネントにインポートできるようにしています。
注: 第6章で独自のデータベースプロバイダーを使用した場合、プロバイダーで動作するようにデータベースクエリを更新する必要があります。クエリは
/app/lib/data.ts
にあります。
ダッシュボード概要ページのデータ取得
データ取得のさまざまな方法を理解したので、ダッシュボード概要ページのデータを取得しましょう。/app/dashboard/page.tsx
に移動し、以下のコードを貼り付けて時間をかけて確認してください:
上記のコードは意図的にコメントアウトされています。各部分を順番に確認していきます。
page
はasyncサーバーコンポーネントです。これによりデータ取得にawait
を使用できます- データを受け取る3つのコンポーネント:
<Card>
、<RevenueChart>
、<LatestInvoices>
が現在コメントアウトされており、まだ実装されていません
**<RevenueChart/>
**のデータ取得
<RevenueChart/>
コンポーネントのデータを取得するには、data.ts
からfetchRevenue
関数をインポートし、コンポーネント内で呼び出します:
次に以下の手順を行います:
<RevenueChart/>
コンポーネントのコメントを解除- コンポーネントファイル(
/app/ui/dashboard/revenue-chart.tsx
)に移動し、内部のコードのコメントを解除 localhost:3000
を確認すると、revenue
データを使用したチャートが表示されます

さらにデータをインポートし、ダッシュボードに表示していきましょう。
**<LatestInvoices/>
**のデータ取得
<LatestInvoices />
コンポーネントでは、日付でソートされた最新の5つの請求書を取得する必要があります。
JavaScriptを使用してすべての請求書を取得し、ソートすることも可能です。データ量が少ない場合は問題ありませんが、アプリケーションが成長するにつれ、各リクエストで転送されるデータ量とソートに必要なJavaScriptが大幅に増加する可能性があります。
メモリ内で最新の請求書をソートする代わりに、SQLクエリを使用して最後の5つの請求書のみを取得できます。例えば、data.ts
ファイルからのSQLクエリは以下の通りです:
ページでfetchLatestInvoices
関数をインポートします:
次に、<LatestInvoices />
コンポーネントのコメントを解除します。また、/app/ui/dashboard/latest-invoices
にある<LatestInvoices />
コンポーネント自体の関連コードもコメント解除する必要があります。
localhostを確認すると、データベースから返された最後の5件のみが表示されます。直接データベースをクエリする利点がわかってきたのではないでしょうか!

練習:<Card>
コンポーネントのデータ取得
次は<Card>
コンポーネントのデータ取得に挑戦しましょう。カードには以下のデータが表示されます:
- 回収済み請求書の総額
- 保留中の請求書の総額
- 請求書の総数
- 顧客の総数
再び、すべての請求書と顧客を取得し、JavaScriptを使用してデータを操作したくなるかもしれません。例えば、Array.length
を使用して請求書と顧客の総数を取得できます:
しかしSQLでは、必要なデータのみを取得できます。Array.length
を使用するよりも少し長くなりますが、リクエスト中に転送されるデータ量が少なくなります。SQLの代替案は以下の通りです:
インポートする必要がある関数はfetchCardData
と呼ばれます。この関数から返される値を分割代入する必要があります。
ヒント:
- カードコンポーネントを確認し、必要なデータを確認
data.ts
ファイルを確認し、関数が返す内容を確認
準備ができたら、以下のトグルを展開して最終コードを確認してください:
素晴らしい!ダッシュボード概要ページのすべてのデータを取得できました。ページは次のようになっているはずです:

しかし...注意すべき2つの点があります:
- データリクエストが意図せず互いをブロックし、リクエストの滝が発生している
- デフォルトでNext.jsはパフォーマンス向上のためルートを事前レンダリング(静的レンダリング)するため、データが変更されてもダッシュボードに反映されない
この章では1番目について、次の章で2番目について詳しく説明します。
リクエストの滝とは?
「滝」とは、前のリクエストの完了に依存する一連のネットワークリクエストを指します。データ取得の場合、各リクエストは前のリクエストがデータを返した後にのみ開始できます。

例えば、fetchLatestInvoices()
の実行を開始する前にfetchRevenue()
の実行を待つ必要があり、以下同様です。
このパターンが必ずしも悪いわけではありません。次のリクエストを行う前に条件が満たされるのを待ちたい場合もあります。例えば、ユーザーのIDとプロファイル情報を最初に取得し、IDを取得した後に友達リストを取得する場合などです。この場合、各リクエストは前のリクエストから返されたデータに依存します。
しかし、この動作は意図的でない場合もあり、パフォーマンスに影響を与える可能性があります。
並列データ取得
滝を避ける一般的な方法は、すべてのデータリクエストを同時に開始することです - 並列で。
JavaScriptでは、Promise.all()
またはPromise.allSettled()
関数を使用してすべてのPromiseを同時に開始できます。例えば、data.ts
ではfetchCardData()
関数でPromise.all()
を使用しています:
このパターンを使用することで:
- すべてのデータ取得を同時に開始でき、滝のように各リクエストの完了を待つよりも高速
- 任意のライブラリやフレームワークに適用可能なネイティブJavaScriptパターンを使用可能
ただし、このJavaScriptパターンだけに依存する場合の欠点が1つあります:1つのデータリクエストが他のすべてよりも遅い場合どうなるでしょうか?次の章でさらに詳しく見ていきましょう。