frontend-api-integration-patterns
Standardizes async API handling to prevent stale data, flickering UI, and duplicated network requests.
Install
mkdir -p .claude/skills/frontend-api-integration-patterns && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15760" && unzip -o skill.zip -d .claude/skills/frontend-api-integration-patterns && rm skill.zipInstalls to .claude/skills/frontend-api-integration-patterns
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Production-ready patterns for integrating frontend applications with backend APIs, including race condition handling, request cancellation, retry strategies, error normalization, and UI state management.Key capabilities
- →Centralize API logic and normalize errors using a custom error class
- →Prevent stale responses from overwriting fresh data with race-safe state management
- →Cancel in-flight requests using AbortController to avoid memory leaks
- →Retry transient failures (5xx or network errors) with exponential backoff
- →Avoid excessive API calls using debounced API calls for input-driven scenarios
- →Prevent duplicate API calls across components with request deduplication
How it works
This skill provides patterns for integrating frontend applications with backend APIs, focusing on handling asynchronous behavior to prevent issues like race conditions and stale data. It includes strategies for error normalization, request cancellation, and retry mechanisms.
Inputs & outputs
When to use frontend-api-integration-patterns
- →Implementing API error normalization
- →Fixing race conditions in React hooks
- →Adding retry strategies for API calls
About this skill
Frontend API Integration Patterns
Overview
This skill provides production-ready patterns for integrating frontend applications with backend APIs.
Most frontend issues are not caused by APIs being difficult to call, but by incorrect handling of asynchronous behavior—leading to race conditions, stale data, duplicated requests, and poor user experience.
This skill focuses on correctness, resilience, and user experience, not just making API calls work.
When to Use This Skill
- Connecting frontend apps (React, React Native, Vue, etc.) to backend APIs
- Integrating ML/AI endpoints (
/predict,/recommend) - Handling asynchronous data in UI
- Fixing stale data, flickering UI, or duplicate requests
- Designing scalable frontend API layers
Core Patterns
1. API Layer (Separation of Concerns)
Centralize API logic and normalize errors.
export class ApiError extends Error {
constructor(message, status, payload = null) {
super(message);
this.name = "ApiError";
this.status = status;
this.payload = payload;
}
}
export const apiClient = async (url, options = {}) => {
const res = await fetch(url, {
headers: { "Content-Type": "application/json" },
...options,
});
if (!res.ok) {
let payload = null;
try {
payload = await res.json();
} catch (_) {}
throw new ApiError(
payload?.message || "Request failed",
res.status,
payload
);
}
// handle empty responses safely (e.g. 204 No Content)
if (res.status === 204) return null;
const text = await res.text();
return text ? JSON.parse(text) : null;
};
2. Race-Safe State Management
Prevent stale responses from overwriting fresh data.
useEffect(() => {
let cancelled = false;
const load = async () => {
try {
setLoading(true);
setError(null);
const result = await getUser();
if (!cancelled) setData(result);
} catch (err) {
if (!cancelled) setError(err.message);
} finally {
if (!cancelled) setLoading(false);
}
};
load();
return () => {
cancelled = true;
};
}, []);
Use a cancellation flag for non-fetch async logic. For network requests, prefer AbortController.
3. Request Cancellation (AbortController)
Cancel in-flight requests to avoid memory leaks and stale updates.
useEffect(() => {
const controller = new AbortController();
const load = async () => {
try {
const data = await getUser({ signal: controller.signal });
setData(data);
} catch (err) {
if (err.name === "AbortError") return;
setError(err.message);
}
};
load();
return () => controller.abort();
}, [userId]);
4. Retry with Exponential Backoff
Retry only transient failures (5xx or network errors).
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const fetchWithBackoff = async (fn, retries = 3, delay = 300) => {
try {
return await fn();
} catch (err) {
const isAbort = err.name === "AbortError";
const isHttpError = typeof err.status === "number";
const isRetryable = !isAbort && (!isHttpError || err.status >= 500);
if (retries <= 0 || !isRetryable) throw err;
const nextDelay = delay * 2 + Math.random() * 100;
await sleep(nextDelay);
return fetchWithBackoff(fn, retries - 1, nextDelay);
}
};
5. Debounced API Calls
Avoid excessive API calls (e.g., search inputs).
const useDebounce = (value, delay = 400) => {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const t = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(t);
}, [value, delay]);
return debounced;
};
6. Request Deduplication
Prevent duplicate API calls across components.
const inFlight = new Map();
export const dedupedFetch = (key, fn) => {
if (inFlight.has(key)) return inFlight.get(key);
const promise = fn().finally(() => inFlight.delete(key));
inFlight.set(key, promise);
return promise;
};
Examples
Example 1: ML Prediction with Cancellation
const controllerRef = useRef(null);
const handlePredict = async (input) => {
controllerRef.current?.abort();
controllerRef.current = new AbortController();
try {
const result = await fetchWithBackoff(() =>
apiClient("/predict", {
method: "POST",
body: JSON.stringify({ text: input }),
signal: controllerRef.current.signal,
})
);
setOutput(result);
} catch (err) {
if (err.name === "AbortError") return;
setError(err.message);
}
};
Example 2: Debounced Search
const debouncedQuery = useDebounce(query, 400);
useEffect(() => {
if (!debouncedQuery) return;
const controller = new AbortController();
searchAPI(debouncedQuery, { signal: controller.signal })
.then(setResults)
.catch((err) => {
if (err.name !== "AbortError") {
setError("Search failed. Please try again.");
}
});
return () => controller.abort();
}, [debouncedQuery]);
Example 3: Optimistic UI Update
const deleteItem = async (id) => {
const previous = items;
setItems((curr) => curr.filter((item) => item.id !== id));
try {
await apiClient(`/items/${id}`, { method: "DELETE" });
} catch (err) {
setItems(previous);
setError("Delete failed. Please try again.");
}
};
Best Practices
- ✅ Centralize API logic in a dedicated layer
- ✅ Normalize errors using a custom error class
- ✅ Always handle loading, error, and success states
- ✅ Use AbortController for request cancellation
- ✅ Retry only transient failures (5xx)
- ✅ Use debouncing for input-driven APIs
- ✅ Deduplicate identical requests
Anti-Patterns
- ❌ Retrying 4xx errors
- ❌ No request cancellation (memory leaks)
- ❌ Race-condition-prone state updates
- ❌ Swallowing errors silently
- ❌ Global loading/error state for multiple requests
- ❌ Calling APIs directly inside components repeatedly
Common Pitfalls
Problem: UI shows stale data Solution: Use cancellation or guard against outdated responses
Problem: Too many API calls on input Solution: Use debouncing + cancellation
Problem: Duplicate requests from multiple components Solution: Use request deduplication
Problem: Server overload during retry Solution: Use exponential backoff
Problem: State updates after component unmount Solution: Use AbortController cleanup
Limitations
- These examples use vanilla JavaScript patterns; adapt them to your framework's data-fetching library when using React Query, SWR, Apollo, Relay, or similar tools.
- Do not retry non-idempotent mutations unless the backend provides idempotency keys or another duplicate-safe contract.
- Do not expose privileged API keys in frontend code; proxy sensitive requests through a backend.
Additional Resources
- https://developer.mozilla.org/en-US/docs/Web/API/AbortController
- https://react.dev
- https://axios-http.com
When not to use it
- →The task involves retrying 4xx errors
- →The task requires exposing privileged API keys in frontend code
- →The task involves non-idempotent mutations without backend support for idempotency keys
Limitations
- →These examples use vanilla JavaScript patterns; adapt them to your framework's data-fetching library
- →Do not retry non-idempotent mutations unless the backend provides idempotency keys or another duplicate-safe contract
- →Do not expose privileged API keys in frontend code; proxy sensitive requests through a backend
How it compares
This skill offers specific code patterns for common asynchronous API integration problems, providing concrete solutions for correctness, resilience, and user experience beyond basic API calls.
Compared to similar skills
frontend-api-integration-patterns side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| frontend-api-integration-patterns (this skill) | 0 | 3mo | Review | Intermediate |
| query-layer | 1 | 2mo | No flags | Advanced |
| expect | 0 | 4mo | No flags | Intermediate |
| zustand | 113 | 2mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by TJSNDHU
View all by TJSNDHU →You might also like
query-layer
EpicenterHQ
Query layer patterns for consuming services with TanStack Query, error transformation, and runtime dependency injection. Use when implementing queries/mutations, transforming service errors for UI, or adding reactive data management.
expect
zakad00e2
Use when editing .tsx/.jsx/.css/.html, React components, pages, routes, forms, styles, or layouts. Also when asked to test, verify, validate, QA, find bugs, check for issues, or fix expect-cli failures.
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
accessibility-compliance
wshobson
Implement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.
react-modernization
wshobson
Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.
react
lobehub
React component development guide. Use when working with React components (.tsx files), creating UI, using @lobehub/ui components, implementing routing, or building frontend features. Triggers on React component creation, modification, layout implementation, or navigation tasks.