Next.jsでローカルのJSONファイルからデータの取得と表示
本日はNext.jsでローカルのJSONファイルからデータを読み取って表示する方法を示します。
サーバー側のレンダリングや静的Webサイトの生成などの追加機能に関して、Next.jsは非常に興味深いものです。
これは、バックエンドをコーディングしたり、データベースのセットアップや接続に時間を費やしたりすることなく、小さなWebアプリをすばやく構築したい場合に非常に役立ちます。
Next.jsの事前レンダリング
Next.jsはいくつかの異なるデータ取得戦略を提供しています。
それらは下記の通りです。
・静的サイトレンダリング(SSG)
・増分静的再生(ISR)
上記の中でNext.jsでは、静的生成とサーバー側レンダリングの2つの事前レンダリングがあります。
その2つの違いは、ページのHTMLを生成するタイミングです。
・ 静的生成(SSG)
静的生成には、データありとデータなしの2種類があります。
Promiseを解決してデータをフェッチした後にHTMLが生成されます。
静的生成は、ビルド時にHTMLを生成する事前レンダリング方法となっています。
その後、事前にレンダリングされたHTMLが各リクエストで再利用されていきます。
もう一つは、動的ページを使用しパスが外部データに依存する場合です。
その場合、どの動的ルートをプリロードする必要があるかを伝えるためのgetStaticPaths関数を使用できます。
主に、動的ルートを使用するページを静的に事前レンダリングしている場合に使用されます。
後ほど解説します。
サーバーサイドレンダリングでは、リクエストごとに HTMLを生成する事前レンダリング方法となっています。
これら2つのどちらかを選択していきますが、ほとんどのページで静的生成を使用します。
クライアント側(CSR)のレンダリングでは、ページの初回ロードが少し遅くなります。
サーバーとの通信は実行時に行われます。
JSの伝統的なよくある方法でそれを行うことができ、クライアント側のデータフェッチでフックを使用し静的生成を使用できます。
ページ制限
Next.jsが同等に強力で使いやすい理由の1つは、ページの配信方法となります、Next.jsページの制限です。
ページをレンダリングする前にデータをフェッチするオプションがありますが、基本的にページはReactコンポーネントとしてレンダリングされます。
そして、それらはアプリケーションレベルのコンポーネントにラップされます。
つまり、Next.jsページのパターンに従って静的または動的なJSONページを生成するための簡単な方法がないということです。
Next.jsアプリケーションでデータを取得したい場合はJSONとしてレンダリングするには、主に2つの方法がある事を先述で学んだはずです。
1つはリクエストごと(SSR)として更新され、もう 1 つはビルドごと(SSG)で更新されます。
・ 動的APIルート
Next.jsは、 APIルートと呼ばれるものをサポートしています。
そのAPIルートは、サーバー側で実行されユーザーにデータを返すメソッドとなります。
・ 静的ファイル
サイトを構築する前に静的JSONファイルを生成し、それを静的アセットとして提供することができます。
基本的にPrismaは使用しません。
Prisma Clientは、任意のNode.jsまたはTypeScript バックエンドアプリケーション(サーバーレス アプリケーションとマイクロサービスを含む)で使用でき、これはREST API、GraphQL API、gRPC APIなどのデータベースを必要とするものであれば、JSでデータベースを作成したり、クエリを発行してデータを取得するのに役立つものです。
データをフェッチするためにSQLまたは任意の種類のデータベース接続することをお勧めしますが、動的APIルートではなくこのチュートリアルを簡単にするために、すべてのデータをローカルの静的JSON ファイルから取得していきます。
アプリケーションのデータは、静的なJSON ファイルから供給されます。
Next.jsを利用したアプリケーションを構築する過程で、多くの場合で内部APIルートファイルまたは外部APIからデータを取得する必要があることに気付くでしょう。
主に、Next.jsにはデータを取得するために使用できる3つの独自の関数があります。
・ getStaticProps()
データはビルド時にフェッチ(取得)され、クライアントのリクエストの前に利用可能となります。
常にサーバー上で実行され、クライアント上では実行される事はありません。
ページを静的生成(SSG)にできます。
その結果、使用可能なすべてのデータフェッチメソッドの中で、GetStaticPropsが最速の読み込み時間を生成するので、データがクライアントに到達する前にレンダリングされるため、ページのSEOが飛躍的に向上します。
・ getStaticPaths()
データの取り込みは動的API ルートを使用してフェッチされます。
Next.jsはこのgetStaticPathsによって提供されるすべてのパスを静的に事前レンダリングします。
本番環境でのビルド中にのみ実行され、実行時には呼び出される事はありません。
getStaticPathsはgetStaticPropsと一緒に使用されるのが最善となっていますが、getServerSidePropsと使用する事はできませんので注意して下さい。
・ getServerSideProps()
サーバー側レンダリング(SSR)として、外部データを取得するときに主に使用され、クライアントがページをロードするたびにデータが更新されるため、常に更新された情報を表示できます。
クライアントの要求に応じてサーバーからデータを取得されます。
つまり、getServerSidePropsはリクエストごとに実行されていきます。
この関数ではデータがクライアントに到達する前にレンダリングされるためSEOを改善できます。
JSONファイル自体は動的に生成する必要がありますが、静的にすることも可能という事です。
本日は、ローカルの静的JSONファイルからデータを読み取るためにgetStaticProps()
関数とfs Promises
APIを使用します。
このfs Promises
はNode.jsの組み込み機能であるため、サードパーティのパッケージを別途インストールする必要はありません。
セットアップ
npx create-next-app project-sample
任意の名前でcreate-next-appのデフォルトテンプレートから始めることができます。
JSONデータの作成
package.json
およびnext.config.js
と同じレベル(階層)で、 data.json
という名前の新しいファイルを作成します。
例で使用するdata.json
ファイル内のJSONデータは下記になります。
ご自身で用意下さっても構いません。
{ "postLists": [ { "id": 1, "title": "user 1", "name": "Leo Dover" }, { "id": 2, "title": "user 2", "name": "Morris Hurley" }, { "id": 3, "title": "user 3", "name": "James Kissack" }, { "id": 4, "title": "user 4", "name": "Ricky Hamer" } ] }
先述したように上記のJSON ファイルからデータを読み取るには、getStaticProps()
関数およびfs Promises API
を使用していきます。
データの取得
getStaticProps
でローカルAPIルートを呼び出す際にfetch()
は使用しないでください。
fetch()
はネットワーク越しにJSONファイルを取得するためのものなのでローカルでは使用しません。
外部APIからのフェッチは問題ございません。
pages/index.js
のデフォルトのコードをすべて削除してから下記のように記述して下さい。
import fsPromises from 'fs/promises' import path from 'path' export default function Home(props) { const postLists = props.postLists; return ( <div> <Head> <title></title> </Head> <div> {postLists.map(post => <div key={post.id} style={{ padding: 20 }}> <h1>{post.id}</h1> <h2>{post.title}</h2> <li style={{ background: "#c6c6c6c6", marginBottom: 3, padding: "0.5em", fontSize: "1.2rem" }}> {post.name} </li> </div> )} </div> </div> ) } export const getStaticProps = async () => { const filePath = path.join(process.cwd(), 'data.json'); const data = await fsPromises.readFile(filePath); const objectData = JSON.parse(data); return { props: objectData } }
getStaticProps
は、あるページからgetStaticProps(静的サイト生成)というasync関数をエクスポートすると、Next.jsはビルド時にgetStaticPropsが返す、Propsを使用してそのページをプリレンダリングされオブジェクトを返します。
データはページコンポーネントにPropsとして渡されます。
export const getStaticProps = async () => { const filePath = path.join(process.cwd(), 'data.json'); const data = await fsPromises.readFile(filePath); const objectData = JSON.parse(data) return { props: objectData } }
これは、エクスポートされたJSONのみが使用されます。
この関数はAPI リクエストから受け取ったデータをビルド時にサーバーでレンダリングしたい場合に特に便利です。
const filePath = path.join(process.cwd(), 'data.json');
process.cwd()
メソッドは、Node.jsプロセスの現在の作業ディレクトリを指定する文字列を返し、それを取得するために使用されるものです。
それをfilePath変数に割り当てます。
const data = await fsPromises.readFile(filePath);
ファイルの読み込みは、fsPromises.readFile()
メソッドを使用します。
引数は、読み込むファイルの名前、または他の場所に保存されている場合はパス全体を保持する文字列、バッファ、URL、または変数名である必要があります。
読み込んだJSONファイルをdata
変数として割り当てます。
const objectData = JSON.parse(data)
そして読み込んだJSONデータをJSON.parse
メソッドでJSON形式で書かれた文字列をJavaScriptのJSONオブジェクトに変換しJavaScriptのオブジェクトを返します。
objectData
をPropsとして渡してあげます。
Home
コンポーネントでPropsを受けとり、map構文でそれぞれ処理を実行し表示させます。
export default function Home(props) { const postLists = props.postLists;
それでは、Next.jsアプリを起動し実行します。
npm run dev
fs.promises
関数およびメソッドが分からない方はNode.jsのfsモジュールを学習してください。
https://nodejs.dev/en/learn/the-nodejs-fs-module
特にファイル操作などでは、fsモジュールはNode.jsで頻繁に使用する機能なのでなるべく扱えるようにしときましょう。
下記の方法でも取得可能です。
JSONファイルをインポートでロードしgetStaticProps
はオブジェクトとして返すのでそれをPropsとして渡してあげる単純な方法です。
import data from '../data.json' export default function Home(props) { const postLists = props.postLists; console.log(postLists) return ( <div style={{ padding: 30 }}> <Head> <title></title> </Head> <div> {postLists.map(post => <div key={post.id} style={{ padding: 20 }}> <h1>{post.id}</h1> <h2>{post.title}</h2> <li style={{ background: "#c6c6c6", marginBottom: 3, padding: "0.5em", fontSize: "1.2rem" }}>{post.name}</li> </div> )} </div> </div> ) } export const getStaticProps = async () => { return { props: { postLists: data } } }
上記の出力を見て分かる通り、index.js
からアクセスしてもエラーがスローされる事なくオブジェクトとして渡されてるのが確認できます。
本番用サーバーでもエラーがスローされる事なく表示されます。
ですがこの方法は、Next.jsでローカルJSONファイルを取得するための方法としては正しいです、しかし、コード内の../data.json
のパスは、JSONファイルがプロジェクトルートの直下にあることを想定している事が前提です。
もしJSONファイルが他の場所にある場合は、パスを適切に変更する必要がありますので気をつけて下さい。
Next.jsで静的JSONデータを取得する場合、必ずしもgetStaticProps
を使用しなければいけないという事ではありません。
つまり、pages/api/data.json
として配置したとします、静的APIルートです。
getStaticProps
関数を使用するとエラーがスローされる可能性があります。
ですので、その場合getStaticProps
は必要ありません、ビルド時にWebpackがJSONをバンドルするためです。
getStaticPropsを使用した場合、json(data)をサーバーに渡すと、サーバーはHTMLを作成しクライアントにはjsonを渡す事はありません。
なので、サーバーはJSONデータを使用してデータを挿入し、キャッシュを作成します。
使用しない場合、JSONファイルはキャッシュされずにクライアント側に挿入されるようになります。
なるべくgetStaticProps
の使用を推奨しますがgetStaticProps
を使用せずに、Next.jsで静的JSONデータを取得する場合の方法を示します。
getStaticPropsを使用せずに静的JSONデータをローカルで取得
import文でJSONデータを読み込む方法です。
プロジェクト内にJSONファイルを作成します。
/data/sample.json
というファイルを作成します。
下記のような形式でデータを用意します。
// /data/sample.json { "title": "Sample Title", "id": 1, "description": "This is a sample description" }
上記の作成したJSONファイルを読み込むには、import文を使用します。
import sampleData from '../data/sample.json'; export default function Home() { return ( <div> <h1>{sampleData.title}</h1> <p>{sampleData.id}</p> <p>{sampleData.description}</p> </div> ); }
前述したように、JSONファイルをAPIエンドポイントとして利用することもできます。
例えば、/pages/api/sample.js
というファイルを作成します。
import sampleData from '../../data/sample.json'; export default function handler(req, res) { res.status(200).json(sampleData); }
上記の例では、/api/sample
にリクエストが来た場合、sampleData
をJSON形式で返します。
このAPIエンドポイントを使用して、クライアントサイドからJSONデータを取得することも可能です。
このように、getStaticProps
を使用せずにNext.jsで静的JSONデータをローカルで取得することができます。
ただし、APIエンドポイントを使用する場合は、getStaticProps
を使用する場合と同様に、データの取得に時間がかかるため、パフォーマンスに注意する必要があります。
クライアント側(CSR)でJSONを使用するか、サーバー側でJSONを使用するのか、それぞれどのアプローチを使用していくかは、完全にあなた次第です。
最後に
これであなたはNext.jsでローカルのJSONファイルからデータを取得してレンダリングする方法を学習しました。
これにより、比較的に小さなウェブサイトをほとんど労力をかけずに迅速に開発していくことが可能となります。
大規模なプロジェクト構築の場合では、先述したようにMySQLやMongoDBまたは任意の種類のデータベースでセットアップし接続することをお勧めします。
外部での取得方法は別途記事に致します。
本日は以上となります。
最後までこの記事を読んで頂きありがとうございます。
この記事が気に入ったらブックマークして、他の方にも共有してください。