opusdev/vector-similarity-api
1
1# Axios Migration Guide2 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 Contents8 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## Overview20 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 Summary24 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 Complexity33 34- **Simple applications**: 1-2 hours35- **Medium applications**: 1-2 days 36- **Large applications with complex error handling**: 3-5 days37 38## Breaking Changes39 40### 1. Error Handling Changes41 42**The most significant change in Axios 1.x is how errors are handled.**43 44#### 0.x Behavior45```javascript46// Axios 0.x - Some HTTP error codes didn't throw47axios.get('/api/data')48 .then(response => {49 // Response interceptor could handle all errors50 console.log('Success:', response.data);51 });52 53// Response interceptor handled everything54axios.interceptors.response.use(55 response => response,56 error => {57 handleError(error);58 // Error was "handled" and didn't propagate59 }60);61```62 63#### 1.x Behavior64```javascript65// Axios 1.x - All HTTP errors throw consistently66axios.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 propagate72 console.error('Request failed:', error);73 });74 75// Response interceptor must re-throw or return rejected promise76axios.interceptors.response.use(77 response => response,78 error => {79 handleError(error);80 // Must explicitly handle propagation81 return Promise.reject(error); // or throw error;82 }83);84```85 86#### Impact87- **Response interceptors** can no longer "swallow" errors silently88- **Every API call** must handle errors explicitly or they become unhandled promise rejections89- **Centralized error handling** requires new patterns90 91### 2. JSON Parsing Changes92 93#### 0.x Behavior94```javascript95// Axios 0.x - Lenient JSON parsing96// Would attempt to parse even invalid JSON97response.data; // Might contain partial data or fallbacks98```99 100#### 1.x Behavior101```javascript102// Axios 1.x - Strict JSON parsing103// Throws clear errors for invalid JSON104try {105 const data = response.data;106} catch (error) {107 // Handle JSON parsing errors explicitly108}109```110 111### 3. Request/Response Transform Changes112 113#### 0.x Behavior114```javascript115// Implicit transformations with some edge cases116transformRequest: [function (data) {117 // Less predictable behavior118 return data;119}]120```121 122#### 1.x Behavior123```javascript124// More consistent transformation pipeline125transformRequest: [function (data, headers) {126 // Headers parameter always available127 // More predictable behavior128 return data;129}]130```131 132### 4. Browser Support Changes133 134- **0.x**: Supported IE11 and older browsers135- **1.x**: Requires modern browsers with Promise support136- **Polyfills**: May be needed for older browser support137 138## Error Handling Migration139 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 Boundary143 144```javascript145// Create a centralized error handler146class ApiErrorHandler {147 constructor() {148 this.setupInterceptors();149 }150 151 setupInterceptors() {152 axios.interceptors.response.use(153 response => response,154 error => {155 // Centralized error processing156 this.processError(error);157 158 // Return a resolved promise with error info for handled errors159 if (this.isHandledError(error)) {160 return Promise.resolve({161 data: null,162 error: this.normalizeError(error),163 handled: true164 });165 }166 167 // Re-throw unhandled errors168 return Promise.reject(error);169 }170 );171 }172 173 processError(error) {174 // Log errors175 console.error('API Error:', error);176 177 // Show user notifications178 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" centrally187 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.code196 };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 message207 console.error(message); // Replace with your notification system208 }209}210 211// Initialize globally212const errorHandler = new ApiErrorHandler();213 214// Usage in components/services215async function fetchUserData(userId) {216 try {217 const response = await axios.get(`/api/users/${userId}`);218 219 // Check if error was handled centrally220 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 handling227 return { data: null, error: { message: 'Unexpected error occurred' } };228 }229}230```231 232### Strategy 2: Wrapper Function Pattern233 234```javascript235// Create a wrapper that provides 0.x-like behavior236function createApiWrapper() {237 const api = axios.create();238 239 // Add response interceptor for centralized handling240 api.interceptors.response.use(241 response => response,242 error => {243 // Handle common errors centrally244 if (error.response?.status === 401) {245 // Handle auth errors246 handleAuthError();247 }248 249 if (error.response?.status >= 500) {250 // Handle server errors251 showServerErrorNotification();252 }253 254 // Always reject to maintain error propagation255 return Promise.reject(error);256 }257 );258 259 // Wrapper function that mimics 0.x behavior260 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 throwing266 return {267 data: null,268 error: {269 status: error.response?.status,270 message: error.response?.data?.message || error.message271 }272 };273 }274 throw error;275 });276 }277 278 return { safeRequest, axios: api };279}280 281// Usage282const { safeRequest } = createApiWrapper();283 284// For calls where you want centralized error handling285const result = await safeRequest(286 { method: 'get', url: '/api/data' },287 { suppressErrors: true }288);289 290if (result.error) {291 // Handle error case292 console.log('Request failed:', result.error.message);293} else {294 // Handle success case295 console.log('Data:', result.data);296}297```298 299### Strategy 3: Global Error Handler with Custom Events300 301```javascript302// Set up global error handling with events303class 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 handling314 this.dispatchEvent(new CustomEvent('apiError', {315 detail: { error, timestamp: new Date() }316 }));317 318 // Always reject to maintain proper error flow319 return Promise.reject(error);320 }321 );322 }323}324 325const globalErrorHandler = new GlobalErrorHandler();326 327// Set up global listeners328globalErrorHandler.addEventListener('apiError', (event) => {329 const { error } = event.detail;330 331 // Centralized error logic332 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 clean342async function apiCall() {343 try {344 const response = await axios.get('/api/data');345 return response.data;346 } catch (error) {347 // Error was already handled globally348 // Just handle component-specific logic349 return null;350 }351}352```353 354## API Changes355 356### Request Configuration357 358#### 0.x to 1.x Changes359```javascript360// 0.x - Some properties had different defaults361const config = {362 timeout: 0, // No timeout by default363 maxContentLength: -1, // No limit364};365 366// 1.x - More secure defaults367const config = {368 timeout: 0, // Still no timeout, but easier to configure369 maxContentLength: 2000, // Default limit for security370 maxBodyLength: 2000, // New property371};372```373 374### Response Object375 376The response object structure remains largely the same, but error responses are more consistent:377 378```javascript379// Both 0.x and 1.x380response = {381 data: {}, // Response body382 status: 200, // HTTP status383 statusText: 'OK', // HTTP status message 384 headers: {}, // Response headers385 config: {}, // Request config386 request: {} // Request object387};388 389// Error responses are more consistent in 1.x390error.response = {391 data: {}, // Error response body392 status: 404, // HTTP error status393 statusText: 'Not Found',394 headers: {},395 config: {},396 request: {}397};398```399 400## Configuration Changes401 402### Default Configuration Updates403 404```javascript405// 0.x defaults406axios.defaults.timeout = 0; // No timeout407axios.defaults.maxContentLength = -1; // No limit408 409// 1.x defaults (more secure)410axios.defaults.timeout = 0; // Still no timeout411axios.defaults.maxContentLength = 2000; // 2MB limit412axios.defaults.maxBodyLength = 2000; // 2MB limit413```414 415### Instance Configuration416 417```javascript418// 0.x - Instance creation419const api = axios.create({420 baseURL: 'https://api.example.com',421 timeout: 1000,422});423 424// 1.x - Same API, but more options available425const api = axios.create({426 baseURL: 'https://api.example.com',427 timeout: 1000,428 maxBodyLength: Infinity, // Override default if needed429 maxContentLength: Infinity,430});431```432 433## Migration Strategies434 435### Step-by-Step Migration Process436 437#### Phase 1: Preparation4381. **Audit Current Error Handling**439 ```bash440 # Find all axios usage441 grep -r "axios\." src/442 grep -r "\.catch" src/443 grep -r "interceptors" src/444 ```445 4462. **Identify Patterns**447 - Response interceptors that handle errors448 - Components that rely on centralized error handling449 - Authentication and retry logic450 4513. **Create Test Cases**452 ```javascript453 // Test current error handling behavior454 describe('Error Handling Migration', () => {455 it('should handle 401 errors consistently', async () => {456 // Test authentication error flows457 });458 459 it('should handle 500 errors with user feedback', async () => {460 // Test server error handling461 });462 });463 ```464 465#### Phase 2: Implementation4661. **Update Dependencies**467 ```bash468 npm update axios469 ```470 4712. **Implement New Error Handling**472 - Choose one of the strategies above473 - Update response interceptors474 - Add error handling to API calls475 4763. **Update Authentication Logic**477 ```javascript478 // 0.x pattern479 axios.interceptors.response.use(null, error => {480 if (error.response?.status === 401) {481 logout();482 // Error was "handled"483 }484 });485 486 // 1.x pattern487 axios.interceptors.response.use(488 response => response,489 error => {490 if (error.response?.status === 401) {491 logout();492 }493 return Promise.reject(error); // Always propagate494 }495 );496 ```497 498#### Phase 3: Testing and Validation4991. **Test Error Scenarios**500 - Network failures501 - HTTP error codes (401, 403, 404, 500, etc.)502 - Timeout errors503 - JSON parsing errors504 5052. **Validate User Experience**506 - Error messages are shown appropriately507 - Authentication redirects work508 - Loading states are handled correctly509 510### Gradual Migration Approach511 512For large applications, consider gradual migration:513 514```javascript515// Create a compatibility layer516const axiosCompat = {517 // Use new axios instance for new code518 v1: axios.create({519 // 1.x configuration520 }),521 522 // Wrapper for legacy code523 legacy: createLegacyWrapper(axios.create({524 // Configuration that mimics 0.x behavior525 }))526};527 528function createLegacyWrapper(axiosInstance) {529 // Add interceptors that provide 0.x-like behavior530 axiosInstance.interceptors.response.use(531 response => response,532 error => {533 // Handle errors in 0.x style for legacy code534 handleLegacyError(error);535 // Don't propagate certain errors536 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 Patterns548 549### Authentication Interceptors550 551#### Updated Authentication Pattern552```javascript553// Token refresh interceptor for 1.x554let 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 refresh574 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 Logic605 606```javascript607// Retry interceptor for 1.x608function 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 backoff627 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// Usage636const api = axios.create();637createRetryInterceptor(3, 1000);638 639// Make request with retry640api.get('/api/data', { retry: true });641```642 643### Loading State Management644 645```javascript646// Loading interceptor for 1.x647class 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 UI677 document.body.classList.toggle('loading', isLoading);678 }679}680 681const loadingManager = new LoadingManager();682```683 684## Troubleshooting685 686### Common Migration Issues687 688#### Issue 1: Unhandled Promise Rejections689 690**Problem:**691```javascript692// This pattern worked in 0.x but causes unhandled rejections in 1.x693axios.get('/api/data'); // No .catch() handler694```695 696**Solution:**697```javascript698// Always handle promises699axios.get('/api/data')700 .catch(error => {701 // Handle error appropriately702 console.error('Request failed:', error.message);703 });704 705// Or use async/await with try/catch706async 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" Errors718 719**Problem:**720```javascript721// 0.x style - interceptor "handled" errors722axios.interceptors.response.use(null, error => {723 showErrorMessage(error.message);724 // Error was considered "handled"725});726```727 728**Solution:**729```javascript730// 1.x style - explicitly control error propagation731axios.interceptors.response.use(732 response => response,733 error => {734 showErrorMessage(error.message);735 736 // Choose whether to propagate the error737 if (shouldPropagateError(error)) {738 return Promise.reject(error);739 }740 741 // Return success-like response for "handled" errors742 return Promise.resolve({743 data: null,744 handled: true,745 error: normalizeError(error)746 });747 }748);749```750 751#### Issue 3: JSON Parsing Errors752 753**Problem:**754```javascript755// 1.x is stricter about JSON parsing756// This might throw where 0.x was lenient757const data = response.data;758```759 760**Solution:**761```javascript762// Add response transformer for better error handling763axios.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 gracefully770 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 Upgrade780 781**Problem:**782```typescript783// TypeScript errors after upgrade784const response = await axios.get('/api/data');785// Property 'someProperty' does not exist on type 'any'786```787 788**Solution:**789```typescript790// Define proper interfaces791interface ApiResponse {792 data: any;793 message: string;794 success: boolean;795}796 797const response = await axios.get<ApiResponse>('/api/data');798// Now properly typed799console.log(response.data.data);800```801 802### Debug Migration Issues803 804#### Enable Debug Logging805```javascript806// Add request/response logging807axios.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 Behavior825```javascript826// Create side-by-side comparison during migration827const axios0x = require('axios-0x'); // Keep old version for testing828const 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## Resources846 847### Official Documentation848- [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 Tools853- [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 Resources857- [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 Issues862- [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 guide874 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.*