From 42d1e17db9be991010bf046fa32c7ad893c83b42 Mon Sep 17 00:00:00 2001 From: developharsh Date: Wed, 28 Jan 2026 17:11:36 +0530 Subject: [PATCH] fest:API robustness enhancement#3670 Signed-off-by: developharsh --- lib/api.ts | 80 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/lib/api.ts b/lib/api.ts index 53464470..b1835cf2 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -3,8 +3,25 @@ export const dynamic = 'force-dynamic'; const API_URL = process.env.WORDPRESS_API_URL || process.env.NEXT_PUBLIC_WORDPRESS_API_URL -async function fetchAPI(query = "", { variables }: Record = {}) { - const headers = { "Content-Type": "application/json" }; +const DEFAULT_TIMEOUT_MS = 10000; +const envTimeoutMs = Number.parseInt( + process.env.WORDPRESS_API_TIMEOUT_MS || "", + 10 +); +// can be overidden by setting the env , else the default will be used +const DEFAULT_REQUEST_TIMEOUT_MS = Number.isFinite(envTimeoutMs) + ? envTimeoutMs + : DEFAULT_TIMEOUT_MS; + +async function fetchAPI( + query = "", + { variables, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS }: Record = {} +) { + if (!API_URL) { + throw new Error("WORDPRESS_API_URL is not configured"); + } + + const headers: Record = { "Content-Type": "application/json" }; if (process.env.WORDPRESS_AUTH_REFRESH_TOKEN) { headers[ @@ -12,21 +29,52 @@ async function fetchAPI(query = "", { variables }: Record = {}) { ] = `Bearer ${process.env.WORDPRESS_AUTH_REFRESH_TOKEN}`; } // WPGraphQL Plugin must be enabled - const res = await fetch(API_URL, { - headers, - method: "POST", - body: JSON.stringify({ - query, - variables, - }), - }); - - const json = await res.json(); - if (json.errors) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + + let res: Response; + try { + res = await fetch(API_URL, { + headers, + method: "POST", + body: JSON.stringify({ + query, + variables, + }), + signal: controller.signal, + }); + } catch (err: any) { + const reason = err?.name === "AbortError" ? "Request timed out" : "Network error"; + throw new Error(`${reason}: ${err?.message || "unknown"}`); + } finally { + clearTimeout(timeoutId); + } + + const contentType = res.headers.get("content-type") || ""; + const isJson = contentType.includes("application/json"); + const rawBody = isJson ? null : await res.text(); + + let json: any = null; + if (isJson) { + try { + json = await res.json(); + } catch (err: any) { + throw new Error(`Invalid JSON response (status ${res.status}): ${err?.message || "parse failed"}`); + } + } + + if (!res.ok) { + const bodySnippet = isJson ? JSON.stringify(json) : rawBody; + throw new Error( + `HTTP ${res.status} ${res.statusText}: ${bodySnippet?.slice(0, 500) || "no body"}` + ); + } + + if (json?.errors) { console.error(json.errors); - throw new Error("Failed to fetch API"); + throw new Error(`GraphQL errors: ${JSON.stringify(json.errors).slice(0, 500)}`); } - return json.data; + return json?.data; } export async function getPreviewPost(id, idType = "DATABASE_ID") { @@ -844,4 +892,4 @@ export async function getAllPostsForSearch(preview = false) { ); return data?.posts?.edges || []; -} \ No newline at end of file +}