Nuxt 4 SSGでローカルJSONファイルを正しく読み込む方法
はじめに Nuxt 4でStatic Site Generation (SSG) を行う際、ローカルのJSONファイルからデータを読み込んで静的ページを生成したいケースがあります。しかし、Next.jsのgetStaticPropsのようにシンプルにはいかず、いくつかのハマりポイントがあります。
本記事では、試行錯誤の末に見つけた正しいアプローチを紹介します。
問題:なぜ単純なfsの読み込みでは動かないのか 最初に試したアプローチ(失敗) // ❌ これは動かない const fetchLocalData = async (filePath: string) => { if (import.meta.server) { const fs = await import('fs'); const path = await import('path'); const fullPath = path.resolve(process.cwd(), 'public/data', filePath); const data = fs.readFileSync(fullPath, 'utf-8'); return JSON.parse(data); } // クライアントサイド const response = await fetch(`/data/${filePath}`); return await response.json(); }; このアプローチには以下の問題があります:
process.cwd()がビルド環境で異なる: ローカル開発とVercel等のビルド環境では作業ディレクトリが異なる Nitroのプリレンダリング時にファイルが見つからない : SSG時、Nitroは独自のコンテキストで動作する useAsyncDataなしでは、クライアントサイドでも実行される : SSGの意味がなくなる 解決策:Nitro Storage API + Server APIルート + useAsyncData アーキテクチャ [SSGビルド時] Page Component ↓ useAsyncData Server API Route (/api/local-data/[...path]) ↓ useStorage (Nitro Storage API) public/data/*.json [生成されたHTML] _payload.json にデータが埋め込まれる → クライアントサイドでのJSON読み込み不要 Step 1: nuxt.config.tsでserverAssetsを設定 // nuxt.config.ts export default defineNuxtConfig({ nitro: { prerender: { crawlLinks: true, failOnError: false, }, // Nitro Storage APIでpublic/dataをサーバーアセットとしてマウント serverAssets: [{ baseName: 'data', dir: './public/data' }], }, routeRules: { '/**': { prerender: true }, }, }); Step 2: Server APIルートを作成(Nitro Storage + fsフォールバック) // server/api/local-data/[...path].ts import { readFileSync, existsSync } from 'fs'; import { resolve } from 'path'; export default defineEventHandler(async (event) => { const pathParam = getRouterParam(event, 'path'); if (!pathParam) { throw createError({ statusCode: 400, message: 'Path is required' }); } const filePath = Array.isArray(pathParam) ? pathParam.join('/') : pathParam; // セキュリティ: パストラバーサル防止 if (filePath.includes('..')) { throw createError({ statusCode: 400, message: 'Invalid path' }); } // Nitro Storage APIを使用 // nuxt.config.tsのserverAssetsで定義した'data'ストレージにアクセス const storage = useStorage('assets:data'); // ファイルパスをストレージキーに変換(/を:に置換) const storageKey = filePath.replace(/\//g, ':'); // Storage APIで取得を試みる if (await storage.hasItem(storageKey)) { const data = await storage.getItem(storageKey); return data; } // フォールバック: 開発環境向けにfsで直接読み込み const fsPath = resolve(process.cwd(), 'public/data', filePath); if (existsSync(fsPath)) { const data = readFileSync(fsPath, 'utf-8'); return JSON.parse(data); } throw createError({ statusCode: 404, message: `File not found: ${filePath}` }); }); Nitro Storage APIを使うメリット:
...
2025年12月11日 · 更新: 2025年12月11日 · 4 分 · Nakamura