@unidev-hub/api-client
v1.0.66
Published
API client library for Unidev Hub applications using the Fetch API
Readme
@unidev-hub/api-client
A comprehensive, feature-rich API client library for JavaScript and TypeScript applications that provides a clean, consistent, and developer-friendly way to interact with RESTful APIs.
Table of Contents
- Features
- Installation
- Quick Start
- Core Components
- Authentication
- Error Handling
- Caching
- Logging and Metrics
- Request Builders
- Mocking
- Advanced Usage
- TypeScript Support
- Examples
- Contributing
- License
Features
- 🚀 HTTP Client: Powerful, configurable HTTP client with built-in interceptors
- 🧩 Resource-Based: CRUD operations and custom methods for API resources
- 🔐 Authentication: Token management, auth headers, and refresh token handling
- ❌ Error Handling: Consistent error normalization and retry mechanisms
- 📦 Caching: In-memory and persistent caching with invalidation strategies
- 📝 Logging: Configurable request/response logging and performance metrics
- 🛠️ Request Builders: Fluent query parameter and payload builders
- 🧪 Mocking: Mock API responses for testing and development
- 📊 Metrics: Performance tracking and analysis
- 📝 TypeScript: Full TypeScript support with comprehensive type definitions
Installation
# Using npm
npm install @unidev-hub/api-client
# Using yarn
yarn add @unidev-hub/api-client
# Using pnpm
pnpm add @unidev-hub/api-clientQuick Start
Basic Usage
import { BaseApiClient } from '@unidev-hub/api-client';
// Create an API client
const apiClient = new BaseApiClient({
baseURL: 'https://api.example.com',
});
// Make requests
async function fetchData() {
try {
// GET request
const users = await apiClient.get('/users');
console.log(users.data);
// POST request
const newUser = await apiClient.post('/users', {
name: 'John Doe',
email: '[email protected]',
});
console.log(newUser.data);
// PUT request
const updatedUser = await apiClient.put('/users/1', {
name: 'John Smith',
});
console.log(updatedUser.data);
// DELETE request
await apiClient.delete('/users/1');
} catch (error) {
console.error('API Error:', error.message);
}
}Using Resource Client
import { BaseApiClient, ResourceClient } from '@unidev-hub/api-client';
// Define your resource type
interface User {
id: number;
name: string;
email: string;
}
// Create the base client
const apiClient = new BaseApiClient({
baseURL: 'https://api.example.com',
});
// Create a resource client
const usersClient = new ResourceClient<User>(apiClient, '/users');
// Use resource methods
async function manageUsers() {
try {
// Get all users
const users = await usersClient.list();
console.log(users.data);
// Get a specific user
const user = await usersClient.get(1);
console.log(user.data);
// Create a new user
const newUser = await usersClient.create({
name: 'Jane Doe',
email: '[email protected]',
});
console.log(newUser.data);
// Update a user
const updatedUser = await usersClient.update(1, {
name: 'Jane Smith',
});
console.log(updatedUser.data);
// Delete a user
await usersClient.delete(1);
} catch (error) {
console.error('Resource Error:', error.message);
}
}Core Components
BaseApiClient
The BaseApiClient is the foundation of the library, providing HTTP request functionality with built-in support for authentication, error handling, caching, and more.
import { BaseApiClient } from '@unidev-hub/api-client';
const apiClient = new BaseApiClient({
baseURL: 'https://api.example.com',
timeout: 30000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
// Enable caching
cacheConfig: {
enabled: true,
ttl: 300000, // 5 minutes
},
// Configure retries
retryConfig: {
maxRetries: 3,
retryDelay: 1000,
exponentialBackoff: true,
},
// Configure logging
logConfig: {
enabled: true,
logLevel: 'info',
},
});
// HTTP methods
apiClient.get('/endpoint');
apiClient.post('/endpoint', data);
apiClient.put('/endpoint', data);
apiClient.patch('/endpoint', data);
apiClient.delete('/endpoint');
apiClient.request({ method: 'GET', url: '/endpoint' });ResourceClient
The ResourceClient provides CRUD operations and custom methods for specific API resources.
import { ResourceClient } from '@unidev-hub/api-client';
// Create a resource client
const productsClient = new ResourceClient(apiClient, '/products', {
idField: 'productId', // Optional custom ID field (default: 'id')
transformResponse: (data) => {
// Optional response transformation
return {
...data,
price: parseFloat(data.price), // Convert string to number
};
},
});
// CRUD operations
productsClient.list(); // GET /products
productsClient.get(123); // GET /products/123
productsClient.create({ name: 'Product', price: 9.99 }); // POST /products
productsClient.update(123, { price: 19.99 }); // PUT /products/123
productsClient.patch(123, { price: 19.99 }); // PATCH /products/123
productsClient.delete(123); // DELETE /products/123
// Custom actions
productsClient.executeAction('publish', { date: '2023-01-01' }, 123); // POST /products/123/publish
productsClient.executeQuery('featured', { limit: 5 }); // GET /products/featured?limit=5Configuration
The library provides extensive configuration options with sensible defaults.
import { BaseApiClient, DEFAULT_CONFIG } from '@unidev-hub/api-client';
// Use default config as a base
console.log(DEFAULT_CONFIG);
// Create client with custom config
const apiClient = new BaseApiClient({
baseURL: 'https://api.example.com',
timeout: 30000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-Key': 'your-api-key',
},
retryConfig: {
maxRetries: 3,
retryDelay: 1000,
retryStatusCodes: [408, 429, 500, 502, 503, 504],
exponentialBackoff: true,
},
cacheConfig: {
enabled: true,
ttl: 300000, // 5 minutes
persistent: true, // Use localStorage when available
},
logConfig: {
enabled: true,
logLevel: 'info', // 'error' | 'warn' | 'info' | 'debug'
maskedHeaders: ['authorization', 'x-api-key'], // Sensitive headers to mask in logs
},
authConfig: {
enabled: true,
scheme: 'Bearer',
refreshTokenEndpoint: '/auth/refresh',
persistTokens: true,
},
mockConfig: {
enabled: false,
latency: { min: 50, max: 300 },
errorRate: 0,
},
});Interceptors
Interceptors allow you to customize request and response handling.
import {
addCustomHeadersInterceptor,
addTimingInterceptor
} from '@unidev-hub/api-client';
// Add custom interceptors to your client's axios instance
addCustomHeadersInterceptor(apiClient.axiosInstance, {
'X-Custom-Header': 'value'
});
addTimingInterceptor(apiClient.axiosInstance);Authentication
The library provides comprehensive authentication utilities, including token management and automatic refresh.
import { BaseApiClient, TokenManager } from '@unidev-hub/api-client';
// Create a token manager
const tokenManager = new TokenManager({
enabled: true,
scheme: 'Bearer',
persistTokens: true, // Store in localStorage
});
// Create an API client with auth config
const apiClient = new BaseApiClient({
baseURL: 'https://api.example.com',
authConfig: {
enabled: true,
scheme: 'Bearer',
refreshTokenEndpoint: '/auth/refresh',
},
});
// Login and store tokens
async function login(username: string, password: string) {
const response = await apiClient.post('/auth/login', {
username,
password,
});
// Store tokens
tokenManager.setTokens({
accessToken: response.data.accessToken,
refreshToken: response.data.refreshToken,
accessTokenExpiresIn: response.data.expiresIn,
});
return response.data;
}
// Logout
function logout() {
tokenManager.clearTokens();
}
// Get access token
const token = tokenManager.getAccessToken();
// Check if token is expired
const isExpired = tokenManager.isAccessTokenExpired();Error Handling
The library provides consistent error handling with a custom ApiError class.
import { ApiError } from '@unidev-hub/api-client';
try {
const response = await apiClient.get('/users/999');
} catch (error) {
if (error instanceof ApiError) {
console.error('API Error:', error.message);
console.error('Status:', error.status);
console.error('Error Code:', error.code);
console.error('Details:', error.details);
} else {
console.error('Unexpected error:', error);
}
}
// Creating custom errors
throw ApiError.notFound('User not found');
throw ApiError.unauthorized('Invalid token');
throw ApiError.validation('Validation failed', { field: ['Error message'] });Caching
The library includes flexible caching capabilities with different storage options.
import {
cacheResponse,
getCachedResponse,
clearCache,
invalidateResourceCache
} from '@unidev-hub/api-client';
// The BaseApiClient handles caching automatically when enabled in config
// But you can also use the caching utilities directly:
// Cache a response
await cacheResponse('users/list', data, 60000); // 1 minute TTL
// Get a cached response
const cached = await getCachedResponse('users/list');
// Clear all cache
await clearCache();
// Invalidate cache for a specific resource
invalidateResourceCache('/users', 123); // Invalidate specific user
invalidateResourceCache('/users'); // Invalidate all usersLogging and Metrics
The library includes comprehensive logging and performance metrics.
import {
configureLogger,
getPerformanceReport,
getAverageResponseTime
} from '@unidev-hub/api-client';
// Configure logging
configureLogger({
enabled: true,
logLevel: 'debug',
customLogger: (level, message, data) => {
// Custom logging implementation
myLogger.log(level, message, data);
},
});
// Get performance metrics
const avgTime = getAverageResponseTime('/users');
const report = getPerformanceReport();
console.log(`Average response time: ${avgTime}ms`);
console.log('Performance report:', report);Request Builders
The library provides fluent builders for query parameters and request payloads.
import { QueryBuilder, PayloadBuilder } from '@unidev-hub/api-client';
// Build query parameters
const query = new QueryBuilder()
.filter({
status: 'active',
category: 'electronics',
price: { gt: 100, lte: 500 },
})
.sort({ createdAt: 'desc', name: 'asc' })
.paginate({ page: 2, pageSize: 25 })
.search('phone', ['name', 'description'])
.select(['id', 'name', 'price', 'category'])
.include(['manufacturer', 'reviews'])
.build();
// Result: { status: 'active', category: 'electronics', price_gt: 100, price_lte: 500, sort: '-createdAt,name', page: 2, pageSize: 25, q: 'phone', searchFields: 'name,description', fields: 'id,name,price,category', include: 'manufacturer,reviews' }
// Build request payload
const payload = new PayloadBuilder()
.field('name', 'Product Name')
.field('price', 99.99)
.field('categories', ['electronics', 'gadgets'])
.setNestedField('metadata.published', true)
.setNestedField('metadata.featured', false)
.build();
// Result: { name: 'Product Name', price: 99.99, categories: ['electronics', 'gadgets'], metadata: { published: true, featured: false } }
// Use in API requests
await apiClient.get('/products', { params: query });
await apiClient.post('/products', payload);Mocking
The library includes powerful mocking capabilities for testing and development.
import { MockApiClient } from '@unidev-hub/api-client';
// Create a mock client
const mockClient = new MockApiClient({
latency: { min: 100, max: 500 },
errorRate: 0.1, // 10% random error rate
});
// Define a resource schema
mockClient.registerResource('users', {
idField: 'id',
onCreate: (user) => {
// Auto-generate fields
user.createdAt = new Date().toISOString();
},
onUpdate: (user) => {
user.updatedAt = new Date().toISOString();
},
});
// Seed with initial data
mockClient.seedData('users', [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' },
]);
// Create a mock resource client
const usersClient = mockClient.resource('users');
// Use like a real API client
const users = await usersClient.list();
const user = await usersClient.get(1);
const newUser = await usersClient.create({ name: 'Alice', email: '[email protected]' });Advanced Usage
Environment-specific Configuration
import { getEnvironmentConfig } from '@unidev-hub/api-client';
const baseConfig = {
timeout: 30000,
environments: {
development: {
baseURL: 'http://localhost:3000/api',
logConfig: { logLevel: 'debug' },
},
staging: {
baseURL: 'https://staging-api.example.com',
logConfig: { logLevel: 'info' },
},
production: {
baseURL: 'https://api.example.com',
logConfig: { logLevel: 'error' },
cacheConfig: { enabled: true, ttl: 600000 },
},
},
};
// Get config for current environment
const config = getEnvironmentConfig(baseConfig, process.env.NODE_ENV);
// Create client with environment config
const apiClient = new BaseApiClient(config);Custom Resource Actions
// Define a resource client for products
const productsClient = new ResourceClient(apiClient, '/products');
// Execute custom actions on a resource
async function publishProduct(productId: number, publishDate: string) {
return productsClient.executeAction('publish', { publishDate }, productId);
// Sends: POST /products/{productId}/publish
}
// Execute custom queries
async function getFeaturedProducts(limit: number) {
return productsClient.executeQuery('featured', { limit });
// Sends: GET /products/featured?limit={limit}
}TypeScript Support
The library is written in TypeScript and provides comprehensive type definitions.
import {
BaseApiClient,
ResourceClient,
ApiResponse,
RequestOptions
} from '@unidev-hub/api-client';
// Define your resource types
interface Product {
id: number;
name: string;
price: number;
createdAt: string;
}
interface CreateProductDto {
name: string;
price: number;
}
// Create typed resource client
const productsClient = new ResourceClient<Product, number>(apiClient, '/products');
// Methods now have proper typing
async function getProducts(options?: RequestOptions): Promise<ApiResponse<Product[]>> {
return productsClient.list(options);
}
async function createProduct(data: CreateProductDto): Promise<ApiResponse<Product>> {
return productsClient.create(data);
}Examples
The library includes a variety of examples to help you get started:
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
