try-error Documentation
Real-World Examples
Complex scenarios and production-ready patterns using try-error
E-commerce Order Processing
A complete order processing system demonstrating error handling across multiple services, payment processing, inventory management, and notification systems.
Order Service
Order Processing Pipelinetypescript
1import { tryAsync, isTryError, createTryError, flatMapResult } from 'try-error';
2
3interface Order {
4 id: string;
5 userId: string;
6 items: OrderItem[];
7 total: number;
8 status: 'pending' | 'processing' | 'completed' | 'failed';
9}
10
11interface OrderItem {
12 productId: string;
13 quantity: number;
14 price: number;
15}
16
17class OrderService {
18 async processOrder(orderData: Partial<Order>): Promise<TryResult<Order, TryError>> {
19 // Validate order data
20 const validationResult = await this.validateOrder(orderData);
21 if (isTryError(validationResult)) {
22 return validationResult;
23 }
24
25 // Check inventory availability
26 const inventoryResult = await flatMapResult(validationResult, order =>
27 this.checkInventory(order.items)
28 );
29 if (isTryError(inventoryResult)) {
30 return inventoryResult;
31 }
32
33 // Calculate pricing and taxes
34 const pricingResult = await flatMapResult(inventoryResult, order =>
35 this.calculatePricing(order)
36 );
37 if (isTryError(pricingResult)) {
38 return pricingResult;
39 }
40
41 // Process payment
42 const paymentResult = await flatMapResult(pricingResult, order =>
43 this.processPayment(order)
44 );
45 if (isTryError(paymentResult)) {
46 // Rollback inventory reservation
47 await this.releaseInventory(inventoryResult.items);
48 return paymentResult;
49 }
50
51 // Reserve inventory
52 const reservationResult = await flatMapResult(paymentResult, order =>
53 this.reserveInventory(order.items)
54 );
55 if (isTryError(reservationResult)) {
56 // Rollback payment
57 await this.refundPayment(paymentResult.paymentId);
58 return reservationResult;
59 }
60
61 // Create order record
62 const orderResult = await flatMapResult(reservationResult, order =>
63 this.createOrderRecord(order)
64 );
65 if (isTryError(orderResult)) {
66 // Rollback everything
67 await this.releaseInventory(reservationResult.items);
68 await this.refundPayment(paymentResult.paymentId);
69 return orderResult;
70 }
71
72 // Send confirmation notifications
73 await this.sendOrderConfirmation(orderResult);
74
75 return orderResult;
76 }
77
78 private async validateOrder(orderData: Partial<Order>): Promise<TryResult<Order, TryError>> {
79 return tryAsync(async () => {
80 if (!orderData.userId) {
81 throw createTryError('ValidationError', 'User ID is required', {
82 field: 'userId',
83 value: orderData.userId
84 });
85 }
86
87 if (!orderData.items || orderData.items.length === 0) {
88 throw createTryError('ValidationError', 'Order must contain at least one item', {
89 field: 'items',
90 value: orderData.items
91 });
92 }
93
94 // Validate each item
95 for (const item of orderData.items) {
96 if (item.quantity <= 0) {
97 throw createTryError('ValidationError', 'Item quantity must be positive', {
98 field: 'quantity',
99 productId: item.productId,
100 value: item.quantity
101 });
102 }
103 }
104
105 return {
106 id: generateOrderId(),
107 userId: orderData.userId,
108 items: orderData.items,
109 total: 0, // Will be calculated later
110 status: 'pending'
111 } as Order;
112 });
113 }
114
115 private async checkInventory(items: OrderItem[]): Promise<TryResult<Order, TryError>> {
116 return tryAsync(async () => {
117 for (const item of items) {
118 const availability = await this.inventoryService.checkAvailability(item.productId);
119
120 if (availability < item.quantity) {
121 throw createTryError('InventoryError', 'Insufficient inventory', {
122 productId: item.productId,
123 requested: item.quantity,
124 available: availability
125 });
126 }
127 }
128
129 return { items } as Order;
130 });
131 }
132
133 private async processPayment(order: Order): Promise<TryResult<Order & { paymentId: string }, TryError>> {
134 return tryAsync(async () => {
135 const paymentResult = await this.paymentService.processPayment({
136 amount: order.total,
137 currency: 'USD',
138 userId: order.userId,
139 orderId: order.id
140 });
141
142 if (!paymentResult.success) {
143 throw createTryError('PaymentError', 'Payment processing failed', {
144 orderId: order.id,
145 amount: order.total,
146 reason: paymentResult.error,
147 retryable: paymentResult.retryable
148 });
149 }
150
151 return {
152 ...order,
153 paymentId: paymentResult.transactionId
154 };
155 });
156 }
157}
Error Recovery & Rollback
Comprehensive Error Recoverytypescript
1class OrderService {
2 async processOrderWithRecovery(orderData: Partial<Order>): Promise<TryResult<Order, TryError>> {
3 const rollbackActions: (() => Promise<void>)[] = [];
4
5 try {
6 // Step 1: Validate order
7 const validationResult = await this.validateOrder(orderData);
8 if (isTryError(validationResult)) {
9 return validationResult;
10 }
11
12 // Step 2: Reserve inventory
13 const inventoryResult = await this.reserveInventory(validationResult.items);
14 if (isTryError(inventoryResult)) {
15 return inventoryResult;
16 }
17 rollbackActions.push(() => this.releaseInventory(validationResult.items));
18
19 // Step 3: Process payment
20 const paymentResult = await this.processPayment(validationResult);
21 if (isTryError(paymentResult)) {
22 await this.executeRollback(rollbackActions);
23 return paymentResult;
24 }
25 rollbackActions.push(() => this.refundPayment(paymentResult.paymentId));
26
27 // Step 4: Create order
28 const orderResult = await this.createOrderRecord({
29 ...validationResult,
30 paymentId: paymentResult.paymentId
31 });
32 if (isTryError(orderResult)) {
33 await this.executeRollback(rollbackActions);
34 return orderResult;
35 }
36
37 // Step 5: Send notifications (non-critical)
38 const notificationResult = await this.sendOrderConfirmation(orderResult);
39 if (isTryError(notificationResult)) {
40 // Log but don't fail the order
41 console.warn('Failed to send order confirmation:', notificationResult.message);
42 }
43
44 return orderResult;
45
46 } catch (error) {
47 await this.executeRollback(rollbackActions);
48 return createTryError('OrderProcessingError', 'Unexpected error during order processing', {
49 originalError: error,
50 rollbackExecuted: true
51 });
52 }
53 }
54
55 private async executeRollback(actions: (() => Promise<void>)[]): Promise<void> {
56 // Execute rollback actions in reverse order
57 for (const action of actions.reverse()) {
58 try {
59 await action();
60 } catch (rollbackError) {
61 console.error('Rollback action failed:', rollbackError);
62 // Continue with other rollback actions
63 }
64 }
65 }
66
67 // Retry with exponential backoff for transient failures
68 async processOrderWithRetry(orderData: Partial<Order>, maxAttempts = 3): Promise<TryResult<Order, TryError>> {
69 let lastError: TryError;
70
71 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
72 const result = await this.processOrderWithRecovery(orderData);
73
74 if (!isTryError(result)) {
75 return result;
76 }
77
78 lastError = result;
79
80 // Don't retry validation errors or payment failures
81 if (hasErrorType(result, 'ValidationError') ||
82 hasErrorType(result, 'PaymentError')) {
83 break;
84 }
85
86 // Only retry on network errors or temporary failures
87 if (hasErrorType(result, 'NetworkError') ||
88 hasErrorType(result, 'InventoryError')) {
89
90 if (attempt < maxAttempts) {
91 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
92 await new Promise(resolve => setTimeout(resolve, delay));
93 console.log(`Retrying order processing, attempt ${attempt + 1}/${maxAttempts}`);
94 }
95 } else {
96 break;
97 }
98 }
99
100 return lastError!;
101 }
102}
Microservices Communication
Handling errors in distributed systems with circuit breakers, timeouts, and fallback strategies.
Service Client with Circuit Breaker
Resilient Service Communicationtypescript
1import { tryAsync, isTryError, createTryError, timeout, retry } from 'try-error';
2
3interface CircuitBreakerState {
4 state: 'closed' | 'open' | 'half-open';
5 failureCount: number;
6 lastFailureTime: number;
7 successCount: number;
8}
9
10class ServiceClient {
11 private circuitBreaker: Map<string, CircuitBreakerState> = new Map();
12 private readonly failureThreshold = 5;
13 private readonly recoveryTimeout = 30000; // 30 seconds
14 private readonly requestTimeout = 5000; // 5 seconds
15
16 async callService<T>(
17 serviceName: string,
18 operation: () => Promise<T>,
19 fallback?: () => Promise<T>
20 ): Promise<TryResult<T, TryError>> {
21
22 // Check circuit breaker
23 const circuitState = this.getCircuitState(serviceName);
24 if (circuitState.state === 'open') {
25 if (Date.now() - circuitState.lastFailureTime < this.recoveryTimeout) {
26 return this.handleCircuitOpen(serviceName, fallback);
27 } else {
28 // Try to recover - move to half-open
29 circuitState.state = 'half-open';
30 circuitState.successCount = 0;
31 }
32 }
33
34 // Execute the operation with timeout and retry
35 const result = await retry(
36 () => timeout(operation, this.requestTimeout),
37 {
38 maxAttempts: 3,
39 delay: 1000,
40 backoff: 'exponential',
41 shouldRetry: (error) => {
42 return hasErrorType(error, 'NetworkError') ||
43 hasErrorType(error, 'TimeoutError');
44 }
45 }
46 );
47
48 // Update circuit breaker based on result
49 this.updateCircuitBreaker(serviceName, result);
50
51 // If operation failed and we have a fallback, try it
52 if (isTryError(result) && fallback) {
53 console.warn(`Service ${serviceName} failed, using fallback:`, result.message);
54 return tryAsync(fallback);
55 }
56
57 return result;
58 }
59
60 private getCircuitState(serviceName: string): CircuitBreakerState {
61 if (!this.circuitBreaker.has(serviceName)) {
62 this.circuitBreaker.set(serviceName, {
63 state: 'closed',
64 failureCount: 0,
65 lastFailureTime: 0,
66 successCount: 0
67 });
68 }
69 return this.circuitBreaker.get(serviceName)!;
70 }
71
72 private updateCircuitBreaker<T>(serviceName: string, result: TryResult<T, TryError>): void {
73 const state = this.getCircuitState(serviceName);
74
75 if (isTryError(result)) {
76 state.failureCount++;
77 state.lastFailureTime = Date.now();
78 state.successCount = 0;
79
80 // Open circuit if failure threshold reached
81 if (state.failureCount >= this.failureThreshold) {
82 state.state = 'open';
83 console.warn(`Circuit breaker opened for service: ${serviceName}`);
84 }
85 } else {
86 // Success
87 if (state.state === 'half-open') {
88 state.successCount++;
89 // Close circuit after successful recovery
90 if (state.successCount >= 2) {
91 state.state = 'closed';
92 state.failureCount = 0;
93 console.info(`Circuit breaker closed for service: ${serviceName}`);
94 }
95 } else {
96 state.failureCount = 0;
97 }
98 }
99 }
100
101 private async handleCircuitOpen<T>(
102 serviceName: string,
103 fallback?: () => Promise<T>
104 ): Promise<TryResult<T, TryError>> {
105 if (fallback) {
106 console.info(`Circuit breaker open for ${serviceName}, using fallback`);
107 return tryAsync(fallback);
108 }
109
110 return createTryError('CircuitBreakerError', `Service ${serviceName} is unavailable`, {
111 serviceName,
112 state: 'open',
113 retryAfter: this.recoveryTimeout
114 });
115 }
116}
117
118// Usage example
119class UserService {
120 private serviceClient = new ServiceClient();
121
122 async getUserProfile(userId: string): Promise<TryResult<UserProfile, TryError>> {
123 return this.serviceClient.callService(
124 'user-profile-service',
125 () => this.fetchUserProfileFromAPI(userId),
126 () => this.getUserProfileFromCache(userId)
127 );
128 }
129
130 async getUserPreferences(userId: string): Promise<TryResult<UserPreferences, TryError>> {
131 return this.serviceClient.callService(
132 'user-preferences-service',
133 () => this.fetchUserPreferencesFromAPI(userId),
134 () => this.getDefaultUserPreferences()
135 );
136 }
137
138 private async fetchUserProfileFromAPI(userId: string): Promise<UserProfile> {
139 const response = await fetch(`/api/users/${userId}/profile`);
140 if (!response.ok) {
141 throw createTryError('NetworkError', 'Failed to fetch user profile', {
142 userId,
143 status: response.status,
144 statusText: response.statusText
145 });
146 }
147 return response.json();
148 }
149
150 private async getUserProfileFromCache(userId: string): Promise<UserProfile> {
151 const cached = await this.cache.get(`user-profile:${userId}`);
152 if (!cached) {
153 throw createTryError('CacheError', 'User profile not found in cache', { userId });
154 }
155 return JSON.parse(cached);
156 }
157}
Data Processing Pipeline
A robust data processing pipeline with validation, transformation, and error recovery.
ETL Pipeline
Data Processing with Error Handlingtypescript
1import { tryAsync, isTryError, combineResults, sequenceResults } from 'try-error';
2
3interface DataRecord {
4 id: string;
5 source: string;
6 data: Record<string, any>;
7 timestamp: Date;
8}
9
10interface ProcessingResult {
11 processed: DataRecord[];
12 failed: Array<{ record: DataRecord; error: TryError }>;
13 summary: {
14 total: number;
15 successful: number;
16 failed: number;
17 duration: number;
18 };
19}
20
21class DataProcessor {
22 async processBatch(records: DataRecord[]): Promise<TryResult<ProcessingResult, TryError>> {
23 const startTime = Date.now();
24
25 return tryAsync(async () => {
26 const results = await Promise.all(
27 records.map(record => this.processRecord(record))
28 );
29
30 const processed: DataRecord[] = [];
31 const failed: Array<{ record: DataRecord; error: TryError }> = [];
32
33 results.forEach((result, index) => {
34 if (isTryError(result)) {
35 failed.push({ record: records[index], error: result });
36 } else {
37 processed.push(result);
38 }
39 });
40
41 const duration = Date.now() - startTime;
42
43 // Log processing summary
44 console.info('Batch processing completed:', {
45 total: records.length,
46 successful: processed.length,
47 failed: failed.length,
48 duration
49 });
50
51 return {
52 processed,
53 failed,
54 summary: {
55 total: records.length,
56 successful: processed.length,
57 failed: failed.length,
58 duration
59 }
60 };
61 });
62 }
63
64 private async processRecord(record: DataRecord): Promise<TryResult<DataRecord, TryError>> {
65 // Step 1: Validate record
66 const validationResult = await this.validateRecord(record);
67 if (isTryError(validationResult)) {
68 return validationResult;
69 }
70
71 // Step 2: Transform data
72 const transformResult = await this.transformRecord(validationResult);
73 if (isTryError(transformResult)) {
74 return transformResult;
75 }
76
77 // Step 3: Enrich with external data
78 const enrichResult = await this.enrichRecord(transformResult);
79 if (isTryError(enrichResult)) {
80 // Enrichment failure is not critical - continue with original data
81 console.warn(`Failed to enrich record ${record.id}:`, enrichResult.message);
82 return transformResult;
83 }
84
85 // Step 4: Save to database
86 const saveResult = await this.saveRecord(enrichResult);
87 if (isTryError(saveResult)) {
88 return saveResult;
89 }
90
91 return enrichResult;
92 }
93
94 private async validateRecord(record: DataRecord): Promise<TryResult<DataRecord, TryError>> {
95 return tryAsync(async () => {
96 // Required fields validation
97 if (!record.id) {
98 throw createTryError('ValidationError', 'Record ID is required', {
99 recordId: record.id,
100 field: 'id'
101 });
102 }
103
104 if (!record.source) {
105 throw createTryError('ValidationError', 'Record source is required', {
106 recordId: record.id,
107 field: 'source'
108 });
109 }
110
111 // Data type validation
112 if (typeof record.data !== 'object' || record.data === null) {
113 throw createTryError('ValidationError', 'Record data must be an object', {
114 recordId: record.id,
115 field: 'data',
116 type: typeof record.data
117 });
118 }
119
120 // Business rule validation
121 if (record.source === 'api' && !record.data.userId) {
122 throw createTryError('ValidationError', 'API records must include userId', {
123 recordId: record.id,
124 source: record.source,
125 field: 'userId'
126 });
127 }
128
129 return record;
130 });
131 }
132
133 private async transformRecord(record: DataRecord): Promise<TryResult<DataRecord, TryError>> {
134 return tryAsync(async () => {
135 const transformedData = { ...record.data };
136
137 // Normalize field names
138 if (transformedData.user_id) {
139 transformedData.userId = transformedData.user_id;
140 delete transformedData.user_id;
141 }
142
143 // Convert timestamps
144 if (transformedData.created_at) {
145 transformedData.createdAt = new Date(transformedData.created_at);
146 delete transformedData.created_at;
147 }
148
149 // Validate transformed data
150 if (transformedData.email && !this.isValidEmail(transformedData.email)) {
151 throw createTryError('TransformationError', 'Invalid email format after transformation', {
152 recordId: record.id,
153 email: transformedData.email
154 });
155 }
156
157 return {
158 ...record,
159 data: transformedData
160 };
161 });
162 }
163
164 private async enrichRecord(record: DataRecord): Promise<TryResult<DataRecord, TryError>> {
165 return tryAsync(async () => {
166 const enrichedData = { ...record.data };
167
168 // Enrich with user data if userId is present
169 if (enrichedData.userId) {
170 const userResult = await this.fetchUserData(enrichedData.userId);
171 if (!isTryError(userResult)) {
172 enrichedData.userProfile = userResult;
173 }
174 }
175
176 // Enrich with geolocation if IP is present
177 if (enrichedData.ipAddress) {
178 const locationResult = await this.fetchLocationData(enrichedData.ipAddress);
179 if (!isTryError(locationResult)) {
180 enrichedData.location = locationResult;
181 }
182 }
183
184 return {
185 ...record,
186 data: enrichedData
187 };
188 });
189 }
190
191 private async saveRecord(record: DataRecord): Promise<TryResult<void, TryError>> {
192 return tryAsync(async () => {
193 await this.database.save('processed_records', record);
194 });
195 }
196
197 private isValidEmail(email: string): boolean {
198 const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
199 return emailRegex.test(email);
200 }
201}
CLI Application
A command-line tool with comprehensive error handling, user-friendly messages, and graceful degradation.
File Processing CLI
CLI with Error Handlingtypescript
1#!/usr/bin/env node
2import { tryAsync, isTryError, createTryError, hasErrorType } from 'try-error';
3import { promises as fs } from 'fs';
4import path from 'path';
5
6interface CLIOptions {
7 input: string;
8 output?: string;
9 format: 'json' | 'csv' | 'xml';
10 verbose: boolean;
11 force: boolean;
12}
13
14class FileProcessorCLI {
15 async run(args: string[]): Promise<void> {
16 const optionsResult = this.parseArguments(args);
17 if (isTryError(optionsResult)) {
18 this.handleError(optionsResult);
19 process.exit(1);
20 }
21
22 const options = optionsResult;
23
24 if (options.verbose) {
25 console.log('Starting file processing with options:', options);
26 }
27
28 const result = await this.processFile(options);
29 if (isTryError(result)) {
30 this.handleError(result);
31 process.exit(1);
32 }
33
34 console.log('✅ File processing completed successfully!');
35 if (options.output) {
36 console.log(`📁 Output saved to: ${options.output}`);
37 }
38 }
39
40 private parseArguments(args: string[]): TryResult<CLIOptions, TryError> {
41 try {
42 const options: Partial<CLIOptions> = {
43 verbose: false,
44 force: false,
45 format: 'json'
46 };
47
48 for (let i = 0; i < args.length; i++) {
49 const arg = args[i];
50
51 switch (arg) {
52 case '--input':
53 case '-i':
54 options.input = args[++i];
55 break;
56 case '--output':
57 case '-o':
58 options.output = args[++i];
59 break;
60 case '--format':
61 case '-f':
62 const format = args[++i];
63 if (!['json', 'csv', 'xml'].includes(format)) {
64 throw createTryError('ValidationError', 'Invalid format specified', {
65 format,
66 validFormats: ['json', 'csv', 'xml']
67 });
68 }
69 options.format = format as 'json' | 'csv' | 'xml';
70 break;
71 case '--verbose':
72 case '-v':
73 options.verbose = true;
74 break;
75 case '--force':
76 options.force = true;
77 break;
78 case '--help':
79 case '-h':
80 this.showHelp();
81 process.exit(0);
82 break;
83 default:
84 if (arg.startsWith('-')) {
85 throw createTryError('ValidationError', `Unknown option: ${arg}`, {
86 option: arg
87 });
88 }
89 }
90 }
91
92 if (!options.input) {
93 throw createTryError('ValidationError', 'Input file is required', {
94 hint: 'Use --input or -i to specify the input file'
95 });
96 }
97
98 return options as CLIOptions;
99 } catch (error) {
100 if (isTryError(error)) {
101 return error;
102 }
103 return createTryError('ParseError', 'Failed to parse command line arguments', {
104 originalError: error
105 });
106 }
107 }
108
109 private async processFile(options: CLIOptions): Promise<TryResult<void, TryError>> {
110 // Step 1: Validate input file
111 const inputValidation = await this.validateInputFile(options.input);
112 if (isTryError(inputValidation)) {
113 return inputValidation;
114 }
115
116 // Step 2: Read and parse input file
117 const readResult = await this.readInputFile(options.input);
118 if (isTryError(readResult)) {
119 return readResult;
120 }
121
122 // Step 3: Process data
123 const processResult = await this.processData(readResult, options);
124 if (isTryError(processResult)) {
125 return processResult;
126 }
127
128 // Step 4: Write output file
129 if (options.output) {
130 const writeResult = await this.writeOutputFile(options.output, processResult, options);
131 if (isTryError(writeResult)) {
132 return writeResult;
133 }
134 } else {
135 // Output to console
136 console.log(JSON.stringify(processResult, null, 2));
137 }
138
139 return undefined as any; // Success
140 }
141
142 private async validateInputFile(inputPath: string): Promise<TryResult<void, TryError>> {
143 return tryAsync(async () => {
144 try {
145 const stats = await fs.stat(inputPath);
146
147 if (!stats.isFile()) {
148 throw createTryError('FileError', 'Input path is not a file', {
149 path: inputPath,
150 type: 'directory'
151 });
152 }
153
154 if (stats.size === 0) {
155 throw createTryError('FileError', 'Input file is empty', {
156 path: inputPath,
157 size: stats.size
158 });
159 }
160
161 if (stats.size > 100 * 1024 * 1024) { // 100MB limit
162 throw createTryError('FileError', 'Input file is too large', {
163 path: inputPath,
164 size: stats.size,
165 maxSize: 100 * 1024 * 1024
166 });
167 }
168
169 } catch (error) {
170 if (error.code === 'ENOENT') {
171 throw createTryError('FileError', 'Input file does not exist', {
172 path: inputPath,
173 code: error.code
174 });
175 }
176 if (error.code === 'EACCES') {
177 throw createTryError('FileError', 'Permission denied accessing input file', {
178 path: inputPath,
179 code: error.code
180 });
181 }
182 throw error;
183 }
184 });
185 }
186
187 private async readInputFile(inputPath: string): Promise<TryResult<any[], TryError>> {
188 return tryAsync(async () => {
189 const content = await fs.readFile(inputPath, 'utf-8');
190 const ext = path.extname(inputPath).toLowerCase();
191
192 switch (ext) {
193 case '.json':
194 try {
195 const data = JSON.parse(content);
196 return Array.isArray(data) ? data : [data];
197 } catch (parseError) {
198 throw createTryError('ParseError', 'Invalid JSON format', {
199 path: inputPath,
200 error: parseError.message
201 });
202 }
203
204 case '.csv':
205 return this.parseCSV(content);
206
207 default:
208 throw createTryError('FileError', 'Unsupported file format', {
209 path: inputPath,
210 extension: ext,
211 supportedFormats: ['.json', '.csv']
212 });
213 }
214 });
215 }
216
217 private handleError(error: TryError): void {
218 console.error('❌ Error:', error.message);
219
220 if (hasErrorType(error, 'ValidationError')) {
221 console.error('💡 Validation issue:', error.context);
222 if (error.context?.hint) {
223 console.error('💡 Hint:', error.context.hint);
224 }
225 } else if (hasErrorType(error, 'FileError')) {
226 console.error('📁 File issue:', error.context);
227 if (error.context?.code === 'ENOENT') {
228 console.error('💡 Make sure the file path is correct and the file exists');
229 } else if (error.context?.code === 'EACCES') {
230 console.error('💡 Check file permissions or run with appropriate privileges');
231 }
232 } else if (hasErrorType(error, 'ParseError')) {
233 console.error('🔍 Parse issue:', error.context);
234 console.error('💡 Check the file format and content structure');
235 }
236
237 if (error.context?.originalError) {
238 console.error('🔧 Technical details:', error.context.originalError);
239 }
240 }
241
242 private showHelp(): void {
243 console.log(`
244File Processor CLI
245
246Usage: file-processor [options]
247
248Options:
249 -i, --input <file> Input file path (required)
250 -o, --output <file> Output file path (optional, prints to console if not specified)
251 -f, --format <format> Output format: json, csv, xml (default: json)
252 -v, --verbose Enable verbose logging
253 --force Overwrite output file if it exists
254 -h, --help Show this help message
255
256Examples:
257 file-processor -i data.json -o processed.json
258 file-processor -i data.csv -o result.xml -f xml
259 file-processor -i input.json --verbose
260`);
261 }
262}
263
264// CLI entry point
265if (require.main === module) {
266 const cli = new FileProcessorCLI();
267 cli.run(process.argv.slice(2));
268}
Production Best Practices
✅ Error Handling Patterns
- • Use circuit breakers for external service calls
- • Implement comprehensive rollback mechanisms
- • Provide meaningful fallback strategies
- • Add retry logic with exponential backoff
- • Include detailed context in error objects
- • Log errors with appropriate severity levels
🔧 Operational Considerations
- • Monitor error rates and patterns
- • Set up alerts for critical error types
- • Implement health checks and readiness probes
- • Use structured logging for better observability
- • Track error recovery success rates
- • Implement graceful degradation strategies
⚡ Performance Tips
- • Use timeouts to prevent hanging operations
- • Implement request deduplication for retries
- • Cache fallback data when possible
- • Use async processing for non-critical operations
- • Batch operations to reduce overhead
- • Monitor and optimize error handling paths