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
javascript
// MUST use exponential backoff
const delay = this.config.retryDelay * Math.pow(2, attempt - 1);
// MUST check shouldRetry() before retrying
if (attempt < this.config.retryAttempts && this.shouldRetry(error)) {
await this.sleep(delay);
return this.retryRequest(config, attempt + 1);
}
Event Emission
javascript
// MUST emit events after state changes
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
javascript
// MUST follow state machine
// On success: reset to CLOSED
this.circuitBreaker.failures = 0;
this.circuitBreaker.state = 'CLOSED';
// On failure: increment and potentially open
this.circuitBreaker.failures++;
if (this.circuitBreaker.failures >= 5) {
this.circuitBreaker.state = 'OPEN';
this.emit('circuit-breaker-open', { service, failures });
}
// On timeout: transition to HALF_OPEN
if (timeSinceLastFailure > 60000) {
this.circuitBreaker.state = 'HALF_OPEN';
}
Authentication Handling
javascript
// MUST handle all auth types in switch statement
switch (auth.type) {
case 'bearer':
case 'apikey':
case 'basic':
case 'hmac':
case 'oauth2':
// Implementation here
break;
}
Safe Modification Examples
Adding a New Authentication Type
javascript
// Add to the switch statement in addAuthentication()
case 'custom_auth_type':
config.headers['X-Custom-Auth'] = auth.config.customValue;
break;
Adding New Retry Conditions
javascript
// Extend shouldRetry() - do NOT replace existing conditions
shouldRetry(error) {
return (
error.code === 'ECONNRESET' ||
error.code === 'ETIMEDOUT' ||
error.code === 'ENOTFOUND' ||
error.code === 'YOUR_NEW_CODE' || // ADD here
(error.response && error.response.status >= 500)
);
}
Adding New Events
javascript
// Add new events - do NOT modify existing event signatures
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:
bash
# 1. Run existing tests
npm test -- --grep "BaseClient"
# 2. Verify authentication works for all types
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);
"
# 3. Test circuit breaker states
# Verify: CLOSED -> OPEN (after 5 failures) -> HALF_OPEN (after 60s)
# 4. Test retry logic
# Verify exponential backoff: delay * 2^(attempt-1)
Rollback Procedure
If changes cause issues:
- •
Immediate Rollback
bashgit checkout HEAD~1 -- core/base-client.js
- •
Verify Recovery
bash# Restart the server bun run dev # Check health endpoint curl http://localhost:3001/health
- •
Event Listener Check
javascript// Verify all components can still receive events client.on('request', () => console.log('Request event works')); client.on('response', () => console.log('Response event works')); client.on('error', () => console.log('Error event works'));