chore: auto-commit (57 archivos)
- 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>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user