source: frontend/node_modules/axios/MIGRATION_GUIDE.md

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 21.2 KB
Line 
1# Axios Migration Guide
2
3> **Migrating from Axios 0.x to 1.x**
4>
5> This guide helps developers upgrade from Axios 0.x to 1.x by documenting breaking changes, providing migration strategies, and offering solutions to common upgrade challenges.
6
7## Table of Contents
8
9- [Overview](#overview)
10- [Breaking Changes](#breaking-changes)
11- [Error Handling Migration](#error-handling-migration)
12- [API Changes](#api-changes)
13- [Configuration Changes](#configuration-changes)
14- [Migration Strategies](#migration-strategies)
15- [Common Patterns](#common-patterns)
16- [Troubleshooting](#troubleshooting)
17- [Resources](#resources)
18
19## Overview
20
21Axios 1.x introduced several breaking changes to improve consistency, security, and developer experience. While these changes provide better error handling and more predictable behavior, they require code updates when migrating from 0.x versions.
22
23### Key Changes Summary
24
25| Area | 0.x Behavior | 1.x Behavior | Impact |
26|------|--------------|--------------|--------|
27| Error Handling | Selective throwing | Consistent throwing | High |
28| JSON Parsing | Lenient | Strict | Medium |
29| Browser Support | IE11+ | Modern browsers | Low-Medium |
30| TypeScript | Partial | Full support | Low |
31
32### Migration Complexity
33
34- **Simple applications**: 1-2 hours
35- **Medium applications**: 1-2 days
36- **Large applications with complex error handling**: 3-5 days
37
38## Breaking Changes
39
40### 1. Error Handling Changes
41
42**The most significant change in Axios 1.x is how errors are handled.**
43
44#### 0.x Behavior
45```javascript
46// Axios 0.x - Some HTTP error codes didn't throw
47axios.get('/api/data')
48 .then(response => {
49 // Response interceptor could handle all errors
50 console.log('Success:', response.data);
51 });
52
53// Response interceptor handled everything
54axios.interceptors.response.use(
55 response => response,
56 error => {
57 handleError(error);
58 // Error was "handled" and didn't propagate
59 }
60);
61```
62
63#### 1.x Behavior
64```javascript
65// Axios 1.x - All HTTP errors throw consistently
66axios.get('/api/data')
67 .then(response => {
68 console.log('Success:', response.data);
69 })
70 .catch(error => {
71 // Must handle errors at call site or they propagate
72 console.error('Request failed:', error);
73 });
74
75// Response interceptor must re-throw or return rejected promise
76axios.interceptors.response.use(
77 response => response,
78 error => {
79 handleError(error);
80 // Must explicitly handle propagation
81 return Promise.reject(error); // or throw error;
82 }
83);
84```
85
86#### Impact
87- **Response interceptors** can no longer "swallow" errors silently
88- **Every API call** must handle errors explicitly or they become unhandled promise rejections
89- **Centralized error handling** requires new patterns
90
91### 2. JSON Parsing Changes
92
93#### 0.x Behavior
94```javascript
95// Axios 0.x - Lenient JSON parsing
96// Would attempt to parse even invalid JSON
97response.data; // Might contain partial data or fallbacks
98```
99
100#### 1.x Behavior
101```javascript
102// Axios 1.x - Strict JSON parsing
103// Throws clear errors for invalid JSON
104try {
105 const data = response.data;
106} catch (error) {
107 // Handle JSON parsing errors explicitly
108}
109```
110
111### 3. Request/Response Transform Changes
112
113#### 0.x Behavior
114```javascript
115// Implicit transformations with some edge cases
116transformRequest: [function (data) {
117 // Less predictable behavior
118 return data;
119}]
120```
121
122#### 1.x Behavior
123```javascript
124// More consistent transformation pipeline
125transformRequest: [function (data, headers) {
126 // Headers parameter always available
127 // More predictable behavior
128 return data;
129}]
130```
131
132### 4. Browser Support Changes
133
134- **0.x**: Supported IE11 and older browsers
135- **1.x**: Requires modern browsers with Promise support
136- **Polyfills**: May be needed for older browser support
137
138## Error Handling Migration
139
140The error handling changes are the most complex part of migrating to Axios 1.x. Here are proven strategies:
141
142### Strategy 1: Centralized Error Handling with Error Boundary
143
144```javascript
145// Create a centralized error handler
146class ApiErrorHandler {
147 constructor() {
148 this.setupInterceptors();
149 }
150
151 setupInterceptors() {
152 axios.interceptors.response.use(
153 response => response,
154 error => {
155 // Centralized error processing
156 this.processError(error);
157
158 // Return a resolved promise with error info for handled errors
159 if (this.isHandledError(error)) {
160 return Promise.resolve({
161 data: null,
162 error: this.normalizeError(error),
163 handled: true
164 });
165 }
166
167 // Re-throw unhandled errors
168 return Promise.reject(error);
169 }
170 );
171 }
172
173 processError(error) {
174 // Log errors
175 console.error('API Error:', error);
176
177 // Show user notifications
178 if (error.response?.status === 401) {
179 this.handleAuthError();
180 } else if (error.response?.status >= 500) {
181 this.showErrorNotification('Server error occurred');
182 }
183 }
184
185 isHandledError(error) {
186 // Define which errors are "handled" centrally
187 const handledStatuses = [401, 403, 404, 422, 500, 502, 503];
188 return handledStatuses.includes(error.response?.status);
189 }
190
191 normalizeError(error) {
192 return {
193 status: error.response?.status,
194 message: error.response?.data?.message || error.message,
195 code: error.response?.data?.code || error.code
196 };
197 }
198
199 handleAuthError() {
200 // Redirect to login, clear tokens, etc.
201 localStorage.removeItem('token');
202 window.location.href = '/login';
203 }
204
205 showErrorNotification(message) {
206 // Show user-friendly error message
207 console.error(message); // Replace with your notification system
208 }
209}
210
211// Initialize globally
212const errorHandler = new ApiErrorHandler();
213
214// Usage in components/services
215async function fetchUserData(userId) {
216 try {
217 const response = await axios.get(`/api/users/${userId}`);
218
219 // Check if error was handled centrally
220 if (response.handled) {
221 return { data: null, error: response.error };
222 }
223
224 return { data: response.data, error: null };
225 } catch (error) {
226 // Unhandled errors still need local handling
227 return { data: null, error: { message: 'Unexpected error occurred' } };
228 }
229}
230```
231
232### Strategy 2: Wrapper Function Pattern
233
234```javascript
235// Create a wrapper that provides 0.x-like behavior
236function createApiWrapper() {
237 const api = axios.create();
238
239 // Add response interceptor for centralized handling
240 api.interceptors.response.use(
241 response => response,
242 error => {
243 // Handle common errors centrally
244 if (error.response?.status === 401) {
245 // Handle auth errors
246 handleAuthError();
247 }
248
249 if (error.response?.status >= 500) {
250 // Handle server errors
251 showServerErrorNotification();
252 }
253
254 // Always reject to maintain error propagation
255 return Promise.reject(error);
256 }
257 );
258
259 // Wrapper function that mimics 0.x behavior
260 function safeRequest(requestConfig, options = {}) {
261 return api(requestConfig)
262 .then(response => response)
263 .catch(error => {
264 if (options.suppressErrors) {
265 // Return error info instead of throwing
266 return {
267 data: null,
268 error: {
269 status: error.response?.status,
270 message: error.response?.data?.message || error.message
271 }
272 };
273 }
274 throw error;
275 });
276 }
277
278 return { safeRequest, axios: api };
279}
280
281// Usage
282const { safeRequest } = createApiWrapper();
283
284// For calls where you want centralized error handling
285const result = await safeRequest(
286 { method: 'get', url: '/api/data' },
287 { suppressErrors: true }
288);
289
290if (result.error) {
291 // Handle error case
292 console.log('Request failed:', result.error.message);
293} else {
294 // Handle success case
295 console.log('Data:', result.data);
296}
297```
298
299### Strategy 3: Global Error Handler with Custom Events
300
301```javascript
302// Set up global error handling with events
303class GlobalErrorHandler extends EventTarget {
304 constructor() {
305 super();
306 this.setupInterceptors();
307 }
308
309 setupInterceptors() {
310 axios.interceptors.response.use(
311 response => response,
312 error => {
313 // Emit custom event for global handling
314 this.dispatchEvent(new CustomEvent('apiError', {
315 detail: { error, timestamp: new Date() }
316 }));
317
318 // Always reject to maintain proper error flow
319 return Promise.reject(error);
320 }
321 );
322 }
323}
324
325const globalErrorHandler = new GlobalErrorHandler();
326
327// Set up global listeners
328globalErrorHandler.addEventListener('apiError', (event) => {
329 const { error } = event.detail;
330
331 // Centralized error logic
332 if (error.response?.status === 401) {
333 handleAuthError();
334 }
335
336 if (error.response?.status >= 500) {
337 showErrorNotification('Server error occurred');
338 }
339});
340
341// Usage remains clean
342async function apiCall() {
343 try {
344 const response = await axios.get('/api/data');
345 return response.data;
346 } catch (error) {
347 // Error was already handled globally
348 // Just handle component-specific logic
349 return null;
350 }
351}
352```
353
354## API Changes
355
356### Request Configuration
357
358#### 0.x to 1.x Changes
359```javascript
360// 0.x - Some properties had different defaults
361const config = {
362 timeout: 0, // No timeout by default
363 maxContentLength: -1, // No limit
364};
365
366// 1.x - More secure defaults
367const config = {
368 timeout: 0, // Still no timeout, but easier to configure
369 maxContentLength: 2000, // Default limit for security
370 maxBodyLength: 2000, // New property
371};
372```
373
374### Response Object
375
376The response object structure remains largely the same, but error responses are more consistent:
377
378```javascript
379// Both 0.x and 1.x
380response = {
381 data: {}, // Response body
382 status: 200, // HTTP status
383 statusText: 'OK', // HTTP status message
384 headers: {}, // Response headers
385 config: {}, // Request config
386 request: {} // Request object
387};
388
389// Error responses are more consistent in 1.x
390error.response = {
391 data: {}, // Error response body
392 status: 404, // HTTP error status
393 statusText: 'Not Found',
394 headers: {},
395 config: {},
396 request: {}
397};
398```
399
400## Configuration Changes
401
402### Default Configuration Updates
403
404```javascript
405// 0.x defaults
406axios.defaults.timeout = 0; // No timeout
407axios.defaults.maxContentLength = -1; // No limit
408
409// 1.x defaults (more secure)
410axios.defaults.timeout = 0; // Still no timeout
411axios.defaults.maxContentLength = 2000; // 2MB limit
412axios.defaults.maxBodyLength = 2000; // 2MB limit
413```
414
415### Instance Configuration
416
417```javascript
418// 0.x - Instance creation
419const api = axios.create({
420 baseURL: 'https://api.example.com',
421 timeout: 1000,
422});
423
424// 1.x - Same API, but more options available
425const api = axios.create({
426 baseURL: 'https://api.example.com',
427 timeout: 1000,
428 maxBodyLength: Infinity, // Override default if needed
429 maxContentLength: Infinity,
430});
431```
432
433## Migration Strategies
434
435### Step-by-Step Migration Process
436
437#### Phase 1: Preparation
4381. **Audit Current Error Handling**
439 ```bash
440 # Find all axios usage
441 grep -r "axios\." src/
442 grep -r "\.catch" src/
443 grep -r "interceptors" src/
444 ```
445
4462. **Identify Patterns**
447 - Response interceptors that handle errors
448 - Components that rely on centralized error handling
449 - Authentication and retry logic
450
4513. **Create Test Cases**
452 ```javascript
453 // Test current error handling behavior
454 describe('Error Handling Migration', () => {
455 it('should handle 401 errors consistently', async () => {
456 // Test authentication error flows
457 });
458
459 it('should handle 500 errors with user feedback', async () => {
460 // Test server error handling
461 });
462 });
463 ```
464
465#### Phase 2: Implementation
4661. **Update Dependencies**
467 ```bash
468 npm update axios
469 ```
470
4712. **Implement New Error Handling**
472 - Choose one of the strategies above
473 - Update response interceptors
474 - Add error handling to API calls
475
4763. **Update Authentication Logic**
477 ```javascript
478 // 0.x pattern
479 axios.interceptors.response.use(null, error => {
480 if (error.response?.status === 401) {
481 logout();
482 // Error was "handled"
483 }
484 });
485
486 // 1.x pattern
487 axios.interceptors.response.use(
488 response => response,
489 error => {
490 if (error.response?.status === 401) {
491 logout();
492 }
493 return Promise.reject(error); // Always propagate
494 }
495 );
496 ```
497
498#### Phase 3: Testing and Validation
4991. **Test Error Scenarios**
500 - Network failures
501 - HTTP error codes (401, 403, 404, 500, etc.)
502 - Timeout errors
503 - JSON parsing errors
504
5052. **Validate User Experience**
506 - Error messages are shown appropriately
507 - Authentication redirects work
508 - Loading states are handled correctly
509
510### Gradual Migration Approach
511
512For large applications, consider gradual migration:
513
514```javascript
515// Create a compatibility layer
516const axiosCompat = {
517 // Use new axios instance for new code
518 v1: axios.create({
519 // 1.x configuration
520 }),
521
522 // Wrapper for legacy code
523 legacy: createLegacyWrapper(axios.create({
524 // Configuration that mimics 0.x behavior
525 }))
526};
527
528function createLegacyWrapper(axiosInstance) {
529 // Add interceptors that provide 0.x-like behavior
530 axiosInstance.interceptors.response.use(
531 response => response,
532 error => {
533 // Handle errors in 0.x style for legacy code
534 handleLegacyError(error);
535 // Don't propagate certain errors
536 if (shouldSuppressError(error)) {
537 return Promise.resolve({ data: null, error: true });
538 }
539 return Promise.reject(error);
540 }
541 );
542
543 return axiosInstance;
544}
545```
546
547## Common Patterns
548
549### Authentication Interceptors
550
551#### Updated Authentication Pattern
552```javascript
553// Token refresh interceptor for 1.x
554let isRefreshing = false;
555let refreshSubscribers = [];
556
557function subscribeTokenRefresh(cb) {
558 refreshSubscribers.push(cb);
559}
560
561function onTokenRefreshed(token) {
562 refreshSubscribers.forEach(cb => cb(token));
563 refreshSubscribers = [];
564}
565
566axios.interceptors.response.use(
567 response => response,
568 async error => {
569 const originalRequest = error.config;
570
571 if (error.response?.status === 401 && !originalRequest._retry) {
572 if (isRefreshing) {
573 // Wait for token refresh
574 return new Promise(resolve => {
575 subscribeTokenRefresh(token => {
576 originalRequest.headers.Authorization = `Bearer ${token}`;
577 resolve(axios(originalRequest));
578 });
579 });
580 }
581
582 originalRequest._retry = true;
583 isRefreshing = true;
584
585 try {
586 const newToken = await refreshToken();
587 onTokenRefreshed(newToken);
588 isRefreshing = false;
589
590 originalRequest.headers.Authorization = `Bearer ${newToken}`;
591 return axios(originalRequest);
592 } catch (refreshError) {
593 isRefreshing = false;
594 logout();
595 return Promise.reject(refreshError);
596 }
597 }
598
599 return Promise.reject(error);
600 }
601);
602```
603
604### Retry Logic
605
606```javascript
607// Retry interceptor for 1.x
608function createRetryInterceptor(maxRetries = 3, retryDelay = 1000) {
609 return axios.interceptors.response.use(
610 response => response,
611 async error => {
612 const config = error.config;
613
614 if (!config || !config.retry) {
615 return Promise.reject(error);
616 }
617
618 config.__retryCount = config.__retryCount || 0;
619
620 if (config.__retryCount >= maxRetries) {
621 return Promise.reject(error);
622 }
623
624 config.__retryCount += 1;
625
626 // Exponential backoff
627 const delay = retryDelay * Math.pow(2, config.__retryCount - 1);
628 await new Promise(resolve => setTimeout(resolve, delay));
629
630 return axios(config);
631 }
632 );
633}
634
635// Usage
636const api = axios.create();
637createRetryInterceptor(3, 1000);
638
639// Make request with retry
640api.get('/api/data', { retry: true });
641```
642
643### Loading State Management
644
645```javascript
646// Loading interceptor for 1.x
647class LoadingManager {
648 constructor() {
649 this.requests = new Set();
650 this.setupInterceptors();
651 }
652
653 setupInterceptors() {
654 axios.interceptors.request.use(config => {
655 this.requests.add(config);
656 this.updateLoadingState();
657 return config;
658 });
659
660 axios.interceptors.response.use(
661 response => {
662 this.requests.delete(response.config);
663 this.updateLoadingState();
664 return response;
665 },
666 error => {
667 this.requests.delete(error.config);
668 this.updateLoadingState();
669 return Promise.reject(error);
670 }
671 );
672 }
673
674 updateLoadingState() {
675 const isLoading = this.requests.size > 0;
676 // Update your loading UI
677 document.body.classList.toggle('loading', isLoading);
678 }
679}
680
681const loadingManager = new LoadingManager();
682```
683
684## Troubleshooting
685
686### Common Migration Issues
687
688#### Issue 1: Unhandled Promise Rejections
689
690**Problem:**
691```javascript
692// This pattern worked in 0.x but causes unhandled rejections in 1.x
693axios.get('/api/data'); // No .catch() handler
694```
695
696**Solution:**
697```javascript
698// Always handle promises
699axios.get('/api/data')
700 .catch(error => {
701 // Handle error appropriately
702 console.error('Request failed:', error.message);
703 });
704
705// Or use async/await with try/catch
706async function fetchData() {
707 try {
708 const response = await axios.get('/api/data');
709 return response.data;
710 } catch (error) {
711 console.error('Request failed:', error.message);
712 return null;
713 }
714}
715```
716
717#### Issue 2: Response Interceptors Not "Handling" Errors
718
719**Problem:**
720```javascript
721// 0.x style - interceptor "handled" errors
722axios.interceptors.response.use(null, error => {
723 showErrorMessage(error.message);
724 // Error was considered "handled"
725});
726```
727
728**Solution:**
729```javascript
730// 1.x style - explicitly control error propagation
731axios.interceptors.response.use(
732 response => response,
733 error => {
734 showErrorMessage(error.message);
735
736 // Choose whether to propagate the error
737 if (shouldPropagateError(error)) {
738 return Promise.reject(error);
739 }
740
741 // Return success-like response for "handled" errors
742 return Promise.resolve({
743 data: null,
744 handled: true,
745 error: normalizeError(error)
746 });
747 }
748);
749```
750
751#### Issue 3: JSON Parsing Errors
752
753**Problem:**
754```javascript
755// 1.x is stricter about JSON parsing
756// This might throw where 0.x was lenient
757const data = response.data;
758```
759
760**Solution:**
761```javascript
762// Add response transformer for better error handling
763axios.defaults.transformResponse = [
764 function (data) {
765 if (typeof data === 'string') {
766 try {
767 return JSON.parse(data);
768 } catch (e) {
769 // Handle JSON parsing errors gracefully
770 console.warn('Invalid JSON response:', data);
771 return { error: 'Invalid JSON', rawData: data };
772 }
773 }
774 return data;
775 }
776];
777```
778
779#### Issue 4: TypeScript Errors After Upgrade
780
781**Problem:**
782```typescript
783// TypeScript errors after upgrade
784const response = await axios.get('/api/data');
785// Property 'someProperty' does not exist on type 'any'
786```
787
788**Solution:**
789```typescript
790// Define proper interfaces
791interface ApiResponse {
792 data: any;
793 message: string;
794 success: boolean;
795}
796
797const response = await axios.get<ApiResponse>('/api/data');
798// Now properly typed
799console.log(response.data.data);
800```
801
802### Debug Migration Issues
803
804#### Enable Debug Logging
805```javascript
806// Add request/response logging
807axios.interceptors.request.use(config => {
808 console.log('Request:', config);
809 return config;
810});
811
812axios.interceptors.response.use(
813 response => {
814 console.log('Response:', response);
815 return response;
816 },
817 error => {
818 console.log('Error:', error);
819 return Promise.reject(error);
820 }
821);
822```
823
824#### Compare Behavior
825```javascript
826// Create side-by-side comparison during migration
827const axios0x = require('axios-0x'); // Keep old version for testing
828const axios1x = require('axios');
829
830async function compareRequests(config) {
831 try {
832 const [result0x, result1x] = await Promise.allSettled([
833 axios0x(config),
834 axios1x(config)
835 ]);
836
837 console.log('0.x result:', result0x);
838 console.log('1.x result:', result1x);
839 } catch (error) {
840 console.log('Comparison error:', error);
841 }
842}
843```
844
845## Resources
846
847### Official Documentation
848- [Axios 1.x Documentation](https://axios-http.com/)
849- [Axios GitHub Repository](https://github.com/axios/axios)
850- [Axios Changelog](https://github.com/axios/axios/blob/main/CHANGELOG.md)
851
852### Migration Tools
853- [Axios Migration Codemod](https://github.com/axios/axios-migration-codemod) *(if available)*
854- [ESLint Rules for Axios 1.x](https://github.com/axios/eslint-plugin-axios) *(if available)*
855
856### Community Resources
857- [Stack Overflow - Axios Migration Questions](https://stackoverflow.com/questions/tagged/axios+migration)
858- [GitHub Discussions](https://github.com/axios/axios/discussions)
859- [Axios Discord Community](https://discord.gg/axios) *(if available)*
860
861### Related Issues
862- [Error Handling Changes Discussion](https://github.com/axios/axios/issues/7208)
863- [Migration Guide Request](https://github.com/axios/axios/issues/xxxx) *(link to related issues)*
864
865---
866
867## Need Help?
868
869If you encounter issues during migration that aren't covered in this guide:
870
8711. **Search existing issues** in the [Axios GitHub repository](https://github.com/axios/axios/issues)
8722. **Ask questions** in [GitHub Discussions](https://github.com/axios/axios/discussions)
8733. **Contribute improvements** to this migration guide
874
875---
876
877*This migration guide is maintained by the community. If you find errors or have suggestions, please [open an issue](https://github.com/axios/axios/issues) or submit a pull request.*
Note: See TracBrowser for help on using the repository browser.