Refactor API hooks to remove infinite query support and update API client base URL
- Removed infinite query options and related functions from Trading212, User Registration, and User API files. - Updated API client base URLs in privateClient.ts and publicClient.ts to use environment variable for backend URL. - Refactored Downloader component to directly call API functions for video info retrieval instead of using a hook.
This commit is contained in:
108
.github/copilot-instructions.md
vendored
108
.github/copilot-instructions.md
vendored
@@ -107,12 +107,108 @@ Notes
|
||||
- **Task queue**: Celery + Redis for async/background jobs.
|
||||
- **API**: REST endpoints, JWT auth, API key support.
|
||||
|
||||
### OpenAPI Client Generation
|
||||
- Schema: `config = { schemaUrl: "/api/schema/", baseUrl: "/api/" }`
|
||||
- Commands: `npm run api:update` (fetch schema + generate client)
|
||||
- Output: `frontend/src/api/generated/` (TypeScript Axios client)
|
||||
- Axios instance: `frontend/src/api/api.ts` with `withCredentials` and JWT auto-refresh via existing `Client.ts`.
|
||||
- Choices helper: `frontend/src/api/get_choices.ts` → `getChoices(requests, lang)` returns `{ "Model.field": [{ value, label }] }`.
|
||||
### OpenAPI Client Generation (Orval)
|
||||
This project uses **Orval** to auto-generate TypeScript API clients from the Django OpenAPI schema.
|
||||
|
||||
#### Configuration
|
||||
- **Orval config**: `frontend/src/orval.config.ts`
|
||||
- **Schema URL**: `/api/schema/` (DRF Spectacular endpoint)
|
||||
- **Fetch script**: `frontend/scripts/fetch-openapi.js`
|
||||
- **Commands**:
|
||||
- `npm run api:update` — fetches schema + generates client
|
||||
- Runs: `node scripts/fetch-openapi.js && npx orval`
|
||||
|
||||
#### Generated Output
|
||||
- **Location**: `frontend/src/api/generated/`
|
||||
- **Files**: TypeScript interfaces, Axios-based API hooks
|
||||
- Uses custom mutators: `publicMutator` and `privateMutator`
|
||||
|
||||
#### Custom Mutators
|
||||
Two Axios clients handle public/private API requests:
|
||||
|
||||
**Public Client** (`frontend/src/api/publicClient.ts`):
|
||||
```ts
|
||||
import axios, { type AxiosRequestConfig } from "axios";
|
||||
|
||||
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
|
||||
|
||||
export const publicApi = axios.create({
|
||||
baseURL: backendUrl + "/api/",
|
||||
withCredentials: false, // no cookies for public endpoints
|
||||
});
|
||||
|
||||
export const publicMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
|
||||
const response = await publicApi.request<T>(config);
|
||||
return response.data;
|
||||
};
|
||||
```
|
||||
|
||||
**Private Client** (`frontend/src/api/privateClient.ts`):
|
||||
```ts
|
||||
import axios, { type AxiosRequestConfig } from "axios";
|
||||
|
||||
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
|
||||
|
||||
export const privateApi = axios.create({
|
||||
baseURL: backendUrl + "/api/",
|
||||
withCredentials: true, // sends HttpOnly cookies (access/refresh tokens)
|
||||
});
|
||||
|
||||
// Auto-refresh on 401
|
||||
privateApi.interceptors.response.use(
|
||||
(res) => res,
|
||||
async (error) => {
|
||||
const original = error.config;
|
||||
if (error.response?.status === 401 && !original._retry) {
|
||||
original._retry = true;
|
||||
try {
|
||||
await privateApi.post("/auth/refresh/");
|
||||
return privateApi(original);
|
||||
} catch {
|
||||
// optional: logout
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export const privateMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
|
||||
const response = await privateApi.request<T>(config);
|
||||
return response.data;
|
||||
};
|
||||
```
|
||||
|
||||
#### Environment Variables (Vite)
|
||||
- **IMPORTANT**: Use `import.meta.env.VITE_*` instead of `process.env` in browser code
|
||||
- **NEVER** import `dotenv/config` in frontend files (causes "process is not defined" error)
|
||||
- **Available vars**:
|
||||
- `VITE_BACKEND_URL` (default: `http://localhost:8000`)
|
||||
- `VITE_API_BASE_URL` (if using Client.ts wrapper)
|
||||
- `VITE_API_REFRESH_URL` (default: `/api/token/refresh/`)
|
||||
- `VITE_LOGIN_PATH` (default: `/login`)
|
||||
|
||||
#### Usage Example
|
||||
```ts
|
||||
import { useGetOrders } from "@/api/generated/orders";
|
||||
|
||||
function OrdersList() {
|
||||
const { data, isLoading, error } = useGetOrders();
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{data?.map(order => <li key={order.id}>{order.status}</li>)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Helpers
|
||||
- **Choices helper**: `frontend/src/api/get_choices.ts`
|
||||
- Function: `getChoices(requests, lang)`
|
||||
- Returns: `{ "Model.field": [{ value, label }] }`
|
||||
|
||||
## References
|
||||
- [frontend/REACT.md](../frontend/REACT.md): Frontend structure, workflows, and conventions.
|
||||
|
||||
Reference in New Issue
Block a user