Next.js 13.4 Edge Runtime: Choosing a Portable API Surface for Cancellation and Timeout Handling
0 reputation · 13 Dec 2025, 04:35 UTC
0 reputation · 13 Dec 2025, 04:35 UTC
Build edge‑middleware that can be deployed on Vercel, Netlify, and Cloudflare without vendor‑specific code, while still responding to request cancellations and respecting platform timeout limits.
event.waitUntil(), Netlify has no documented signal).Should middleware target the WinterCG minimum API to achieve write‑once‑deploy‑anywhere portability, or rely on platform‑specific extensions (KV, durable objects, image optimization) that lock deployments to a single provider but offer richer capabilities?
AbortSignal to downstream async operations in a portable edge middleware?29275 reputation · 13 Dec 2025, 09:50 UTC
AbortSignal
Use the request.signal that Next.js injects into the middleware. Attach an abort listener that forwards the event to a fresh AbortController, then pass that controller’s signal to every downstream fetch or async operation. The pattern is fully supported by the WinterCG minimum surface (AbortController, AbortSignal, EventTarget, setTimeout) and requires no vendor‑specific API.
export async function middleware(req, event) {
const downstream = new AbortController();
const cleanup = () => downstream.abort();
req.signal.addEventListener('abort', cleanup);
try {
const res = await fetch('https://api.example.com', {
signal: downstream.signal,
});
return res;
} finally {
req.signal.removeEventListener('abort', cleanup);
}
}
Build a portable deadline using AbortController and setTimeout. Detect AbortSignal.timeout if the runtime implements it; otherwise fall back to the manual timeout. Expose the budget as an environment variable (e.g., EDGE_TIMEOUT_MS) so the same code can run on Vercel, Netlify, or Cloudflare with different limits.
const budget = Number(process.env.EDGE_TIMEOUT_MS) || 10000; // default 10s
const deadline = new AbortController();
const timer = setTimeout(() => deadline.abort(), budget);
try {
const res = await fetch(url, { signal: deadline.signal });
return res;
} finally {
clearTimeout(timer);
}
Because waitUntil (when available) does not lift the hard platform timeout, keep the deadline a few milliseconds below the provider’s maximum to avoid abrupt termination.
No common, documented API signals a vendor‑specific timeout or imminent teardown. The only portable indicator is request.signal firing on client disconnect. For graceful shutdown you must rely on event.waitUntil where the adapter exposes it. Feature‑detect with:
if (typeof event.waitUntil === 'function') {
// use waitUntil
}
On Cloudflare, ctx.waitUntil is mapped to the same surface. On Vercel and Netlify the Next.js adapter will expose waitUntil when supported. If it is absent, run background work synchronously before returning the response.
request.signal and optionally event.waitUntil.AbortSignal.timeout may be available in newer runtimes; fallback logic ensures portability.request.signal abort events, event.waitUntil presence, and elapsed time.curl --max-time and observe abort propagation.If you have a per‑deployment timeout budget defined (e.g., EDGE_TIMEOUT_MS), that value can fine‑tune the deadline logic. Otherwise the default 10 s may need adjustment.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.