Ejemplos con JavaScript / TypeScript

Los snippets usan fetch (disponible en Node 18+, Bun, Deno y todos los navegadores). Si prefieres axios o ky, la lógica es la misma.

Cliente mínimo

const BASE = 'http://localhost/api/v1';
 
async function login(usuario: string, password: string) {
  const res = await fetch(`${BASE}/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ usuario, password }),
  });
  if (!res.ok) throw new Error(`Login falló: ${res.status}`);
  const json = await res.json();
  return json.data.token as string;
}
 
async function get<T>(path: string, token: string): Promise<T> {
  const res = await fetch(`${BASE}${path}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const json = await res.json();
  if (json.errors) throw new Error(json.errors[0].message);
  return json.data;
}
 
const token = await login('cb', '1234');
const monedas = await get<Array<{ id: number; nombre: string }>>('/Moneda', token);
console.log(monedas);

Manejo de errores tipado

type ApiError = { code: string; message: string; field?: string | null };
type ApiResponse<T> = { data: T | null; meta: Record<string, unknown>; errors: ApiError[] | null };
 
async function call<T>(path: string, init: RequestInit, token?: string): Promise<T> {
  const headers: HeadersInit = {
    'Content-Type': 'application/json',
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
    ...init.headers,
  };
  const res = await fetch(`${BASE}${path}`, { ...init, headers });
  const json = (await res.json()) as ApiResponse<T>;
  if (json.errors && json.errors.length > 0) {
    const first = json.errors[0];
    const err = new Error(first.message);
    (err as Error & { code?: string }).code = first.code;
    throw err;
  }
  return json.data as T;
}

Iterar todas las páginas

async function listAll<T>(modelo: string, token: string): Promise<T[]> {
  const all: T[] = [];
  let page = 1;
  while (true) {
    const res = await fetch(`${BASE}/${modelo}?page=${page}&per_page=100`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    const json = await res.json();
    all.push(...json.data);
    if (page >= json.meta.pages) break;
    page++;
  }
  return all;
}
 
const compras = await listAll<{ id: number; total: number }>('Compra', token);
console.log(`Total compras: ${compras.length}`);

Business Action — autorizar batch

async function autorizarCompras(ids: number[], token: string) {
  const res = await fetch(`${BASE}/Compra/actions/autorizar_compra`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body: JSON.stringify({ ids }),
  });
  const json = await res.json();
  const failures = json.data.results.filter((r: { ok: boolean }) => !r.ok);
  return { succeeded: json.meta.succeeded, failed: json.meta.failed, failures };
}
 
const result = await autorizarCompras([12345, 12346, 12347], token);
if (result.failed > 0) {
  console.warn(`${result.failed} fallaron:`, result.failures);
}