
How I Made My Next.js App Resilient After Vercel's API Fetching Time Limits

Table Of Contents
A few months ago, I was working on a Next.js project for a client. Everything was going smoothly—pages were loading fast, data flowed seamlessly, and my API calls were rock solid. Or so I thought.
One morning, I woke up to a frantic message:
"Hey! The app isn’t loading data properly. It’s just… blank. Clients are complaining. Please check it ASAP!"
That’s when the nightmare started.
The Root of the Problem
After digging into the logs, I noticed several API calls failed due to intermittent server issues. Usually, these calls would self-correct after a page refresh or two, but this time, they weren't.
What made things worse? Vercel had recently introduced stricter time limits on API requests. You were out of luck if your API call didn't return within a specific window. Boom. Failure. No retries, no second chances.
And, of course, this happened on the day the API I was using was acting up.
The "Aha!" Moment
I knew I couldn't keep relying on a "hope-it-works" strategy. I needed a way to make my API calls more resilient. What if, instead of immediately failing when an API call hit a bump, my app could pause, retry, and try again?
That's when the idea for fetchWithRetry was born—a simple function that would give my app the power to recover from transient API failures without breaking a sweat.
Building fetchWithRetry: A Resilient API Fetcher
I sat down, brewed a strong cup of coffee, and got to work. My mission? To create a function that could:
- Retry failed API calls a specific number of times.
- Wait a few seconds between retries to give the server a chance to recover.
- Throw an error only after all retries have been exhausted.
After a few hours of tinkering, I had this:
const fetchWithRetry = async (
fetchFunction: () => Promise<any>,
maxRetries: number = 3,
delay: number = 4000
): Promise<any> => {
let retries = maxRetries;
while (retries > 0) {
try {
return await fetchFunction(); // Try the API call
} catch (error) {
retries -= 1; // Reduce the retry count
if (retries === 0) {
// All retries failed
throw new Error("Failed to fetch data after multiple retries.");
}
console.warn(
`Fetch failed. Retrying... (${maxRetries - retries}/${maxRetries})`
);
// Wait before retrying
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
};
export { fetchWithRetry };
How It Saved My App
I plugged this function into my Next.js project. The first test was in getServerSideProps, where the app fetched data for server-side rendering.
The New getServerSideProps
import { fetchWithRetry } from "./utils/fetchWithRetry";
export const getServerSideProps = async () => {
try {
const data = await fetchWithRetry(async () => {
const res = await fetch("https://api.example.com/data");
if (!res.ok) throw new Error("Failed to fetch");
return res.json();
});
return { props: { data } };
} catch (error) {
console.error("Error fetching data:", error.message);
return { props: { data: null } };
}
};
The difference was night and day. If an API call fails once, the app will try again. And again. And—if necessary—a third time. The data loaded without a hitch if the server recovered within those attempts. There are no blank screens. No angry clients.
A Real-Life Scenario
Let me walk you through how this works in practice.
Imagine your app calls an API to fetch weather data for a user's location. The API server might return an error or timeout on a bad day due to high traffic. Without retry logic, your app would fail immediately, leaving users stuck.
With fetchWithRetry, here's what happens:
- First Attempt: The app makes the API call. The server is unresponsive—result: Retry #1.
- Second Attempt: The server is still struggling, but it's starting to recover—result: Retry #2.
- Third Attempt: The server responds successfully, and the app loads the data. Result: Problem solved without the user noticing a thing.
Lessons Learned
Here's what I realized after implementing fetchWithRetry:
- Retry Logic is Essential; downtime or missing data can impact your users in production apps.
- Delays Make a Difference: Giving the server a moment to recover often resolves transient failures.
- Exponential Backoff is Next-Level: For even better results, you can increase the delay with each retry (e.g., 2 seconds, then 4 seconds, then 8 seconds).
The Payoff
Thanks to this simple function, my Next.js app became much more resilient. The blank screens disappeared, my client was happy, and I could finally sleep at night knowing my app could handle hiccups gracefully.
If you're building a Next.js app and you're worried about API failures—especially with Vercel's time limits—give fetchWithRetry a try. It might just save you a frantic call from your client one day.
Final Thoughts
Have you ever faced challenges with API failures in your apps? Share your story with me by scheduling a meeting, and let's talk about how we can build stronger, more reliable applications together. 🚀