---
title: Nuxt.jsのSSGを使用してAPIコール回数を削減できますか？
description: 事前に一覧の全データをローカルに保存しておくことで、ページ内リンクされていないページを生成することが可能です。
category: howto
---

import StructuredDataFaqs from '@site/src/components/StructuredDataFaqs';

<StructuredDataFaqs faqs={[
    { question: "Nuxt.jsのSSGで、ページの生成時にAPIリクエストを減らす方法はありますか？", answer: "事前に一覧の全データをローカルに保存しておくことで、ページ内リンクされていないページを生成することが可能です。" }
]} />

## Nuxt 3でAPIコール回数を削減する方法

### 概要

Nuxt 3でSSG（静的サイト生成）を使用する際、多数のページを生成するとAPIリクエストが増加し、ビルド時間やコストに影響することがあります。この記事では、**事前にデータを一括取得**することで、APIコール回数を効率的に削減する方法を紹介します。

### この方法が役立つケース

- 記事一覧と詳細ページなど、多数のページを生成する場合
- APIコール回数に制限がある場合
- ビルド時間を短縮したい場合
- サーバー負荷を軽減したい場合

### 解決方法

通常のSSGでは、各ページの生成時にAPIリクエストが発生します。例えば、100記事の詳細ページを生成する場合、100回のAPIリクエストが必要になります。

しかし、**プリフェッチ方式**を使用すると、最初に一括でデータを取得し、そのデータを使って各ページを生成できます。これにより、APIリクエスト回数を大幅に削減できます。

## 実装手順

### 1. プリフェッチの基本設定

まず、APIからデータを事前に取得するための環境を構築します。

```bash
# プリフェッチ用のディレクトリとファイルを作成
mkdir -p prefetch/data
touch prefetch/index.js

# .gitignoreにデータディレクトリを追加
echo "prefetch/data" >> .gitignore
```

### 2. プリフェッチスクリプトの作成 (prefetch/index.js)

次に、APIからデータを取得して保存するスクリプトを作成します。以下のコードを `prefetch/index.js` ファイルに保存します。

```javascript
// prefetch/index.js
import fs from 'fs/promises';
import path from 'path';
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';

// 環境変数の読み込み
dotenv.config();
const ROOT_URL = process.env.NUXT_PUBLIC_API_BASE;

// ファイルパスの設定
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const EXPORT_PATH = path.join(__dirname, 'data');

// 取得対象のAPIエンドポイント一覧
const ALL_LIST_ENDPOINTS = ['/rcms-api/1/example/list'];

// エクスポート先ディレクトリの作成
const createExportPath = async () => {
    try {
        await fs.access(EXPORT_PATH);
    } catch (error) {
        await fs.mkdir(EXPORT_PATH, { recursive: true });
    }
};

// APIデータを取得する関数
async function kurocoAPIAll(endpoint) {
    // 最初のページを取得
    const initialData = await fetch(`${ROOT_URL}${endpoint}`).then(res => res.json());
    const { list, pageInfo } = initialData;
    const totalPageCnt = pageInfo.totalPageCnt;
    
    // 2ページ目以降を並列で取得
    const promises = [];
    for (let i = 2; i <= totalPageCnt; i++) {
        promises.push(
            fetch(`${ROOT_URL}${endpoint}?pageID=${i}`).then(res => res.json())
        );
    }
    
    const allData = await Promise.all(promises);
    const allList = allData.map((data) => data.list).flat();
    
    // 全てのデータを結合
    return [...list, ...allList];
}

// メイン処理
(async () => {
    console.log('データのプリフェッチを開始します');
    
    await createExportPath();
    
    // 全てのエンドポイントからデータを取得して保存
    for (const endpoint of ALL_LIST_ENDPOINTS) {
        const data = await kurocoAPIAll(endpoint);
        const fileName = endpoint.replaceAll('/', '_');
        const filePath = path.join(EXPORT_PATH, `all${fileName}.json`);
        
        await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
        console.log(`データを保存しました: ${filePath}`);
    }
    
    console.log('全てのデータのプリフェッチが完了しました');
})();
```

