0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TypeScript】API・DB不要!JSONファイルで一覧ページを作る方法

0
Posted at

はじめに

今回は、実際にNext.jsでweb制作をした時に遭遇したケースを共有していきます!

今からご説明する方法は以下の場面を想定した際に、使用できると考えてます。

  • ポートフォリオ:制作実績一覧
  • コーポレートサイト:事例紹介、導入企業一覧
  • 採用サイト:社員紹介、インタビュー記事
  • サービスサイト:料金プラン、機能一覧

この記事では、Next.jsのweb制作で実績一覧を実装する際にJSON + fsモジュールを用いた方法を紹介していきます!

例として簡易的に社員紹介の一覧画面を作る場面を想定してます。

この記事ではJSONファイルからfsモジュールでデータを取得するところまでを解説します。

この記事で学べること

  1. fsモジュールとは?
  2. fsモジュールを使ったJSONファイルの読み込み方
  3. DB・API不要のJSON管理方法

fsモジュールとは?

簡単に言うと、ファイルの読み書きやフォルダの操作ができるNode.jsの標準機能です。今回はこれを使ってJSONファイルを読み込みます。

The node:fs module enables interacting with the file system in a way modeled on standard POSIX functions.
— Node.js公式ドキュメントより

他のNode.jsのfsモジュールの使い方を知りたい方は以下の記事がおすすめです!

前提条件

  • Next.js 15以上(App Router使用)
  • TypeScript
  • Node.js環境

プロジェクト構成

src/
├── app/
│   └── members/
│       └── page.tsx              # メンバー一覧ページ
├── components/
│   └── MemberCard/
│       ├── MemberCard.tsx        # メンバーカード
│       └── MemberCard.module.css
├── data/
│   └── members/
│       ├── tanaka.json
│       ├── sato.json
│       └── momo.json             # 個別JSONファイル
├── lib/
│   └── getMembers.ts             # データ取得関数
└── types/
    └── member.ts                 # 型定義

1. データ構造の設計

型定義を作成

まず、メンバー情報の型を定義します。

src/types/member.ts
export interface Member {
  id: string;
  name: string;
  role: string;
  bio: string;
}

個別JSONファイルで管理

各メンバーを個別のJSONファイルとして管理します。

src/data/members/tanaka.json
{
  "id": "tanaka",
  "name": "田中太郎",
  "role": "エンジニア",
  "bio": "バックエンド開発を担当しています。Go言語とNext.jsが得意です。"
}
src/data/members/sato.json
{
  "id": "sato",
  "name": "佐藤花子",
  "role": "デザイナー",
  "bio": "UI/UXデザインを担当。Figmaを使ったデザインが得意です。"
}
src/data/members/momo.json
{
  "id": "momo",
  "name": "桃太郎",
  "role": "エンジニア",
  "bio": "AIに全て任せます!"
}

ポイント

  • 1メンバー = 1JSONファイルでメンテナンスしやすい
  • 全てのJSONファイルで同じプロパティを持たせる

2. データ取得関数の実装

Next.jsでJSONからデータを取得して表示するときにfsモジュールを使います。

サーバーコンポーネントで実装するのがセオリーです。この方法を使うことでビルド時にJSONを埋め込むことができ、クライアントからのリクエストを0回にできるためパフォーマンスが向上します。

src/lib/getMembers.ts
import fs from 'fs';
import path from 'path';
import { Member } from '@/types/member';

export function getMembers(): Member[] {
  // 1. membersフォルダの絶対パスを取得
  const membersDir = path.join(process.cwd(), 'src/data/members');
  
  // 2. フォルダ内の全ファイル名を配列で取得
  // *ビルド時にローカルファイルを読み込むだけなので、ここでは同期処理のreadFileSyncを使っています。
  const files = fs.readdirSync(membersDir);
  // → ['tanaka.json', 'sato.json', 'momo.json']

  // 3. JSONファイルを処理して配列に変換
  const members = files
    // .jsonで終わるファイルだけをフィルタリング
    .filter(file => file.endsWith('.json'))
    // → ['tanaka.json', 'sato.json', 'momo.json']
    
    // 各JSONファイルを読み込んでオブジェクトに変換
    .map(file => {
      // ファイルの絶対パスを作成
      const filePath = path.join(membersDir, file);
      
      // ファイル内容をUTF-8文字列として読み込み
      const content = fs.readFileSync(filePath, 'utf-8');
      
      // JSON文字列→JavaScriptオブジェクトに変換
      // as Member で型を指定(JSON.parseの戻り値はany型のため)
      return JSON.parse(content) as Member;
    });

  // デバッグ方法
  // console.log(members);
  return members;
}

上記の関数を実行すると、以下のような配列が返ってきます。
(ブラウザのコンソールで確認した例)

[
  0: { id: 'tanaka', name: '田中太郎', role: 'エンジニア', bio: '...' },
  1: { id: 'sato', name: '佐藤花子', role: 'デザイナー', bio: '...' },
  2: { id: 'momo', name: '桃太郎', role: 'エンジニア', bio: '...' }
]
as Member(型アサーション)について

JSON.parse() の戻り値は any 型なので、TypeScriptに「これは Member 型だよ」と教えてあげる必要があります。これを型アサーションと呼びます。

注意:型アサーションの落とし穴

as Member は実行時チェックを行いません。JSONの構造が型定義と異なっていてもエラーになりません。

今回はシンプルさを優先して型アサーションを使っていますが、データの信頼性が重要な場面では型ガードやZodなどのバリデーションライブラリを検討してください。


おわりに

この方法を使えば、APIサーバーやデータベースを用意しなくても、簡単にデータ一覧を実装できます。

特に以下のようなケースで有効と考えてます。

  • 更新頻度が低いデータ(社員紹介、実績一覧など)
  • 小〜中規模のサイト
  • コストを抑えたい場合

ぜひ試してみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?