http-clients

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HTTP 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: Use
mcp__documentation__fetch_docs
with technology:
http-clients
for comprehensive documentation.
完整参考:如需了解令牌刷新流程、指数退避重试、请求取消以及类型安全的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
    axios
    skill)
  • GraphQL client setup (use
    graphql-codegen
    skill)
  • tRPC client configuration (use
    trpc
    skill)
  • WebSocket or Server-Sent Events
  • Axios专属配置问题(请使用
    axios
    相关技能)
  • GraphQL客户端配置(请使用
    graphql-codegen
    相关技能)
  • tRPC客户端配置(请使用
    trpc
    相关技能)
  • WebSocket或服务器发送事件(Server-Sent Events)相关问题

Anti-Patterns

反模式

Anti-PatternWhy It's BadSolution
No timeout configuredHanging requestsSet timeout on all clients
Hardcoded API URLsEnvironment couplingUse environment variables
No retry logicPoor UX on transient failuresImplement exponential backoff
Ignoring token expiration401 errorsImplement token refresh flow
Not canceling on unmountMemory leaksUse AbortController cleanup
Not typing responsesRuntime errorsUse TypeScript generics
反模式问题所在解决方案
未配置超时请求挂起,无响应为所有客户端设置超时时间
硬编码API地址与环境耦合,部署不便使用环境变量配置
未实现重试逻辑瞬时故障导致用户体验差实现指数退避重试机制
忽略令牌过期出现401未授权错误实现令牌自动刷新流程
组件卸载时未取消请求造成内存泄漏使用AbortController进行清理
不对响应进行类型定义运行时易出现错误使用TypeScript泛型

Quick Troubleshooting

快速排查指南

IssuePossible CauseSolution
CORS errorsServer misconfigurationConfigure CORS on backend
401 after some timeToken expiredImplement token refresh
Memory leaksNot aborting on unmountAdd cleanup in useEffect
Network timeoutServer slowIncrease timeout, add retry
Infinite refresh loopRefresh returns 401Exclude 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