初めに
Next.jsの案件でSSGでデータを取得する実装をした際に、非同期処理について理解が浅かったので APIからデータを取得して表示するプロセスにおいて、今回はSSG(Static Site Generation)の説明は割愛し、非同期関数に焦点を当てて振り返りたいと思います。
大まかに以下3つの流れに沿って、書いていきます。
・APIエンドポイントの準備
・データ取得のフェッチ処理
・データの表示
非同期関数とは
以下、ChatGPTに聞いてみました。
APIからデータを取得するような時間がかかる操作を非同期的に処理するための関数です。非同期関数は主に
async
とawait
キーワードを使用して記述され、これによりJavaScriptの実行を停止することなく、バックグラウンドでデータのロードやその他のタスクを完了させることができます。
ディレクトリの構造について
APIエンドポイントの準備
APIエンドポイントとは
APIエンドポイントとは、APIが提供する特定のURLであり、クライアントがリクエストを送り、APIの仕様に基づいたデータを返す役割をします。
以下の手順に従って、APIエンドポイントを作成していきます。
・APIルートファイルの作成
・API応答用の初期データ設定
・応答遅延とデータの返却
APIルートファイルの作成
ファイルの位置については、pages/
ディレクトリ内に新しいファイルを作成します。ファイル名やディレクトリ構造がそのままURLのパスになります。例えば、pages/api/todos/index.ts
は http://localhost:3000/api/todos
に対応します。
コード全体
import { NextApiRequest, NextApiResponse } from "next";
type Todo = {
title: string;
}
const todos: Todo[] = [
{ title: "読書" },
{ title: "プログラミング" },
{ title: "散歩" },
];
export default async (req: NextApiRequest, res: NextApiResponse) => {
await new Promise((resolve) => setTimeout(resolve, 4000));
res.status(200).json(todos);
};
API応答用の初期データ設定
type
を使用してTodo型を定義しています。これは、title
という名前の文字列型のプロパティを持つオブジェクトの型です。このtitle
プロパティは、各TODOアイテムのタイトルを表しています。type
はTypeScriptでカスタムのデータ型を作成するための構文です。
type Todo = {
title: string;
}
todos
配列は、Todo型のオブジェクトを要素として含んでおり、事前に定義されたTODOリストを表しています。この配列には「読書」、「プログラミング」、「散歩」というタイトルを持つ3つのタスクが含まれています。
const todos: Todo[] = [
{ title: "読書" },
{ title: "プログラミング" },
{ title: "散歩" },
];
応答遅延とデータの返却
await new Promise((resolve) => setTimeout(resolve, 4000));
この行は、APIの応答を意図的に4秒遅延させています。
res.status(200).json(todos);
この行は、HTTPステータスコード200(成功)とともに、todosという名前のデータ配列をJSON形式でクライアントに返しています。これにより、APIを呼び出したクライアントは正常にデータを受け取ることができます。
export default async (req: NextApiRequest, res: NextApiResponse) => {
await new Promise((resolve) => setTimeout(resolve, 4000));
res.status(200).json(todos);
};
データ取得のフェッチ処理
上記で作成したAPIを、取得してから表示するまでのファイルを作成します。ファイルの位置については、app/todos/
ディレクトリ配下に page.tsx
ファイルを置きます。
コード全体
import Link from "next/link";
type Todo = {
title: string;
};
async function getData() {
const res = await fetch("http://localhost:3000/api/todos");
return res.json();
}
export default async function Page() {
const todos: Todo[] = await getData();
return (
<>
<h1>Todos</h1>
{todos.map((todo) => (
<div key={todo.title}>{todo.title}</div>
))}
<Link href="/">Home</Link>
</>
);
}
getData
は非同期関数で、fetch
を使用して指定されたURL(http://localhost:3000/api/todos
)からデータを取得します。fetch関数はAPIからのレスポンスを取得し、そのレスポンスをJSON形式で解析します。この関数は解析されたデータを返します。
async function getData() {
const res = await fetch("http://localhost:3000/api/todos");
return res.json();
}
データの表示
getData関数を呼び出してTODOリストのデータを取得します。
const todos: Todo[] = await getData();
取得したデータ(todos)は、map関数で展開されて表示しています。
return (
<>
<h1>Todos</h1>
{todos.map((todo) => (
<div key={todo.title}>{todo.title}</div>
))}
<Link href="/">Home</Link>
</>
);
学んだこと
最初の状態は「何から始めればいいの?」で手が止まってしまいましたが、「Next.js API」で検索しながら必要な知識を補いながら、進めていました。APIの実装に初挑戦し、以下学んだことを書きます。
フォルダ構成の理解
このプロジェクトでは、Next.jsの pages ディレクトリ内に api フォルダを作成しました。このフォルダ内にJavaScriptファイルを配置することで、APIのデータを定義することができます。この設計により、ファイルパスが自動的にエンドポイントのURLに変換されるため、APIの管理が非常に効率的になることを学びました。
非同期処理の理解
非同期処理に関して、今回のプロジェクトで完全に理解するには至りませんでしたが、fetchを使用して特定のURLからデータを取得する基本的な方法について学ぶことができました。
終わりに
簡単なTODOアプリのAPIを自分で実装することにより、APIに関する理解が深まりました。今回はpagesディレクトリを使用したフォルダ構成にしました。次回は、appルーターを導入してAPIの設計がどのように変わるのかを試してみることが楽しみです。