Files
blog-eleventy-indiekit/lib/data-fetch.js
Ricardo 0fe99ee5b1 perf: add timeout and watch-mode cache extension to all data files
Introduce shared cachedFetch helper (lib/data-fetch.js) wrapping
EleventyFetch with two protections:

- 10-second hard timeout via AbortController on every network request,
  preventing slow or unresponsive APIs from hanging the build
- 4-hour cache TTL in watch/serve mode (vs 5-15 min originals), so
  incremental rebuilds serve from disk cache instead of re-fetching
  APIs every time a markdown file changes

All 13 network _data files updated to use cachedFetch. Production
builds keep original short TTLs for fresh data.

Targets the "Data File" benchmark (12,169ms / 32% of incremental
rebuild) — the largest remaining bottleneck after filter memoization.

Confab-Link: http://localhost:8080/sessions/0b241cd6-aff2-4fec-853c-2b5a61e61946
2026-03-10 17:11:24 +01:00

55 lines
1.6 KiB
JavaScript

/**
* Shared data-fetching helper for _data files.
*
* Wraps @11ty/eleventy-fetch with two protections:
* 1. Hard timeout — 10-second AbortController ceiling on every request
* 2. Watch-mode cache extension — uses "4h" TTL during watch/serve,
* keeping the original (shorter) TTL only for production builds
*
* Usage:
* import { cachedFetch } from "../lib/data-fetch.js";
* const data = await cachedFetch(url, { duration: "15m", type: "json" });
*/
import EleventyFetch from "@11ty/eleventy-fetch";
const FETCH_TIMEOUT_MS = 10_000; // 10 seconds
// In watch/serve mode, extend cache to avoid re-fetching on every rebuild.
// Production builds use the caller's original TTL for fresh data.
const isWatchMode = process.env.ELEVENTY_RUN_MODE !== "build";
const WATCH_MODE_DURATION = "4h";
/**
* Fetch with timeout and watch-mode cache extension.
*
* @param {string} url - URL to fetch
* @param {object} options - EleventyFetch options (duration, type, fetchOptions, etc.)
* @returns {Promise<any>} Parsed response
*/
export async function cachedFetch(url, options = {}) {
// Extend cache in watch mode
const duration = isWatchMode ? WATCH_MODE_DURATION : (options.duration || "15m");
// Create abort controller for hard timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
try {
const fetchOptions = {
...options.fetchOptions,
signal: controller.signal,
};
const result = await EleventyFetch(url, {
...options,
duration,
fetchOptions,
});
return result;
} finally {
clearTimeout(timeoutId);
}
}