Handle API errors gracefully in your storefront
try {
const response = await fetch(`${API_BASE}/locations/${locationId}/products`, {
headers: { 'X-API-Key': API_KEY }
});
} catch (error) {
if (error.name === 'TypeError') {
// Network error - show offline message
showOfflineMessage();
}
}
const handleAPIResponse = async (response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({
message: 'Unknown error occurred'
}));
switch (response.status) {
case 401:
throw new Error('Invalid API key');
case 404:
throw new Error('Resource not found');
case 429:
throw new Error('Rate limit exceeded. Please try again later.');
case 500:
throw new Error('Server error. Please try again.');
default:
throw new Error(error.message || 'Request failed');
}
}
return response.json();
};
const addToCart = async (product, quantity) => {
try {
const updatedCart = await addItemToCart(locationId, cartId, product, quantity);
setCart(updatedCart);
} catch (error) {
// Handle specific cart errors
if (error.message.includes('out of stock')) {
showOutOfStockMessage(product.name);
} else if (error.message.includes('minimum order')) {
showMinimumOrderMessage();
} else {
showGenericErrorMessage();
}
}
};
const retryRequest = async (fn, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
};
const fetchMenuWithFallback = async (locationId) => {
try {
return await fetchMenu(locationId);
} catch (error) {
console.warn('Failed to fetch menu, using cached data:', error);
return getCachedMenu(locationId) || getStaticMenuData();
}
};
const rateLimitedRequest = async (requestFn) => {
try {
return await requestFn();
} catch (error) {
if (error.message.includes('rate limit')) {
// Wait and retry after rate limit period
await new Promise(resolve => setTimeout(resolve, 60000));
return requestFn();
}
throw error;
}
};
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const loadProducts = async () => {
setLoading(true);
setError(null);
try {
const products = await fetchProducts(locationId);
setProducts(products);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
class APIErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h2>Something went wrong</h2>
<p>Please refresh the page or try again later.</p>
<button onClick={() => window.location.reload()}>
Refresh Page
</button>
</div>
);
}
return this.props.children;
}
}