UNPKG

@fjell/registry

Version:

Dependency injection and service location system for the Fjell ecosystem

760 lines (637 loc) 18.5 kB
# Configuration Guide Complete guide to configuring the Fjell Client API for development and production environments. ## Overview The Fjell Client API provides extensive configuration options for authentication, error handling, retry logic, monitoring, and performance optimization. This guide covers all available options with practical examples. ## Basic Configuration ### Minimal Setup ```typescript import { createPItemApi, createCItemApi } from '@fjell/client-api'; // Minimal configuration for development const config = { baseUrl: 'http://localhost:3000/api' }; const userApi = createPItemApi<User, 'user'>('user', ['users'], config); const taskApi = createCItemApi<Task, 'task', 'user'>('task', ['users', 'tasks'], config); ``` ### Standard Development Configuration ```typescript const devConfig = { baseUrl: 'http://localhost:3000/api', headers: { 'Content-Type': 'application/json', 'X-Client-Version': '1.0.0' }, timeout: 10000, enableErrorHandling: true, retryConfig: { maxRetries: 2, initialDelayMs: 500, maxDelayMs: 5000 } }; ``` ## Configuration Options ### ClientApiOptions Interface ```typescript interface ClientApiOptions { // Required baseUrl: string; // API base URL // HTTP Configuration headers?: Record<string, string>; // Default headers timeout?: number; // Request timeout (ms) // Authentication readAuthenticated?: boolean; // Authenticate read operations writeAuthenticated?: boolean; // Authenticate write operations getOptions?: RequestOptions; // GET request options postOptions?: RequestOptions; // POST request options putOptions?: RequestOptions; // PUT request options deleteOptions?: RequestOptions; // DELETE request options // Error Handling enableErrorHandling?: boolean; // Enable comprehensive error handling retryConfig?: RetryConfig; // Retry behavior configuration errorHandler?: (error: any, context?: Record<string, any>) => void; // Custom error handler // Advanced transformRequest?: (data: any) => any; // Transform request data transformResponse?: (data: any) => any; // Transform response data } ``` ### RetryConfig Interface ```typescript interface RetryConfig { maxRetries: number; // Maximum retry attempts initialDelayMs: number; // Initial delay between retries maxDelayMs: number; // Maximum delay between retries backoffMultiplier: number; // Exponential backoff multiplier enableJitter: boolean; // Add randomness to delays // Advanced retry logic shouldRetry?: (error: any, attemptNumber: number) => boolean; // Custom retry decision onRetry?: (error: any, attemptNumber: number, delay: number) => void; // Retry callback } ``` ## Authentication Configuration ### Basic Authentication ```typescript const authConfig = { baseUrl: 'https://api.example.com', headers: { 'Authorization': 'Bearer your-api-token', 'Content-Type': 'application/json' }, readAuthenticated: true, writeAuthenticated: true }; ``` ### Dynamic Authentication ```typescript // Token that updates automatically let authToken = 'initial-token'; const dynamicAuthConfig = { baseUrl: 'https://api.example.com', headers: { get 'Authorization'() { return `Bearer ${authToken}`; }, 'Content-Type': 'application/json' } }; // Update token when needed function updateAuthToken(newToken: string) { authToken = newToken; } ``` ### Per-Operation Authentication ```typescript const mixedAuthConfig = { baseUrl: 'https://api.example.com', // Default headers (no auth) headers: { 'Content-Type': 'application/json' }, // Authenticate only write operations writeAuthenticated: true, // Custom options for different operations postOptions: { headers: { 'Authorization': 'Bearer write-token', 'X-Write-Permission': 'true' } }, putOptions: { headers: { 'Authorization': 'Bearer write-token', 'X-Write-Permission': 'true' } }, deleteOptions: { headers: { 'Authorization': 'Bearer admin-token', 'X-Admin-Permission': 'true' } } }; ``` ## Retry Configuration ### Development Retry Settings ```typescript const devRetryConfig = { maxRetries: 2, initialDelayMs: 500, maxDelayMs: 5000, backoffMultiplier: 2, enableJitter: true }; ``` ### Production Retry Settings ```typescript const prodRetryConfig = { maxRetries: 5, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 1.5, enableJitter: true, // Custom retry logic for production shouldRetry: (error, attemptNumber) => { // Always retry network and server errors if (error.code === 'NETWORK_ERROR' || error.code === 'SERVER_ERROR') { return attemptNumber < 5; } // Limited retries for rate limiting if (error.code === 'RATE_LIMIT_ERROR') { return attemptNumber < 3; } // No retries for client errors if (error.status >= 400 && error.status < 500) { return false; } return error.isRetryable && attemptNumber < 3; }, // Monitor retry attempts onRetry: (error, attemptNumber, delay) => { console.log(`Retry attempt ${attemptNumber + 1} for ${error.code} (delay: ${delay}ms)`); // Send metrics to monitoring service metrics.increment('api.retry', { error_code: error.code, attempt: attemptNumber + 1 }); } }; ``` ### Operation-Specific Retry Configuration ```typescript // Different retry strategies for different operations const adaptiveRetryConfig = { shouldRetry: (error, attemptNumber, context) => { // More aggressive retries for read operations if (context?.operation === 'get' || context?.operation === 'all') { return error.isRetryable && attemptNumber < 5; } // Conservative retries for write operations if (context?.operation === 'create' || context?.operation === 'update') { return error.isRetryable && attemptNumber < 2; } // No retries for delete operations if (context?.operation === 'remove') { return false; } return error.isRetryable && attemptNumber < 3; } }; ``` ## Error Handling Configuration ### Basic Error Handler ```typescript const basicErrorHandler = (error: any, context?: Record<string, any>) => { console.error('API Error:', { message: error.message, code: error.code, operation: context?.operation, url: context?.url }); }; const config = { baseUrl: 'https://api.example.com', enableErrorHandling: true, errorHandler: basicErrorHandler }; ``` ### Production Error Handler ```typescript const productionErrorHandler = (error: any, context?: Record<string, any>) => { // Structured logging logger.error('API Operation Failed', { error: { message: error.message, code: error.code, stack: error.stack }, context: { operation: context?.operation, url: context?.url, method: context?.method, duration: context?.duration, attempts: context?.attempts }, timestamp: new Date().toISOString(), requestId: context?.requestId || generateRequestId() }); // Error tracking service errorTracking.captureException(error, { tags: { service: 'fjell-client-api', operation: context?.operation, environment: process.env.NODE_ENV }, extra: context, user: { id: getCurrentUserId() } }); // Business logic based on error type if (error.code === 'AUTHENTICATION_ERROR') { // Redirect to login authService.redirectToLogin(); } else if (error.code === 'RATE_LIMIT_ERROR') { // Implement circuit breaker circuitBreaker.recordFailure(); } else if (error.code === 'PAYMENT_ERROR') { // Send business notification notificationService.sendPaymentFailure({ userId: context?.userId, orderId: context?.orderId, error: error.message }); } // Metrics collection metrics.increment('api.errors.total', { error_code: error.code, operation: context?.operation, environment: process.env.NODE_ENV }); // Alerting for critical errors if (error.code === 'SERVER_ERROR' && context?.attempts >= 3) { alerting.sendAlert({ severity: 'high', title: 'API Server Error After Multiple Retries', description: `Operation ${context.operation} failed with ${error.message} after ${context.attempts} attempts`, context }); } }; ``` ## Environment-Specific Configuration ### Development Environment ```typescript const developmentConfig = { baseUrl: 'http://localhost:3000/api', headers: { 'Content-Type': 'application/json', 'X-Environment': 'development' }, timeout: 10000, enableErrorHandling: true, retryConfig: { maxRetries: 2, initialDelayMs: 500, maxDelayMs: 5000, backoffMultiplier: 2, enableJitter: false // Consistent timing for development }, errorHandler: (error, context) => { // Detailed logging for development console.group('🚨 API Error Details'); console.error('Error:', error); console.log('Context:', context); console.groupEnd(); } }; ``` ### Staging Environment ```typescript const stagingConfig = { baseUrl: 'https://api-staging.example.com', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.STAGING_API_TOKEN}`, 'X-Environment': 'staging' }, timeout: 15000, enableErrorHandling: true, retryConfig: { maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 15000, backoffMultiplier: 2, enableJitter: true }, errorHandler: (error, context) => { // Same as production but with additional debugging productionErrorHandler(error, context); // Extra staging-specific logging if (process.env.STAGING_DEBUG === 'true') { console.log('Staging Debug Info:', { error, context, environment: 'staging' }); } } }; ``` ### Production Environment ```typescript const productionConfig = { baseUrl: 'https://api.example.com', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.PRODUCTION_API_TOKEN}`, 'X-Environment': 'production', 'X-Service-Version': process.env.SERVICE_VERSION, 'X-Request-Source': 'fjell-client-api' }, timeout: 30000, enableErrorHandling: true, retryConfig: { maxRetries: 5, initialDelayMs: 1000, maxDelayMs: 60000, backoffMultiplier: 1.5, enableJitter: true, shouldRetry: (error, attemptNumber) => { // Production-optimized retry logic if (error.code === 'NETWORK_ERROR') return attemptNumber < 5; if (error.code === 'SERVER_ERROR') return attemptNumber < 3; if (error.code === 'RATE_LIMIT_ERROR') return attemptNumber < 3; if (error.code === 'TIMEOUT_ERROR') return attemptNumber < 2; return false; }, onRetry: (error, attemptNumber, delay) => { // Production retry monitoring metrics.increment('api.retries', { error_code: error.code, attempt: attemptNumber + 1 }); } }, errorHandler: productionErrorHandler }; ``` ## Advanced Configuration Patterns ### Multi-Environment Configuration Factory ```typescript interface EnvironmentConfig { api: { baseUrl: string; token: string; }; retry: { maxRetries: number; timeout: number; }; monitoring: { enabled: boolean; endpoint?: string; }; } const environments: Record<string, EnvironmentConfig> = { development: { api: { baseUrl: 'http://localhost:3000/api', token: 'dev-token' }, retry: { maxRetries: 2, timeout: 10000 }, monitoring: { enabled: false } }, production: { api: { baseUrl: 'https://api.example.com', token: process.env.PROD_API_TOKEN! }, retry: { maxRetries: 5, timeout: 30000 }, monitoring: { enabled: true, endpoint: 'https://monitoring.example.com' } } }; function createApiConfig(environment: string): ClientApiOptions { const envConfig = environments[environment]; if (!envConfig) { throw new Error(`Unknown environment: ${environment}`); } return { baseUrl: envConfig.api.baseUrl, headers: { 'Authorization': `Bearer ${envConfig.api.token}`, 'Content-Type': 'application/json', 'X-Environment': environment }, timeout: envConfig.retry.timeout, enableErrorHandling: true, retryConfig: { maxRetries: envConfig.retry.maxRetries, initialDelayMs: 1000, maxDelayMs: environment === 'production' ? 60000 : 15000, backoffMultiplier: environment === 'production' ? 1.5 : 2, enableJitter: true }, errorHandler: envConfig.monitoring.enabled ? productionErrorHandler : developmentErrorHandler }; } // Usage const config = createApiConfig(process.env.NODE_ENV || 'development'); const userApi = createPItemApi<User, 'user'>('user', ['users'], config); ``` ### Configuration with Secrets Management ```typescript import { getSecret } from './secrets'; async function createSecureApiConfig(): Promise<ClientApiOptions> { const apiToken = await getSecret('API_TOKEN'); const encryptionKey = await getSecret('ENCRYPTION_KEY'); return { baseUrl: process.env.API_BASE_URL!, headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json', 'X-Encryption-Key': encryptionKey }, timeout: 30000, enableErrorHandling: true, // Transform sensitive data transformRequest: (data) => { // Encrypt sensitive fields if (data.password) { data.password = encrypt(data.password, encryptionKey); } return data; }, transformResponse: (data) => { // Decrypt sensitive fields if (data.encryptedData) { data.decryptedData = decrypt(data.encryptedData, encryptionKey); delete data.encryptedData; } return data; } }; } ``` ### Configuration Validation ```typescript import Joi from 'joi'; const configSchema = Joi.object({ baseUrl: Joi.string().uri().required(), timeout: Joi.number().min(1000).max(300000).default(30000), retryConfig: Joi.object({ maxRetries: Joi.number().min(0).max(10).required(), initialDelayMs: Joi.number().min(100).max(10000).required(), maxDelayMs: Joi.number().min(1000).max(300000).required(), backoffMultiplier: Joi.number().min(1).max(5).required(), enableJitter: Joi.boolean().required() }).required() }); function validateConfig(config: ClientApiOptions): ClientApiOptions { const { error, value } = configSchema.validate(config); if (error) { throw new Error(`Invalid configuration: ${error.message}`); } return value; } // Usage const config = validateConfig({ baseUrl: 'https://api.example.com', retryConfig: { maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2, enableJitter: true } }); ``` ## Testing Configuration ### Mock Configuration for Testing ```typescript const testConfig: ClientApiOptions = { baseUrl: 'http://mock-api.test', timeout: 5000, enableErrorHandling: false, // Disable for predictable testing retryConfig: { maxRetries: 1, // Minimal retries for faster tests initialDelayMs: 10, maxDelayMs: 100, backoffMultiplier: 1, enableJitter: false // Consistent timing for tests }, errorHandler: (error, context) => { // Capture errors for test assertions testErrorCapture.capture(error, context); } }; ``` ### Integration Test Configuration ```typescript const integrationTestConfig: ClientApiOptions = { baseUrl: process.env.TEST_API_URL || 'http://localhost:3001/api', headers: { 'Authorization': `Bearer ${process.env.TEST_API_TOKEN}`, 'X-Test-Mode': 'true' }, timeout: 10000, enableErrorHandling: true, retryConfig: { maxRetries: 2, initialDelayMs: 100, maxDelayMs: 1000, backoffMultiplier: 2, enableJitter: false } }; ``` ## Configuration Best Practices ### 1. **Environment Variables** ```typescript // Use environment variables for sensitive data const config = { baseUrl: process.env.API_BASE_URL!, headers: { 'Authorization': `Bearer ${process.env.API_TOKEN!}`, }, timeout: parseInt(process.env.API_TIMEOUT || '30000'), retryConfig: { maxRetries: parseInt(process.env.API_MAX_RETRIES || '3'), initialDelayMs: parseInt(process.env.API_INITIAL_DELAY || '1000') } }; ``` ### 2. **Configuration Hierarchy** ```typescript // Base configuration const baseConfig = { headers: { 'Content-Type': 'application/json', 'User-Agent': 'fjell-client-api/1.0' }, timeout: 30000, enableErrorHandling: true }; // Environment-specific overrides const envConfig = { development: { ...baseConfig, baseUrl: 'http://localhost:3000/api', retryConfig: { maxRetries: 2 } }, production: { ...baseConfig, baseUrl: 'https://api.example.com', retryConfig: { maxRetries: 5 } } }; ``` ### 3. **Type Safety** ```typescript // Use TypeScript for configuration validation interface AppConfig { api: ClientApiOptions; features: { enableRetries: boolean; enableCaching: boolean; }; } const appConfig: AppConfig = { api: { baseUrl: process.env.API_BASE_URL!, retryConfig: { maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2, enableJitter: true } }, features: { enableRetries: true, enableCaching: false } }; ``` ## Related Documentation - [Error Handling](./error-handling/README.md) - Configure error handling behavior - [Operations](./operations/README.md) - Operation-specific configuration - [Examples](../examples-README.md) - Configuration examples and patterns ## Next Steps 1. Choose appropriate configuration for your environment 2. Set up error handling and monitoring 3. Configure retry behavior for your use case 4. Test configuration in staging environment 5. Monitor and adjust configuration based on production metrics