用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/majiayu000/claude-skill-registry --skill http-client-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | http-client-patterns |
| description | HTTP client patterns for axios configuration, interceptors, and request handling |
See error-handling skill for complete error mapping reference.
class GitHubClient {
private httpClient: AxiosInstance;
private token?: string;
private baseUrl: string;
async connect(profile: ConnectionProfile): Promise<void> {
// Configure HTTP client
this.baseUrl = profile.endpoint || 'https://api.github.com';
this.token = profile.credentials.token;
// Set up HTTP client
this.httpClient = axios.create({
baseURL: this.baseUrl,
timeout: 30000,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
// Set up interceptors
this.setupInterceptors();
// Validate connection with real API call
await this.isConnected();
// Return void (not ConnectionState)
}
async isConnected(): Promise<boolean> {
try {
// Real API call to verify - NOT just checking stored state
await this.httpClient.get('/user');
return true;
} catch {
return false;
}
}
async disconnect(): Promise<void> {
// Clean up resources
this.httpClient = undefined;
this.token = undefined;
// Clear any cached data
}
}
Key points:
connect() returns Promise<void> (not ConnectionState)isConnected() makes real API call (not state check)disconnect() cleans up all resourcesconst client = axios.create({
baseURL: this.baseUrl,
timeout: 30000, // 30 seconds
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
Configuration options:
baseURL - API endpoint from connection profiletimeout - Reasonable timeout (30s typical)headers - Accept and content-type for JSON APIs// Request interceptor for auth
client.interceptors.request.use(config => {
if (this.token) {
config.headers.Authorization = `Bearer ${this.token}`;
}
return config;
});
Use cases:
// Response interceptor for errors
client.interceptors.response.use(
response => response,
error => this.handleApiError(error)
);
private handleApiError(error: any): never {
const status = error.response?.status || 500;
const message = error.response?.data?.message || error.message;
switch (status) {
case 401:
throw new InvalidCredentialsError();
case 403:
throw new UnauthorizedError();
case 404:
throw new NoSuchObjectError('resource', 'id');
case 429:
throw new RateLimitExceededError();
case 500:
case 502:
case 503:
throw new ServiceUnavailableError();
default:
throw new UnexpectedError(`API error: ${message}`, status);
}
}
Critical rules:
Error or leave unhandled// ❌ WRONG - Exposes credentials
throw new Error(`Failed to connect with token ${this.token}`);
// ✅ CORRECT - Safe error message
throw new InvalidCredentialsError();
class UserProducer {
constructor(private client: GitHubClient) {}
async list(): Promise<User[]> {
const response = await this.client.get('/users');
// Validate response format
if (!Array.isArray(response.data)) {
throw new UnexpectedError('Invalid response format');
}
// Map to internal types
return response.data.map(toUser);
}
async get(id: string): Promise<User> {
const response = await this.client.get(`/users/${id}`);
return toUser(response.data);
}
async create(data: CreateUserRequest): Promise<User> {
response = ..(, data);
(response.);
}
(: , : ): <> {
response = ..(, data);
(response.);
}
(: ): <> {
..();
}
}
Key patterns:
async isConnected(): Promise<boolean> {
try {
// Real API call - lightweight endpoint
await this.client.get('/user');
return true;
} catch {
return false;
}
}
Why real API call:
Anti-pattern:
// ❌ WRONG - Just checking state
async isConnected(): Promise<boolean> {
return this.token !== undefined;
}
// For transient failures only (5xx errors, timeouts)
async function withRetry<T>(
operation: () => Promise<T>,
maxAttempts = 3
): Promise<T> {
for (let i = 0; i < maxAttempts; i++) {
try {
return await operation();
} catch (error) {
// Don't retry client errors (4xx)
if (error.response?.status >= 400 && error.response?.status < 500) {
throw error;
}
// Last attempt - throw error
if (i === maxAttempts - 1) {
throw error;
}
// Exponential backoff: 1s, 2s, 4s
await delay(Math.pow(2, i) * 1000);
}
}
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
Retry rules:
async get(path: string): Promise<any> {
return withRetry(() => this.httpClient.get(path));
}
async list(): Promise<User[]> {
const response = await this.client.get('/users');
// ✅ CORRECT - Validate response format
if (!Array.isArray(response.data)) {
throw new UnexpectedError('Invalid response format: expected array');
}
return response.data.map(toUser);
}
Validation checks:
Array.isArray(data)typeof data === 'object' && data !== nullif (!data.id) throw ...const client = axios.create({
baseURL: this.baseUrl,
timeout: 30000, // 30 seconds (typical)
});
Timeout guidelines:
async longRunningOperation(): Promise<Result> {
return this.client.get('/export', {
timeout: 120000 // 2 minutes for this specific call
});
}
async disconnect(): Promise<void> {
// Clear client reference
this.httpClient = undefined;
// Clear credentials
this.token = undefined;
// Clear any cached data
this.cache?.clear();
// Cancel pending requests (if using axios)
this.cancelTokenSource?.cancel('Connection closed');
}
Cleanup checklist:
throw new Error('API call failed'); // Generic!
throw new ServiceUnavailableError(); // Core type
return response.data.map(toUser); // What if not array?
if (!Array.isArray(response.data)) {
throw new UnexpectedError('Invalid response format');
}
return response.data.map(toUser);
async isConnected(): Promise<boolean> {
return this.token !== undefined; // Just checking state!
}
async isConnected(): Promise<boolean> {
try {
await this.client.get('/user');
return true;
} catch {
return false;
}
}
import nock from 'nock';
describe('UserProducer', () => {
it('should list users', async () => {
nock('https://api.github.com')
.get('/users')
.reply(200, [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' }
]);
const users = await producer.list();
expect(users).toHaveLength(2);
});
});
See nock-patterns skill for complete mocking patterns.
HTTP client implementation MUST meet all criteria: