feat: Migrate ApiRequestService to React with Axios
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
		
							parent
							
								
									00e9026ee1
								
							
						
					
					
						commit
						58335b4a43
					
				
							
								
								
									
										31
									
								
								logs/phase2_api_service_migration.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								logs/phase2_api_service_migration.log
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| # Log: API Service Migration | ||||
| 
 | ||||
| **Task**: Migrate Angular's `ApiRequestService` to a reusable React service using `axios`. | ||||
| 
 | ||||
| **Analysis of `api-request.service.ts`**: | ||||
| - Uses `appConfig` to get `baseApiPath`. This path includes the protocol, host, and a trailing slash (e.g., `http://localhost:3000/`). | ||||
| - Handles setting `Content-Type: application/json` headers. | ||||
| - Has commented-out logic to add an `Authorization` token from `userInfoService`. | ||||
| - Provides wrappers for GET, POST, PUT, DELETE methods. | ||||
| - Includes a special method `postFormData` for `multipart/form-data` uploads. | ||||
| - Basic error handling in `handleError`. | ||||
| 
 | ||||
| **Analysis of `helper.ts` and other services**: | ||||
| - Some services (`forgotpass.service`, `user-info.service`) import a `baseUrl` from `helper.ts`. | ||||
| - `helper.ts` gets its value from `environment.backendUrl`. | ||||
| - The provided `environment.ts` lacks `backendUrl`, but has `apiUrl: 'http://localhost:3000'`. | ||||
| - **Assumption**: `backendUrl` and `apiUrl` should be treated as the same value. | ||||
| - Services using `baseUrl` manually add a `/` separator, e.g., `${baseUrl}/api/...`. This is consistent with `baseApiPath` from `appConfig` which already includes the trailing slash. | ||||
| 
 | ||||
| **Migration Plan**: | ||||
| 1.  Create `migration2react/src/services/apiService.ts`. | ||||
| 2.  Use `axios.create()` to set up a default instance with `baseURL` derived from our new `apiConfig.ts`. | ||||
| 3.  Implement an `axios` request interceptor to attach the Authorization token. This will replicate the logic from `userInfoService` and `apiRequestService` to get the token from `sessionStorage`. | ||||
| 4.  Implement an `axios` response interceptor for global error handling, improving upon the `handleError` method. | ||||
| 5.  Export the configured `axios` instance as the default module export. | ||||
| 6.  Create and export a separate `postFormData` function for handling file uploads, as its headers and configuration differ. | ||||
| 
 | ||||
| **Result**: | ||||
| - A single, configured `axios` instance (`apiService`) that can be imported and used for all standard API calls. | ||||
| - A separate utility function (`postFormData`) for specific multipart requests. | ||||
| - This centralizes API configuration and handling in a standard React pattern. | ||||
							
								
								
									
										85
									
								
								migration2react/src/services/apiService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								migration2react/src/services/apiService.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| import axios, { AxiosRequestConfig } from 'axios'; | ||||
| import { BASE_API_PATH } from '../config/apiConfig'; | ||||
| 
 | ||||
| const apiService = axios.create({ | ||||
|     baseURL: BASE_API_PATH, | ||||
|     headers: { | ||||
|         'Accept': 'application/json', | ||||
|         'Content-Type': 'application/json', | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
| // Request interceptor to add the auth token to headers
 | ||||
| // This mirrors the logic in `UserInfoService.getStoredToken` and `ApiRequestService.getHeaders`
 | ||||
| apiService.interceptors.request.use((config) => { | ||||
|     // In Angular, user info is stored in sessionStorage under "currentUser"
 | ||||
|     const currentUserStr = sessionStorage.getItem('currentUser'); | ||||
|     if (currentUserStr) { | ||||
|         const currentUser = JSON.parse(currentUserStr); | ||||
|         if (currentUser && currentUser.token) { | ||||
|             if (config.headers) { | ||||
|                 // The Angular service just adds the token, not "Bearer". We will follow that.
 | ||||
|                 config.headers['Authorization'] = currentUser.token; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return config; | ||||
| }, (error) => { | ||||
|     return Promise.reject(error); | ||||
| }); | ||||
| 
 | ||||
| // Response interceptor for global error handling
 | ||||
| // This is a more robust version of `ApiRequestService.handleError`
 | ||||
| apiService.interceptors.response.use( | ||||
|     (response) => response, | ||||
|     (error) => { | ||||
|         if (error.response) { | ||||
|             console.error("API Error Response:", error.response); | ||||
|             const { status } = error.response; | ||||
|             let errorMessage = 'Server Error'; | ||||
| 
 | ||||
|             switch (status) { | ||||
|                 case 401: | ||||
|                     errorMessage = 'Forbidden. Please check your credentials or login again.'; | ||||
|                     // In a real app, you'd likely redirect to the login page here.
 | ||||
|                     // e.g., window.location.href = '/login';
 | ||||
|                     break; | ||||
|                 case 404: | ||||
|                     errorMessage = 'The requested resource was not found.'; | ||||
|                     break; | ||||
|                 case 408: | ||||
|                     errorMessage = 'The request timed out.'; | ||||
|                     break; | ||||
|                 case 500: | ||||
|                     errorMessage = 'An internal server error occurred.'; | ||||
|                     break; | ||||
|                 default: | ||||
|                     errorMessage = error.response.data?.message || 'An unexpected error occurred.'; | ||||
|             } | ||||
|             return Promise.reject(new Error(errorMessage)); | ||||
|         } else if (error.request) { | ||||
|             console.error("API Error Request (no response received):", error.request); | ||||
|             return Promise.reject(new Error('Network error. Please check your connection and try again.')); | ||||
|         } else { | ||||
|             console.error('API Setup Error:', error.message); | ||||
|             return Promise.reject(new Error(error.message)); | ||||
|         } | ||||
|     } | ||||
| ); | ||||
| 
 | ||||
| export default apiService; | ||||
| 
 | ||||
| /** | ||||
|  * A dedicated function for POSTing multipart/form-data. | ||||
|  * This is based on `ApiRequestService.postFormData`. | ||||
|  * @param url The endpoint URL (relative to base path). | ||||
|  * @param formData The FormData object to send. | ||||
|  * @returns A promise with the axios response. | ||||
|  */ | ||||
| export const postFormData = (url: string, formData: FormData) => { | ||||
|     return apiService.post(url, formData, { | ||||
|         headers: { | ||||
|             'Content-Type': 'multipart/form-data', | ||||
|         }, | ||||
|     }); | ||||
| }; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Your Name
						Your Name