fix(rates): reduce cache TTL, expose fallback header and show stale data warning
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
RATES_CACHE_CONTROL_VALUE,
|
||||
} from "@/lib/server/rates-cache";
|
||||
|
||||
export const revalidate = 300;
|
||||
export const revalidate = 60;
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
@@ -19,13 +19,18 @@ export async function GET() {
|
||||
});
|
||||
} catch (error) {
|
||||
const cachedRates = getLastCachedRates();
|
||||
const fallbackErrorMessage =
|
||||
error instanceof Error
|
||||
? error.message.replace(/[\r\n]+/g, " ")
|
||||
: "Upstream provider error";
|
||||
|
||||
if (cachedRates) {
|
||||
return NextResponse.json(cachedRates, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Cache-Control": RATES_CACHE_CONTROL_VALUE,
|
||||
"X-Cache-Fallback": "stale-on-error"
|
||||
"X-Cache-Fallback": "stale-on-error",
|
||||
"X-Cache-Fallback-Error": fallbackErrorMessage
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -243,6 +243,24 @@ export function ConverterCard({
|
||||
}
|
||||
};
|
||||
|
||||
const displayUpdatedAt = useMemo(() => {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timestamps = [new Date(data.updatedAt).getTime()];
|
||||
|
||||
if (marketData?.updatedAt) {
|
||||
timestamps.push(new Date(marketData.updatedAt).getTime());
|
||||
}
|
||||
|
||||
const latest = Math.max(
|
||||
...timestamps.filter((timestamp) => Number.isFinite(timestamp)),
|
||||
);
|
||||
|
||||
return Number.isFinite(latest) ? new Date(latest).toISOString() : data.updatedAt;
|
||||
}, [data, marketData?.updatedAt]);
|
||||
|
||||
if (isLoading && !data) {
|
||||
return <ConverterSkeleton />;
|
||||
}
|
||||
@@ -497,7 +515,7 @@ export function ConverterCard({
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-[0.12em]">Last updated</p>
|
||||
<p className="mt-1 text-sm text-foreground">
|
||||
{formatTimestamp(data.updatedAt)}
|
||||
{formatTimestamp(displayUpdatedAt ?? data.updatedAt)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,9 +28,11 @@ export function useMarketRates() {
|
||||
|
||||
const payload = await response.json();
|
||||
const parsed = parseRatesResponse(payload);
|
||||
const isFallback = response.headers.get("X-Cache-Fallback") === "stale-on-error";
|
||||
const fallbackError = response.headers.get("X-Cache-Fallback-Error");
|
||||
|
||||
setData(parsed);
|
||||
setError(null);
|
||||
setError(isFallback ? (fallbackError ?? "Upstream provider error") : null);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Unknown error";
|
||||
setError(message);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { fetchUnifiedRates } from "@/lib/api/normalize";
|
||||
import type { RatesResponse } from "@/lib/rates";
|
||||
|
||||
export const RATES_CACHE_TTL_MS = 300_000;
|
||||
export const RATES_CACHE_TTL_MS = 60_000;
|
||||
export const RATES_CACHE_CONTROL_VALUE =
|
||||
"s-maxage=300, stale-while-revalidate=1800";
|
||||
"s-maxage=60, stale-while-revalidate=600";
|
||||
|
||||
let cachedRates: RatesResponse | null = null;
|
||||
let cacheTimestamp = 0;
|
||||
|
||||
Reference in New Issue
Block a user