### 3. package.jsonにスクリプトを追加

プリフェッチを簡単に実行できるようにするため、package.jsonにスクリプトを追加します。

```json
"scripts": {
    "prefetch": "node prefetch/index.js"
}
```

### 4. 一覧ページの実装

プリフェッチしたデータを使用して、一覧ページを実装します。

```markup title="pages/ssg_list/index.vue"
<template>
    <ul>
        <li v-for="item in listJSON" :key="item.topics_id">
            <NuxtLink :to="`/ssg_list/${item.topics_id}`">{{ item.subject }}</NuxtLink>
        </li>
    </ul>
</template>

<script lang="ts" setup>
// プリフェッチしたJSONファイルを読み込む
const { data: listJSON } = await useAsyncData('filteredList', async () => {
    const fullListJSON = await import('~/prefetch/data/all_rcms-api_1_example_list.json')
        .then((m) => m.default);
    
    // 必要なプロパティのみを抽出（データ量削減）
    return fullListJSON.map((item: any) => ({
        topics_id: item.topics_id,
        subject: item.subject
    }));
});
</script>
```

### 5. 詳細ページの実装

同様に、詳細ページもプリフェッチしたデータを使用して実装します。

```markup title="pages/ssg_list/[topics_id].vue"
<template>
    <template v-if="item">
        <h1>
            <span>{{ item.subject }}</span>
        </h1>
        <section v-if="item.content">
            <div v-for="(content, idx) in item.content" :key="idx" 
                 v-html="content.content_wysiwyg"></div>
        </section>
    </template>

    <NuxtLink to="/ssg_list">戻る</NuxtLink>
</template>

<script lang="ts" setup>
const { topics_id } = useRoute().params;

// プリフェッチしたJSONファイルから該当データを検索
const { data: item } = await useAsyncData(`filteredList-${topics_id}`, async () => {
    const fullListJSON = await import('~/prefetch/data/all_rcms-api_1_example_list.json')
        .then((m) => m.default);
    
    // IDに一致するアイテムを検索
    return fullListJSON
        .map((item: any) => ({
            topics_id: item.topics_id,
            subject: item.subject,
            content: item.content
        }))
        .find((item) => `${item.topics_id}` === topics_id);
});

// アイテムが見つからない場合は404エラーを表示
if (!item) {
    throw createError({
        statusCode: 404,
        statusMessage: 'Not Found'
    });
}
</script>
```

### 6. SSGの実行

最後に、プリフェッチとSSGを実行します。

```bash
# データのプリフェッチを実行
npm run prefetch

# 静的サイトを生成
npm run generate
```

:::info
想定通りの静的ファイル生成が確認できたらYAMLファイルを調整してnpm run generate の前にnpm run prefetchを実行するステップを追加してください。(KurocoFrontでデプロイする場合)
:::

## メリットと注意点

### メリット

1. **APIコール削減**: 多数のページを生成する場合でも、APIコール回数を大幅に削減できます
2. **高速な表示**: 事前に生成されたHTMLとJSONを使用するため、ページ表示が高速です
3. **SEO対策**: 静的HTMLが生成されるため、検索エンジンのクローラーに適しています
4. **コスト削減**: APIコール回数の削減により、APIサービスの利用料金を削減できます

### 注意点

1. **データの鮮度**: プリフェッチしたデータは静的なため、最新の情報を反映するには定期的に再ビルドする必要があります
2. **ビルド時間**: データ量が多い場合、プリフェッチとビルドに時間がかかる場合があります
3. **動的コンテンツ**: ユーザー固有のデータなど、完全に動的なコンテンツには適していません

## まとめ

Nuxt 3のSSGでAPIコール回数を削減するには、**プリフェッチ方式**が効果的です。この方法を使うことで、多数のページを効率的に生成しながら、APIコール回数を最小限に抑えることができます。特に大規模なサイトや、APIコール回数に制限がある場合に有効な手法です。

