http-clients
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHTTP Clients Core Knowledge
HTTP客户端核心知识
Full Reference: See advanced.md for token refresh flow, retry with exponential backoff, request cancellation, and type-safe API client patterns.
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.http-clients
完整参考:如需了解令牌刷新流程、指数退避重试、请求取消以及类型安全的API客户端模式,请查看advanced.md。
深度知识:使用工具并指定技术类型为mcp__documentation__fetch_docs,可获取全面文档。http-clients
Axios Setup
Axios配置
typescript
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
});
// Request interceptor - add auth token
api.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor - handle errors
api.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
if (error.response?.status === 401) {
window.location.href = '/login';
}
return Promise.reject(error);
}
);typescript
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
});
// Request interceptor - add auth token
api.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor - handle errors
api.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
if (error.response?.status === 401) {
window.location.href = '/login';
}
return Promise.reject(error);
}
);Fetch API Wrapper
Fetch API封装
typescript
class ApiError extends Error {
constructor(public status: number, public statusText: string, public data?: unknown) {
super(`${status}: ${statusText}`);
}
}
async function fetchWithTimeout(url: string, options: RequestInit & { timeout?: number } = {}): Promise<Response> {
const { timeout = 10000, ...fetchOptions } = options;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
return await fetch(url, { ...fetchOptions, signal: controller.signal });
} finally {
clearTimeout(timeoutId);
}
}
export async function apiFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = localStorage.getItem('accessToken');
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
};
const response = await fetchWithTimeout(`${API_URL}${endpoint}`, { ...options, headers });
if (!response.ok) {
throw new ApiError(response.status, response.statusText);
}
return response.json();
}typescript
class ApiError extends Error {
constructor(public status: number, public statusText: string, public data?: unknown) {
super(`${status}: ${statusText}`);
}
}
async function fetchWithTimeout(url: string, options: RequestInit & { timeout?: number } = {}): Promise<Response> {
const { timeout = 10000, ...fetchOptions } = options;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
return await fetch(url, { ...fetchOptions, signal: controller.signal });
} finally {
clearTimeout(timeoutId);
}
}
export async function apiFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const token = localStorage.getItem('accessToken');
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
};
const response = await fetchWithTimeout(`${API_URL}${endpoint}`, { ...options, headers });
if (!response.ok) {
throw new ApiError(response.status, response.statusText);
}
return response.json();
}ky (Modern Fetch Wrapper)
ky(现代Fetch封装库)
typescript
import ky from 'ky';
const api = ky.create({
prefixUrl: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
retry: {
limit: 2,
methods: ['get', 'put', 'delete'],
statusCodes: [408, 429, 500, 502, 503, 504],
},
hooks: {
beforeRequest: [
(request) => {
const token = localStorage.getItem('accessToken');
if (token) {
request.headers.set('Authorization', `Bearer ${token}`);
}
},
],
},
});
// Usage
const users = await api.get('users').json<User[]>();
const user = await api.post('users', { json: newUser }).json<User>();typescript
import ky from 'ky';
const api = ky.create({
prefixUrl: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
retry: {
limit: 2,
methods: ['get', 'put', 'delete'],
statusCodes: [408, 429, 500, 502, 503, 504],
},
hooks: {
beforeRequest: [
(request) => {
const token = localStorage.getItem('accessToken');
if (token) {
request.headers.set('Authorization', `Bearer ${token}`);
}
},
],
},
});
// Usage
const users = await api.get('users').json<User[]>();
const user = await api.post('users', { json: newUser }).json<User>();ofetch (Universal Fetch)
ofetch(通用Fetch库)
typescript
import { ofetch } from 'ofetch';
const api = ofetch.create({
baseURL: process.env.NUXT_PUBLIC_API_URL,
retry: 2,
retryDelay: 500,
timeout: 10000,
async onRequest({ options }) {
const token = localStorage.getItem('accessToken');
if (token) {
options.headers = { ...options.headers, Authorization: `Bearer ${token}` };
}
},
});
// Works in Node.js and browser
const users = await api<User[]>('/users');typescript
import { ofetch } from 'ofetch';
const api = ofetch.create({
baseURL: process.env.NUXT_PUBLIC_API_URL,
retry: 2,
retryDelay: 500,
timeout: 10000,
async onRequest({ options }) {
const token = localStorage.getItem('accessToken');
if (token) {
options.headers = { ...options.headers, Authorization: `Bearer ${token}` };
}
},
});
// Works in Node.js and browser
const users = await api<User[]>('/users');When NOT to Use This Skill
不适用场景
- Axios-specific configuration (use skill)
axios - GraphQL client setup (use skill)
graphql-codegen - tRPC client configuration (use skill)
trpc - WebSocket or Server-Sent Events
- Axios专属配置问题(请使用相关技能)
axios - GraphQL客户端配置(请使用相关技能)
graphql-codegen - tRPC客户端配置(请使用相关技能)
trpc - WebSocket或服务器发送事件(Server-Sent Events)相关问题
Anti-Patterns
反模式
| Anti-Pattern | Why It's Bad | Solution |
|---|---|---|
| No timeout configured | Hanging requests | Set timeout on all clients |
| Hardcoded API URLs | Environment coupling | Use environment variables |
| No retry logic | Poor UX on transient failures | Implement exponential backoff |
| Ignoring token expiration | 401 errors | Implement token refresh flow |
| Not canceling on unmount | Memory leaks | Use AbortController cleanup |
| Not typing responses | Runtime errors | Use TypeScript generics |
| 反模式 | 问题所在 | 解决方案 |
|---|---|---|
| 未配置超时 | 请求挂起,无响应 | 为所有客户端设置超时时间 |
| 硬编码API地址 | 与环境耦合,部署不便 | 使用环境变量配置 |
| 未实现重试逻辑 | 瞬时故障导致用户体验差 | 实现指数退避重试机制 |
| 忽略令牌过期 | 出现401未授权错误 | 实现令牌自动刷新流程 |
| 组件卸载时未取消请求 | 造成内存泄漏 | 使用AbortController进行清理 |
| 不对响应进行类型定义 | 运行时易出现错误 | 使用TypeScript泛型 |
Quick Troubleshooting
快速排查指南
| Issue | Possible Cause | Solution |
|---|---|---|
| CORS errors | Server misconfiguration | Configure CORS on backend |
| 401 after some time | Token expired | Implement token refresh |
| Memory leaks | Not aborting on unmount | Add cleanup in useEffect |
| Network timeout | Server slow | Increase timeout, add retry |
| Infinite refresh loop | Refresh returns 401 | Exclude refresh from interceptor |
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| CORS错误 | 服务器配置错误 | 在后端配置CORS规则 |
| 一段时间后出现401错误 | 令牌已过期 | 实现令牌刷新机制 |
| 内存泄漏 | 组件卸载时未终止请求 | 在useEffect中添加清理逻辑 |
| 网络超时 | 服务器响应缓慢 | 增加超时时间,添加重试机制 |
| 无限刷新循环 | 刷新令牌请求返回401 | 将刷新请求排除在拦截器之外 |
Production Checklist
生产环境检查清单
- Base URL via environment
- Request timeout configured
- Auth token interceptor
- Token refresh logic
- Error response handling
- Retry with exponential backoff
- Request cancellation on unmount
- Type-safe API methods
- 通过环境变量配置基础地址
- 已配置请求超时
- 已实现认证令牌拦截器
- 已实现令牌刷新逻辑
- 已处理错误响应
- 已实现指数退避重试
- 组件卸载时会取消请求
- 已实现类型安全的API方法
Reference Documentation
参考文档
- Axios Configuration
- Fetch Patterns
- ky and ofetch
- Axios配置
- Fetch模式
- ky与ofetch