Serving Google Fonts from the FileSystem instead of fetching from the Google API
Answered
LuisLl posted this in #help-forum
LuisLlOP
## Context:
Hi, everyone. I’m integrating the Google Fonts API to a project which has a Font Picker, I want to support the whole Google Font catalogue.
First I was doing fetch requests directly to the Google Fonts API with React Query (useInfiniteQuery) + API route, the traditional set up. But then i thought that Google Fonts don’t change often and it didn’t make sense to fetch the data fresh on every interaction.
The way Figma and Canva seem to do this is by serving the Fonts from a CDN, but I don’t have this infrastructure.
## Options:
1. Make the fetch to Google Fonts API but make sure this gets Cached by Next.js so users always get the same data back. The endpoint will still need to be Hit on every user interaction (to filter via category or name)
2. Run a script that fetches only once from the Google Fonts API and writes to my fileSystem a HUGE JSON file (20 000 lines) and locally filter and paginate the JSON on each request.
Since the filtering and pagination is done in the API route in both cases, what would you do to solve this issue?
Thank you in advance!
Hi, everyone. I’m integrating the Google Fonts API to a project which has a Font Picker, I want to support the whole Google Font catalogue.
First I was doing fetch requests directly to the Google Fonts API with React Query (useInfiniteQuery) + API route, the traditional set up. But then i thought that Google Fonts don’t change often and it didn’t make sense to fetch the data fresh on every interaction.
The way Figma and Canva seem to do this is by serving the Fonts from a CDN, but I don’t have this infrastructure.
## Options:
1. Make the fetch to Google Fonts API but make sure this gets Cached by Next.js so users always get the same data back. The endpoint will still need to be Hit on every user interaction (to filter via category or name)
2. Run a script that fetches only once from the Google Fonts API and writes to my fileSystem a HUGE JSON file (20 000 lines) and locally filter and paginate the JSON on each request.
Since the filtering and pagination is done in the API route in both cases, what would you do to solve this issue?
Thank you in advance!
Answered by LuisLl
I ended up doing this:
1. I have a function that fetches the whole Google Fonts catalogue and caches the results indefinitely (this is what I want)
2. I call an Route Handler with
So, I got rid of the huge JSON and let Next.js cache layer handle it, at the end it achieves the same goal, I only ever want this fetch to occur once ever, and all requests will re-use the cache results.
1. I have a function that fetches the whole Google Fonts catalogue and caches the results indefinitely (this is what I want)
2. I call an Route Handler with
useInfiniteQuery
to filter and paginate in memory, using the cached return value of the Google API.So, I got rid of the huge JSON and let Next.js cache layer handle it, at the end it achieves the same goal, I only ever want this fetch to occur once ever, and all requests will re-use the cache results.
4 Replies
Mucuchies
i'd say do the api one. The json is bound to go wrong. And its more expensive on IO on e.g. vercel
LuisLlOP
I ended up doing this:
1. I have a function that fetches the whole Google Fonts catalogue and caches the results indefinitely (this is what I want)
2. I call an Route Handler with
So, I got rid of the huge JSON and let Next.js cache layer handle it, at the end it achieves the same goal, I only ever want this fetch to occur once ever, and all requests will re-use the cache results.
1. I have a function that fetches the whole Google Fonts catalogue and caches the results indefinitely (this is what I want)
2. I call an Route Handler with
useInfiniteQuery
to filter and paginate in memory, using the cached return value of the Google API.So, I got rid of the huge JSON and let Next.js cache layer handle it, at the end it achieves the same goal, I only ever want this fetch to occur once ever, and all requests will re-use the cache results.
Answer
LuisLlOP
const cachedFetchGoogleFonts = unstable_cache(fetchGoogleFonts, ["google-fonts-catalogue"], {
tags: ["google-fonts-catalogue"],
});
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const query = searchParams.get("q")?.toLowerCase() || "";
const category = searchParams.get("category")?.toLowerCase();
const limit = Math.min(Number(searchParams.get("limit")) || 50, 100);
const offset = Number(searchParams.get("offset")) || 0;
let googleFonts = FALLBACK_FONTS;
try {
googleFonts = await cachedFetchGoogleFonts(process.env.GOOGLE_FONTS_API_KEY);
} catch (error) {
console.error("Error fetching Google Fonts:", error);
console.log("Using fallback fonts");
}
// Filter fonts based on search query and category
let filteredFonts = googleFonts;
if (query) {
filteredFonts = filteredFonts.filter((font) => font.family.toLowerCase().includes(query));
}
if (category && category !== "all") {
filteredFonts = filteredFonts.filter((font) => font.category === category);
}
const paginatedFonts = filteredFonts.slice(offset, offset + limit);
const response: PaginatedFontsResponse = {
fonts: paginatedFonts,
total: filteredFonts.length,
offset,
limit,
hasMore: offset + limit < filteredFonts.length,
};
return NextResponse.json(response);
} catch (error) {
console.error("Error in Google Fonts API:", error);
return NextResponse.json({ error: "Failed to fetch fonts" }, { status: 500 });
}
}
LuisLlOP
Now data lives in .next folder, but I'm not longer responsible of it, and It's only ever fetched once 😄