Frontend API Integration
frontend-api-integration
Guide for building robust API integration layers in React/TypeScript applications. Use when implementing HTTP clients (axios/fetch), request/response interceptors, authentication token handling, error handling strategies, retry logic, request cancellation, or organizing API services. Triggers on ...
SKILL.md
Full skill instructions
Frontend API Integration
API Client Setup
Axios Instance with Interceptors
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
// Request interceptor - auth token injection
api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('accessToken');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// Response interceptor - error handling + token refresh
api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
const originalRequest = error.config;
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
originalRequest._retry = true;
const newToken = await refreshAccessToken();
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return api(originalRequest);
}
return Promise.reject(error);
}
);
Request Cancellation
// Per-request cancellation
const controller = new AbortController();
api.get('/data', { signal: controller.signal });
controller.abort();
// Auto-timeout helper
function withTimeout(ms: number) {
const controller = new AbortController();
setTimeout(() => controller.abort(), ms);
return controller.signal;
}
Retry Logic
async function withRetry<T>(
fn: () => Promise<T>,
retries = 3,
delay = 1000
): Promise<T> {
try {
return await fn();
} catch (error) {
if (retries === 0) throw error;
await new Promise(r => setTimeout(r, delay));
return withRetry(fn, retries - 1, delay * 2);
}
}
Service Layer Pattern
// services/userService.ts
export const userService = {
getById: (id: string) => api.get<User>(`/users/${id}`).then(r => r.data),
create: (data: CreateUserDTO) => api.post<User>('/users', data).then(r => r.data),
update: (id: string, data: Partial<User>) => api.patch<User>(`/users/${id}`, data).then(r => r.data),
delete: (id: string) => api.delete(`/users/${id}`)
};
Error Handling
// types/api.ts
interface ApiError {
message: string;
code: string;
status: number;
}
function parseError(error: unknown): ApiError {
if (axios.isAxiosError(error)) {
return {
message: error.response?.data?.message ?? error.message,
code: error.response?.data?.code ?? 'UNKNOWN',
status: error.response?.status ?? 500
};
}
return { message: 'Unknown error', code: 'UNKNOWN', status: 500 };
}
Reference Files
- axios-patterns.md: Advanced interceptor patterns, request deduplication
- react-query-patterns.md: useQuery/useMutation setup, cache strategies
- swr-patterns.md: SWR configuration, global mutate patterns
