fix(api): handle 204 No Content в apiFetch (#1219) #1249

Merged
bot-backend merged 1 commit from fix/api-fetch-204-no-content into main 2026-06-13 05:13:44 +00:00
Collaborator

Summary

Closes #1219 (P2 silent prod-bug). apiFetch безусловно звал response.json() на пустом теле 204 → SyntaxError → mutation реджектится → onSuccess/invalidateQueries не срабатывают → POI/профиль виден в UI до случайного рефетча.

Воспроизведение

  • DELETE /api/v1/custom-pois/{id} → backend отдаёт 204 пустой body.
  • Frontend useDeleteCustomPoiapiFetch<unknown>(...)response.json() на ""SyntaxError: Unexpected end of JSON input.
  • useMutation promise rejects → onSuccess не вызывается → queryClient.invalidateQueries(['custom-pois']) не выполняется.
  • POI остаётся на SiteMap (staleTime: 5min, refetchOnWindowFocus: false). 100% repro.

Что в PR

В apiFetch после !response.ok-check добавлена обработка 204 / пустого тела:

if (response.status === 204) return undefined as T;
const rawText = await response.text();
if (!rawText) return undefined as T;
return JSON.parse(rawText) as T;

Семантика выровнена с уже существующим apiFetchWithStatus. Сигнатура Promise<T> сохранена — 80 не-204 callers backward-compatible; DELETE-хуки уже типизированы <unknown>/<void> и игнорируют возврат.

Альтернатива (отклонена)

Перевести useDeleteCustomPoi / useDeleteProfile на apiFetchWithStatus — больше изменений по чужим файлам, без выигрыша. Fix в одном месте лучше.

Test plan

  • npx vitest run src/lib/__tests__/api.test.ts → 6 / 6 green (3 ms). Кейсы: 204 No Content, regression guard, 200+JSON, 200+empty-body edge case, 404, 500.
  • npm run test (full) → 73 / 73 green, никаких регрессий.
  • npx tsc --noEmit → clean.
  • npm run lint → no warnings/errors.

Closes #1219

## Summary Closes #1219 (P2 silent prod-bug). `apiFetch` безусловно звал `response.json()` на пустом теле 204 → SyntaxError → mutation реджектится → `onSuccess`/`invalidateQueries` не срабатывают → POI/профиль виден в UI до случайного рефетча. ## Воспроизведение - `DELETE /api/v1/custom-pois/{id}` → backend отдаёт 204 пустой body. - Frontend `useDeleteCustomPoi` → `apiFetch<unknown>(...)` → `response.json()` на `""` → `SyntaxError: Unexpected end of JSON input`. - `useMutation` promise rejects → `onSuccess` не вызывается → `queryClient.invalidateQueries(['custom-pois'])` не выполняется. - POI остаётся на SiteMap (`staleTime: 5min`, `refetchOnWindowFocus: false`). 100% repro. ## Что в PR В `apiFetch` после `!response.ok`-check добавлена обработка 204 / пустого тела: ```ts if (response.status === 204) return undefined as T; const rawText = await response.text(); if (!rawText) return undefined as T; return JSON.parse(rawText) as T; ``` Семантика выровнена с уже существующим `apiFetchWithStatus`. Сигнатура `Promise<T>` сохранена — 80 не-204 callers backward-compatible; DELETE-хуки уже типизированы `<unknown>/<void>` и игнорируют возврат. ## Альтернатива (отклонена) Перевести `useDeleteCustomPoi` / `useDeleteProfile` на `apiFetchWithStatus` — больше изменений по чужим файлам, без выигрыша. Fix в одном месте лучше. ## Test plan - [x] `npx vitest run src/lib/__tests__/api.test.ts` → 6 / 6 green (3 ms). Кейсы: 204 No Content, regression guard, 200+JSON, 200+empty-body edge case, 404, 500. - [x] `npm run test` (full) → **73 / 73 green**, никаких регрессий. - [x] `npx tsc --noEmit` → clean. - [x] `npm run lint` → no warnings/errors. Closes #1219
bot-backend added 1 commit 2026-06-13 05:04:35 +00:00
fix(api): handle 204 No Content в apiFetch (#1219)
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (push) Successful in 48s
CI / frontend-tests (pull_request) Successful in 49s
CI / changes (push) Successful in 6s
CI / backend-tests (push) Has been skipped
8f84776969
apiFetch безусловно звал response.json() после !response.ok-check, что
падало SyntaxError'ом на пустом теле 204. DELETE /custom-pois/{id} и
DELETE /admin/site-finder/weight-profiles/{id} объявлены status_code=204
→ mutation promise реджектился → onSuccess/invalidateQueries не срабатывали
→ POI/профиль виден в UI до случайного рефетча, хотя реально удалён на
сервере. Silent fail для DELETE кастомных POI на SiteMap (#1219, P2).

Fix: на 204 или пустом теле resolve'имся с undefined (аналог семантики
apiFetchWithStatus). Сигнатура Promise<T> сохранена — 80 не-204 callers
backward-compatible; два DELETE-хука уже типизированы <unknown>/<void>
и корректно игнорируют возврат.

6 новых vitest кейсов (api.test.ts): 204, empty-body-200, 200+JSON, 404,
500, regression guard. 73/73 frontend тестов зелёные, lint+tsc clean.

Closes #1219
bot-backend merged commit 9a2cf32787 into main 2026-06-13 05:13:44 +00:00
bot-backend deleted branch fix/api-fetch-204-no-content 2026-06-13 05:13:45 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1249
No description provided.