@thalorlabs/api
v1.1.0
Published
Standardized HTTP client layer for ThalorLabs services with retry logic, error handling, and observability integration
Downloads
18
Readme
@thalorlabs/api
A TypeScript HTTP client package for ThalorLabs projects, providing standardized HTTP client implementations with retry logic, error handling, and observability integration.
Installation
npm install @thalorlabs/apiFeatures
- Standardized HTTP Clients - Consistent HTTP client implementations across services
- Retry Logic - Configurable retry mechanisms with exponential backoff
- Error Handling - Centralized error categorization and retry recommendations
- Observability Integration - Built-in request/response logging and metrics collection
- Type Safety - Full TypeScript support with comprehensive type definitions
- Axios Integration - Built on top of Axios for reliable HTTP requests
- Authentication Support - Flexible authentication token handling
Usage
Basic HTTP Client Usage
import { BaseHttpClient, HttpClientConfig } from '@thalorlabs/api';
// Create a custom HTTP client by extending BaseHttpClient
class MyServiceClient extends BaseHttpClient {
constructor(config: HttpClientConfig) {
super({
...config,
serviceName: 'my-service',
retries: 3,
timeout: 10000,
});
}
protected setupAxios(): void {
// Configure Axios instance with authentication, headers, etc.
this.axiosInstance = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
headers: {
Authorization: `Bearer ${this.config.authToken}`,
...this.config.customHeaders,
},
});
}
async getUsers(): Promise<any[]> {
const context = {
method: 'GET',
url: '/users',
requestId: crypto.randomUUID(),
};
const { response } = await this.handleRequest(
{ method: 'GET', url: '/users' },
context
);
return response.data;
}
}
// Initialize and use the client
const client = new MyServiceClient({
baseURL: 'https://api.example.com',
serviceName: 'my-service',
retries: 3,
authToken: 'your-api-token',
});
const users = await client.getUsers();Error Handling and Retry Logic
import { BaseHttpClient, HttpError } from '@thalorlabs/api';
class WeatherClient extends BaseHttpClient {
constructor(config: HttpClientConfig) {
super({
...config,
serviceName: 'weather',
retries: 3, // Will retry up to 3 times on network/server errors
});
}
protected setupAxios(): void {
this.axiosInstance = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
});
}
async getWeather(city: string): Promise<any> {
const context = {
method: 'GET',
url: `/weather/${city}`,
requestId: crypto.randomUUID(),
};
try {
const { response } = await this.handleRequest(
{ method: 'GET', url: `/weather/${city}` },
context
);
return response.data;
} catch (error) {
if (error instanceof HttpError) {
console.error(`Weather API error: ${error.message}`);
console.error(
`Status: ${error.status}, Retryable: ${error.isRetryable}`
);
}
throw error;
}
}
}
// The client will automatically retry on network errors and 5xx status codes
const weatherClient = new WeatherClient({
baseURL: 'https://api.openweathermap.org/data/2.5',
serviceName: 'weather',
retries: 3,
authToken: process.env.WEATHER_API_KEY,
});
const weather = await weatherClient.getWeather('London');Observability and Logging
import { BaseHttpClient, OutboundRequestLogger } from '@thalorlabs/api';
class PaymentClient extends BaseHttpClient {
constructor(config: HttpClientConfig) {
super({
...config,
serviceName: 'payment',
retries: 3,
});
}
protected setupAxios(): void {
this.axiosInstance = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
headers: {
Authorization: `Bearer ${this.config.authToken}`,
'Content-Type': 'application/json',
},
});
}
async createPayment(paymentData: any): Promise<any> {
const context = {
method: 'POST',
url: '/payments',
requestId: crypto.randomUUID(),
requestBody: paymentData,
};
// All requests are automatically logged with timing and status
const { response } = await this.handleRequest(
{
method: 'POST',
url: '/payments',
data: paymentData,
},
context
);
return response.data;
}
}
// Logs will automatically include:
// - Request details (method, URL, service name)
// - Response status and duration
// - Error details if requests fail
// - Retry attempts and timingAvailable Types
Core Types
| Type | Description | Usage |
| ----------------------- | ---------------------------------------- | ----------------------------------------------------------- |
| BaseHttpClient | Abstract base class for HTTP clients | class MyClient extends BaseHttpClient |
| HttpClientConfig | Configuration interface for HTTP clients | { baseURL: string, serviceName: string, retries: number } |
| HttpError | Standardized HTTP error class | new HttpError(message, status, isRetryable) |
| OutboundRequestLogger | Request/response logging utility | new OutboundRequestLogger(serviceName) |
| HttpErrorHandler | Static error handling utility | HttpErrorHandler.handleError(error, context) |
Configuration Types
| Type | Description | Usage |
| ------------------- | --------------------------- | ------------------------------------------ |
| RequestLogContext | Context for request logging | { method, url, requestId, requestBody? } |
| ErrorContext | Context for error handling | { method, url, requestId, requestBody? } |
Error Handling
The package provides comprehensive error handling with automatic retry logic:
Automatic Retry Logic
The HTTP client automatically retries requests on:
- Network errors: Connection refused, timeouts, DNS failures
- Server errors: 5xx HTTP status codes (500, 502, 503, 504)
- Rate limiting: 429 status codes
Error Categorization
import { HttpError, HttpErrorHandler } from '@thalorlabs/api';
try {
const response = await client.get('/api/data');
} catch (error) {
if (error instanceof HttpError) {
console.log(`Error: ${error.message}`);
console.log(`Status: ${error.status}`);
console.log(`Retryable: ${error.isRetryable}`);
console.log(`Service: ${error.serviceName}`);
console.log(`Timestamp: ${error.timestamp}`);
}
}Retry Configuration
const client = new MyServiceClient({
baseURL: 'https://api.example.com',
serviceName: 'my-service',
retries: 3, // Retry up to 3 times
timeout: 10000, // 10 second timeout
});
// Retry delays follow exponential backoff:
// Attempt 1: Immediate
// Attempt 2: 1 second delay
// Attempt 3: 2 second delay
// Attempt 4: 4 second delayProject Structure
src/
├── index.ts # Main exports
├── baseHttpClient.ts # Core HTTP client implementation
└── tests/ # Test files
└── baseHttpClient.test.ts # HTTP client testsPackage Structure
The package is built as an ES module with the following structure:
- Main entry:
./dist/index.js - Types:
./dist/index.d.ts - Source:
./src/directory with TypeScript source files - Tests: Co-located test files alongside source code
TypeScript Support
This package includes full TypeScript support with:
- Complete type definitions for all HTTP client interfaces
- IntelliSense support for configuration and method signatures
- Compile-time type checking for HTTP client implementations
- Axios integration with proper TypeScript types
- Error handling with typed error classes
Best Practices
HTTP Client Implementation
Always extend BaseHttpClient for consistent behavior:
class MyServiceClient extends BaseHttpClient {
constructor(config: HttpClientConfig) {
super({
...config,
serviceName: 'my-service',
retries: 3,
timeout: 10000,
});
}
protected setupAxios(): void {
this.axiosInstance = axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout,
headers: {
Authorization: `Bearer ${this.config.authToken}`,
'Content-Type': 'application/json',
},
});
}
}Error Handling
Handle errors appropriately based on their retryability:
try {
const data = await client.getData();
} catch (error) {
if (error instanceof HttpError) {
if (error.isRetryable) {
// Log and potentially retry manually
console.warn(`Retryable error: ${error.message}`);
} else {
// Handle non-retryable errors (4xx client errors)
console.error(`Client error: ${error.message}`);
}
}
}Authentication
Use secure token handling:
const client = new MyServiceClient({
baseURL: 'https://api.example.com',
serviceName: 'my-service',
retries: 3,
authToken: process.env.API_TOKEN, // Use environment variables
bearerTokenRetriever: async () => {
// Dynamic token retrieval for long-running services
return await getFreshToken();
},
});Development
Building
npm run buildTesting
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run tests for CI
npm run test:ciType Checking
npx tsc --noEmitAdding New HTTP Clients
- Create a new client file extending
BaseHttpClient - Implement the
setupAxios()method for Axios configuration - Add service-specific methods using
handleRequest()for automatic retry/logging - Export the client from
src/index.ts - Write tests for the new client
- Update documentation with usage examples
Code Standards
- Use TypeScript strict mode
- Follow JSDoc documentation standards
- Implement proper error handling
- Use consistent naming conventions
- Write comprehensive tests
- Follow the existing code structure
License
ISC
