Skip to main content

Is there any way to reduce API requests when generating pages in Nuxt.js SSG?

How to Reduce API Calls in Nuxt 3 SSG

Overview

When using SSG (Static Site Generation) in Nuxt 3, generating numerous pages can increase API requests, affecting build time and costs. This article introduces a method to efficiently reduce API calls by prefetching data in bulk.

When This Method Is Useful

  • When generating many pages, such as article lists and detail pages
  • When there are limitations on API call frequency
  • When you want to reduce build time
  • When you want to reduce server load

Solution

In standard SSG, API requests occur during the generation of each page. For example, generating 100 article detail pages requires 100 API requests.

However, by using the prefetch method, you can retrieve all data at once initially and use that data to generate each page. This significantly reduces the number of API requests.

Implementation Steps

1. Basic Prefetch Setup

First, set up an environment to prefetch data from the API.

# Create directories and files for prefetching
mkdir -p prefetch/data
touch prefetch/index.js

# Add data directory to .gitignore
echo "prefetch/data" >> .gitignore

2. Creating the Prefetch Script (prefetch/index.js)

Next, create a script to fetch and save data from the API. Save the following code in the prefetch/index.js file.

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

// Load environment variables
dotenv.config();
const ROOT_URL = process.env.NUXT_PUBLIC_API_BASE;

// Set file paths
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const EXPORT_PATH = path.join(__dirname, 'data');

// API endpoints to fetch
const ALL_LIST_ENDPOINTS = ['/rcms-api/1/example/list'];

// Create export directory
const createExportPath = async () => {
try {
await fs.access(EXPORT_PATH);
} catch (error) {
await fs.mkdir(EXPORT_PATH, { recursive: true });
}
};

// Function to fetch API data
async function kurocoAPIAll(endpoint) {
// Fetch first page
const initialData = await fetch(`${ROOT_URL}${endpoint}`).then(res => res.json());
const { list, pageInfo } = initialData;
const totalPageCnt = pageInfo.totalPageCnt;

// Fetch remaining pages in parallel
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();

// Combine all data
return [...list, ...allList];
}

// Main process
(async () => {
console.log('Starting data prefetch');

await createExportPath();

// Fetch and save data from all endpoints
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(`Data saved: ${filePath}`);
}

console.log('All data prefetch completed');
})();

3. Adding Scripts to package.json

Add scripts to package.json to easily run the prefetch process.

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

4. Implementing the List Page

Implement a list page using the prefetched data.

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>
// Load prefetched JSON file
const { data: listJSON } = await useAsyncData('filteredList', async () => {
const fullListJSON = await import('~/prefetch/data/all_rcms-api_1_example_list.json')
.then((m) => m.default);

// Extract only necessary properties (reduce data size)
return fullListJSON.map((item: any) => ({
topics_id: item.topics_id,
subject: item.subject
}));
});
</script>

5. Implementing the Detail Page

Similarly, implement a detail page using the prefetched data.

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">Back</NuxtLink>
</template>

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

// Search for matching data in the prefetched JSON file
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);

// Find item matching the ID
return fullListJSON
.map((item: any) => ({
topics_id: item.topics_id,
subject: item.subject,
content: item.content
}))
.find((item) => `${item.topics_id}` === topics_id);
});

// Display 404 error if item not found
if (!item) {
throw createError({
statusCode: 404,
statusMessage: 'Not Found'
});
}
</script>

6. Running SSG

Finally, run the prefetch and SSG processes.

# Run data prefetch
npm run prefetch

# Generate static site
npm run generate
info

After confirming that the static file generation works as expected, adjust the YAML file to add a step to run npm run prefetch before npm run generate. (When deploying with KurocoFront)

Benefits and Considerations

Benefits

  1. Reduced API Calls: Significantly reduces API calls even when generating many pages
  2. Fast Display: Pages load quickly using pre-generated HTML and JSON
  3. SEO Friendly: Generated static HTML is suitable for search engine crawlers
  4. Cost Reduction: Reducing API calls can lower API service usage fees

Considerations

  1. Data Freshness: Prefetched data is static, requiring periodic rebuilds to reflect the latest information
  2. Build Time: Prefetching and building may take longer with large amounts of data
  3. Dynamic Content: Not suitable for fully dynamic content such as user-specific data

Conclusion

The prefetch method is effective for reducing API calls in Nuxt 3 SSG. By using this method, you can efficiently generate numerous pages while minimizing API calls. This technique is particularly effective for large-scale sites or when there are limitations on API call frequency.


Support

If you have any other questions, please contact us or check out Our Slack Community.