| name | base-client-guardian |
| description | Guardrails for edits to core/base-client.js covering auth injection, retries, circuit breaker behavior, and request/response events. Use when modifying the universal API client or its auth/retry/error handling. |
Skill: Base Client Guardian
Purpose & Scope
This skill applies when modifying the Universal API Client (core/base-client.js).
The Base Client provides:
- HTTP request handling with axios
- Multi-protocol authentication (Bearer, API Key, Basic, HMAC, OAuth2)
- Exponential backoff retry logic
- Circuit breaker pattern for fault tolerance
- Rate limiting management
- EventEmitter-based request/response/error logging
Critical Rules - NEVER Do
Public Method Stability
- NEVER remove or rename these public methods:
request() - Main entry point for API calls
retryRequest() - Handles retry logic with backoff
healthCheck() - Service health verification
generateMethods() - Dynamic method generation from endpoints
addAuthentication() - Auth injection into requests
EventEmitter Events
- NEVER remove or rename these event names:
request - Emitted before each request
response - Emitted on successful response
error - Emitted on request failure
circuit-breaker-open - Emitted when circuit opens
Authentication Types
- NEVER change these auth type identifiers:
bearer - Bearer token auth
apikey - API key (header or query)
basic - Basic auth
hmac - HMAC signature auth
oauth2 - OAuth 2.0 auth
Security
- NEVER log sensitive data (auth tokens, credentials, API keys)
- NEVER disable TLS/SSL verification
- NEVER expose raw credentials in error messages
- NEVER store credentials in plain text
Circuit Breaker
- NEVER change the state machine flow:
CLOSED -> OPEN -> HALF_OPEN -> CLOSED
- NEVER remove the 5-failure threshold for circuit opening
- NEVER remove the 60-second recovery timeout
Required Patterns - MUST Follow
Retry Logic
const delay = this.config.retryDelay * Math.pow(2, attempt - 1);
if (attempt < this.config.retryAttempts && this.shouldRetry(error)) {
await this.sleep(delay);
return this.retryRequest(config, attempt + 1);
}
Event Emission
this.emit('request', { service, method, url, timestamp });
this.emit('response', { service, status, statusText, timestamp });
this.emit('error', { service, type, message, status, timestamp });
Circuit Breaker State Management
this.circuitBreaker.failures = 0;
this.circuitBreaker.state = 'CLOSED';
this.circuitBreaker.failures++;
if (this.circuitBreaker.failures >= 5) {
this.circuitBreaker.state = 'OPEN';
this.emit('circuit-breaker-open', { service, failures });
}
if (timeSinceLastFailure > 60000) {
this.circuitBreaker.state = 'HALF_OPEN';
}
Authentication Handling
switch (auth.type) {
case 'bearer':
case 'apikey':
case 'basic':
case 'hmac':
case 'oauth2':
break;
}
Safe Modification Examples
Adding a New Authentication Type
case 'custom_auth_type':
config.headers['X-Custom-Auth'] = auth.config.customValue;
break;
Adding New Retry Conditions
shouldRetry(error) {
return (
error.code === 'ECONNRESET' ||
error.code === 'ETIMEDOUT' ||
error.code === 'ENOTFOUND' ||
error.code === 'YOUR_NEW_CODE' ||
(error.response && error.response.status >= 500)
);
}
Adding New Events
this.emit('your-new-event', { service, customData, timestamp });
Integration Points
| Component | Integration Method |
|---|
| Metrics Collector | Listen to request, response, error events |
| Compliance Manager | Intercept in addAuthentication() |
| Version Manager | Pass version in request options |
| Vendor Abstraction | Uses request() method |
Testing Requirements
Before any changes to this file:
npm test -- --grep "BaseClient"
node -e "const BaseClient = require('./core/base-client'); const client = new BaseClient({ name: 'test', baseUrl: 'https://httpbin.org' }); console.log('Client initialized:', client.config.name);"
Rollback Procedure
If changes cause issues:
-
Immediate Rollback
git checkout HEAD~1 -- core/base-client.js
-
Verify Recovery
bun run dev
curl http://localhost:3001/health
-
Event Listener Check
client.on('request', () => console.log('Request event works'));
client.on('response', () => console.log('Response event works'));
client.on('error', () => console.log('Error event works'));