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