--- name: fetch_json kind: function lang: ts domain: infra version: "1.0.0" purity: impure signature: "async function fetchJSON(path: string, init?: RequestInit, baseUrl?: string): Promise" description: "Wrapper de fetch que parsea JSON, lanza HTTPError en errores HTTP y retorna undefined en 204. Exporta la clase HTTPError con el status code de la respuesta fallida." tags: [http, fetch, json, rest, api, error, httperror, infra] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [] params: - name: path desc: "Path relativo a baseUrl (ej: /api/users). Se concatena directamente: ${baseUrl ?? ''}${path}" - name: init desc: "RequestInit de fetch (method, body, headers, etc.). headers se mergea con el default Content-Type: application/json. credentials se puede sobreescribir (default: include)" - name: baseUrl desc: "URL base sin slash final (ej: https://api.example.com). Opcional, default string vacio" output: "Promise con el JSON parseado. Lanza HTTPError si !res.ok. Retorna undefined as T si status 204." tested: false tests: [] test_file_path: "" file_path: "frontend/functions/infra/fetch_json.ts" --- ## Ejemplo ```typescript import { fetchJSON, HTTPError } from "@fn_library/../infra/fetch_json"; // GET con base URL const users = await fetchJSON("/users", undefined, "https://api.example.com"); // POST con body const card = await fetchJSON( "/cards", { method: "POST", body: JSON.stringify({ title: "Nueva tarea" }) }, "/api", ); // Capturar HTTPError try { await fetchJSON("/protected"); } catch (err) { if (err instanceof HTTPError) { console.error(`HTTP ${err.status}: ${err.message}`); } } ``` ## Comportamiento detallado - **URL**: `${baseUrl ?? ""}${path}` — si baseUrl es undefined o vacío, path se usa tal cual. - **Headers**: `{ "Content-Type": "application/json" }` como base, mergeados con `init?.headers`. Los headers de init tienen precedencia. - **credentials**: `"include"` por defecto (envía cookies). Se puede sobreescribir pasando `credentials: "omit"` en init. - **Error HTTP** (`!res.ok`): intenta `res.json()` para leer el cuerpo del error. Si el parse falla (body vacío, no-JSON), usa `{ Message: res.statusText }`. Construye `HTTPError(status, err.Message ?? err.message ?? res.statusText)`. - **204 No Content**: retorna `undefined as T` sin intentar parsear el body (que está vacío). - **Errores de red**: no se capturan — el rechazo de la Promise nativa de `fetch` se propaga tal cual. ## Notas Extraído de `apps/kanban/frontend/src/api.ts` (líneas 14–32) y generalizado añadiendo el parámetro `baseUrl` opcional para reutilización fuera del contexto kanban. `HTTPError` se exporta para que el consumidor pueda hacer `instanceof HTTPError` en sus catch.