refactor(api): improve API error handling and revalidation

- Update API utilities to use new ApiError class
- Improve error handling and status code management
- Enhance revalidation logic for better cache management
This commit is contained in:
Thomas Camlong
2025-11-07 08:11:01 +01:00
parent 8931ed64d8
commit 5e19028cea
2 changed files with 48 additions and 22 deletions

View File

@@ -1,25 +1,17 @@
import { unstable_cache } from "next/cache"
import { METADATA_URL } from "@/constants"
import type { IconFile, IconWithName } from "@/types/icons"
/**
* Custom error class for API errors
*/
export class ApiError extends Error {
status: number
constructor(message: string, status = 500) {
super(message)
this.name = "ApiError"
this.status = status
}
}
import { ApiError } from "@/lib/errors"
import type { AuthorData, IconFile, IconWithName } from "@/types/icons"
/**
* Fetches all icon data from the metadata.json file
* Uses fetch with revalidate for caching
*/
export async function getAllIcons(): Promise<IconFile> {
try {
const response = await fetch(METADATA_URL)
const response = await fetch(METADATA_URL, {
next: { revalidate: 3600 },
})
if (!response.ok) {
throw new ApiError(`Failed to fetch icons: ${response.statusText}`, response.status)
@@ -93,16 +85,14 @@ export async function getIconData(iconName: string): Promise<IconWithName | null
}
/**
* Fetches author data from GitHub API
* Fetch author data from GitHub API (raw function without caching)
*/
export async function getAuthorData(authorId: number) {
async function fetchAuthorData(authorId: number) {
try {
const response = await fetch(`https://api.github.com/user/${authorId}`, {
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
"Cache-Control": "public, max-age=86400",
},
next: { revalidate: 86400 }, // Revalidate cache once a day
})
if (!response.ok) {
@@ -134,6 +124,26 @@ export async function getAuthorData(authorId: number) {
}
}
/**
* Cached version of fetchAuthorData
* Uses unstable_cache with tags for on-demand revalidation
* Revalidates every 86400 seconds (24 hours)
* Cache key: author-{authorId}
*
* This prevents hitting GitHub API rate limits by caching author data
* across multiple page builds and requests.
*/
export async function getAuthorData(authorId: number): Promise<AuthorData> {
return unstable_cache(
async () => await fetchAuthorData(authorId),
[`author-${authorId}`],
{
revalidate: 86400,
tags: ["authors", `author-${authorId}`],
}
)()
}
/**
* Fetches total icon count
*/

View File

@@ -1,21 +1,37 @@
"use server"
import { revalidatePath, revalidateTag } from "next/cache"
import { revalidatePath, updateTag } from "next/cache"
/**
* Revalidate the community page cache
* Can be called from server actions after submission approval/rejection
* Uses updateTag for immediate updates (read-your-writes semantics)
*/
export async function revalidateCommunityPage() {
revalidatePath("/community")
revalidateTag("community-gallery")
updateTag("community-gallery")
}
/**
* Revalidate a specific community icon page
* Use this when a specific submission's status changes
* Uses updateTag for immediate updates (read-your-writes semantics)
*/
export async function revalidateCommunityIcon(iconName: string) {
revalidatePath(`/community/${iconName}`)
updateTag("community-gallery")
updateTag("community-submission")
updateTag("community-gallery-record")
}
/**
* Revalidate all submission-related caches
* Uses updateTag for immediate updates (read-your-writes semantics)
*/
export async function revalidateSubmissions() {
revalidateTag("community-gallery")
updateTag("community-gallery")
updateTag("community-submission")
updateTag("community-gallery-record")
revalidatePath("/community")
revalidatePath("/dashboard")
}