mcpField
H
http-clients

http-clients

github.com/claude-dev-suiteDevelopment4636 installsUpdated 2d ago
Repository

About http-clients

http-clients is a reference skill for developers implementing HTTP clients in TypeScript. It covers setup patterns for Axios, the Fetch API, ky, and ofetch, along with production checks and troubleshooting guidance.

Key features

  1. 01Axios setup examples
  2. 02Fetch API wrapper patterns
  3. 03ky and ofetch configuration
  4. 04HTTP client anti-patterns
  5. 05Production checklist and troubleshooting

Use cases

  1. 01Setting up a TypeScript HTTP client
  2. 02Choosing among documented client patterns
  3. 03Reviewing an API client's production readiness

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill http-clients

After installing, type/http-clientsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

advanced.md
quick-ref/
axios.md
error-handling.md
fetch.md
interceptors.md
ky-ofetch.md

Details

From the project README

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-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

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

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

Comments

Markdown supported