Add AbortSignal support to API functions for request cancellation
Add optional signal?: AbortSignal parameter to all API GET functions so they can be cancelled when components unmount. This prevents state-update warnings and wasted resources. Changes: - frontend/src/api/history.ts: fetchHistory, fetchIpHistory - frontend/src/api/map.ts: fetchBansByCountry - frontend/src/api/jails.ts: fetchJails, fetchActiveBans - frontend/src/api/config.ts: fetchJailConfig, fetchInactiveJails, fetchJailConfigFiles, fetchFilterFiles (threads signal through fetchFilters), fetchFilterFile, fetchActionFiles, fetchActionFile - frontend/src/api/blocklist.ts: fetchImportLog, previewBlocklist Updated all calling hooks to pass the abort signal from their controllers: - useHistory, useIpHistory - useMapData - useActiveBans - useJails - useConfigActiveStatus (fetchJails and fetchJailConfigs) - useJailAdmin (fetchInactiveJails) - useJailConfigDetail (fetchJailConfig) - useImportLog (fetchImportLog) - useBlocklists (previewBlocklist with AbortController) Updated Docs/Web-Development.md to document the convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -76,14 +76,16 @@ import type { Ban } from "../types/ban";
|
||||
- Use a **central API client** (e.g., a thin wrapper around `fetch` or `axios`) that returns typed data — individual components never call `fetch` directly.
|
||||
- Validate or assert the response structure at the boundary when dealing with untrusted data; for critical flows, consider a runtime validation library (e.g., `zod`).
|
||||
- API endpoint paths are **constants** defined in a single file (`api/endpoints.ts`) — never hard-code URLs in components.
|
||||
- **All API functions that perform a `GET` request must accept an optional `signal?: AbortSignal` parameter and forward it to the HTTP client.** This enables hooks to cancel in-flight requests when components unmount, preventing silent state-update errors and wasted resources. When an API function calls another internal API function, thread the signal through to the underlying call.
|
||||
|
||||
```ts
|
||||
// api/client.ts
|
||||
const BASE_URL = import.meta.env.VITE_API_URL ?? "/api";
|
||||
|
||||
async function get<T>(path: string): Promise<T> {
|
||||
async function get<T>(path: string, signal?: AbortSignal): Promise<T> {
|
||||
const response: Response = await fetch(`${BASE_URL}${path}`, {
|
||||
credentials: "include",
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new ApiError(response.status, await response.text());
|
||||
@@ -99,11 +101,19 @@ export const api = { get, post, put, del } as const;
|
||||
import type { BanListResponse } from "../types/ban";
|
||||
import { api } from "./client";
|
||||
|
||||
export async function fetchBans(hours: number): Promise<BanListResponse> {
|
||||
return api.get<BanListResponse>(`/bans?hours=${hours}`);
|
||||
export async function fetchBans(hours: number, signal?: AbortSignal): Promise<BanListResponse> {
|
||||
return api.get<BanListResponse>(`/bans?hours=${hours}`, signal);
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// hooks/useBans.ts
|
||||
const ctrl = new AbortController();
|
||||
fetchBans(24, ctrl.signal) // Pass the signal to enable cancellation on unmount
|
||||
.then(resp => { /* ... */ })
|
||||
.catch(err => { /* ... */ });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Code Organization
|
||||
|
||||
Reference in New Issue
Block a user