返回 Skill 列表
extension
分类: 开发与工程无需 API Key

http-clients

前端和Node.js的HTTP客户端。涵盖Axios、Fetch API、ky和ofetch。包括拦截器、错误处理、重试逻辑和认证令牌管理。用于配置API客户端和HTTP通信。使用场景:当用户提到“HTTP客户端”、“Fetch API”、“ky”、“ofetch”、“HTTP封装”、“重试逻辑”、“令牌刷新”,或者询问“使用哪个HTTP客户端”、“HTTP请求库”、“API客户端设置”、“请求拦截器”时。不适用于:关于Axios的具体问题 - 应使用`axios`;GraphQL - 应使用`graphql-codegen`;tRPC - 应使用`trpc`;WebSocket连接。

person作者: jakexiaohubgithub

HTTP Clients Core Knowledge

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.

Axios Setup

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

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)

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)

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

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 |

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 |

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

Reference Documentation