03568c88e3
- frontend/functions/core/format_datetime_short.md - frontend/functions/core/format_datetime_short.test.ts - frontend/functions/core/format_datetime_short.ts - frontend/functions/core/format_duration.md - frontend/functions/core/format_duration.test.ts - frontend/functions/core/format_duration.ts - frontend/functions/core/month_grid.md - frontend/functions/core/month_grid.test.ts - frontend/functions/core/month_grid.ts - frontend/functions/core/string_hash_palette.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
2.8 KiB
Markdown
70 lines
2.8 KiB
Markdown
---
|
||
name: fetch_json
|
||
kind: function
|
||
lang: ts
|
||
domain: infra
|
||
version: "1.0.0"
|
||
purity: impure
|
||
signature: "async function fetchJSON<T>(path: string, init?: RequestInit, baseUrl?: string): Promise<T>"
|
||
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<T> 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<User[]>("/users", undefined, "https://api.example.com");
|
||
|
||
// POST con body
|
||
const card = await fetchJSON<Card>(
|
||
"/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.
|