Next.js API 内でファイルを取得する方法のメモ
Next.js app router でプロトタイプのアプリを作っています。
DBからデータを取得して返す部分を Next.js の API でモック的に作っており、API 内でダミーのデータファイルを取得してフロントに返す実装をしたメモ。
/root
|-/public
| |-/data
| |-dummyData.json #ダミーのデータファイル
|-/src
|-/api
|-/users
|-route.ts
1. fs でファイルあを取得して返す
import path from "path";
import fs from "fs";
import { NextRequest } from "next/server";
export const GET = async (req: NextRequest) => {
try {
const filePath = path.join(process.cwd(), "/public/data/dummyData.json");
const file = JSON.parse((fs.readFileSync(filePath, "utf8"));
return Response.json({data: file}, {status: 200});
} catch(error) {
console.log('Error: Failed to read file', error);
return Response.json({error: "Failed to read file"}, {status: 500});
}
};
process.cwd()
で node コマンドが実行されている root のディレクトリが取得できるので、それを起点に path.join
でファイルへのパスを作り、fs.readFileSync
でファイルを取得する方法。
fs.readFileSync
はただのテキストファイルになるので JSON.parse
で JSON に変換してから返した
process.cwd()
Theprocess.cwd()
method returns the current working directory of the Node.js process.
https://nodejs.org/api/process.html#processcwd
fs.readFileSync(path[, options])
For detailed information, see the documentation of the asynchronous version of this API:fs.readFile()
.
If the encoding option is specified then this function returns a string. Otherwise it returns a buffer.
https://nodejs.org/api/fs.html#fsreadfilesyncpath-options
2. API 内から fetch する
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000
import path from "path";
import { NextRequest } from "next/server";
const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:3000";
export const GET = async (req: NextRequest) => {
try {
const json = await fetch(
path.join(baseURL, "/public/data/dummyData.json")
).then((res) => {
if (res.status !== 200) {
throw new Error("Fetch Error");
}
return res.json();
});
return Response.json({data: json}, {status: 200});
} catch(error) {
console.log('Error: Failed to fetch file', error);
return Response.json({error: "Failed to fetch file"}, {status: 500});
}
};
API 内から fetch する際に /
始まりで fetch できないので、アプリの protocl と host が必要になる。
プロトタイプなら .env
に定義してしまうのが楽。
req
から protocl と host を取得することもできた
import path from "path";
import { NextRequest } from "next/server";
const buildBasePath = (req: Request, fallbackHost: string = "localhost:3000") => {
const protocl = req.headers.get("x-forwarded-proto") ?? "http";
const host = req.headers.get("host") ?? fallbackHost;
return `${protocol}://${host}`;
};
export const GET = async (req: NextRequest) => {
try {
const baseURL = buildBasePath(req);
const json = await fetch(
path.join(baseURL, "/public/data/dummyData.json")
).then((res) => res.json());
return Response.json({data: json}, {status: 200});
} catch (error) {
// 略
}
};
他にも req.url
が API 自身の URL になっているので、不要なパスを replace で消してしまうのも楽かもしれない
X-Forwarded-Proto
X-Forwarded-Proto
(XFP) ヘッダーは、プロキシーまたはロードバランサーへ接続するのに使っていたクライアントのプロトコル (HTTP または HTTPS) を特定するために事実上の標準となっているヘッダーです。サーバーのアクセスログにはサーバーとロードバランサーの間で使われたプロトコルが含まれていますが、クライアントとロードバランサーの間で使用されたプロトコルは含まれていません。クライアントとロードバランサーの間で使用されたプロトコルを特定するには、X-Forwarded-Proto
リクエストヘッダーを使用することができます。
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-Proto
追記 nextUrl から origin を取得する方法が良さそう
export const GET = async (req: NextRequest) => {
try {
const baseURL = req.nextUrl.origin;
const json = await fetch(
path.join(baseURL, "/public/data/dummyData.json")
).then((res) => res.json());
return Response.json({data: json}, {status: 200});
} catch (error) {
// 略
}
};
nextUrl
Extends the native URL API with additional convenience methods, including Next.js specific properties.
https://nextjs.org/docs/app/api-reference/functions/next-request#nexturl
3. json なら require で取得できる
そもそも JSON ファイルなら const json = require("/path/to/file.json")
で取得できる
export const GET = async (req: NextRequest) => {
try {
const data = require("../../../../public/data/dummyData.json");
return Response.json({ data }, {status: 200});
} catch (error) {
console.log('Error: Failed to read file', error);
return Response.json({error: "Failed to read file"}, {status: 500});
}
};
この方法なら /public
にデータを置いておく必要もない。
ただし取得したいデータが .geojson
など設定外の拡張子の場合はエラーになるので next.config.ts
などで設定する必要がある
おわり
Discussion