npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jysperu/helper-fetcher

v1.1.2

Published

Librería moderna TypeScript para peticiones HTTP con autenticación (Bearer/Token), headers personalizados, timeout configurable y detección automática de URL base

Downloads

54

Readme

@jysperu/helper-fetcher

npm version npm downloads License: MIT TypeScript Bundle Size

Librería moderna TypeScript para peticiones HTTP con autenticación (Bearer/Token), headers personalizados, timeout configurable y detección automática de URL base.

🚀 Características

  • 🔧 TypeScript completamente tipado con genéricos
  • 🔑 Autenticación integrada - Bearer Token y headers personalizados
  • API moderna basada en fetch con timeout
  • 🛡️ Manejo de errores robusto con soporte para respuestas no-JSON
  • 🎯 Métodos HTTP completos: GET, POST, PUT, DELETE, PATCH
  • 🆕 Métodos directos - fetcher.get(), fetcher.post(), etc. (v1.0.2)
  • 📤 Upload/Download con progreso - fetcher.upload(), fetcher.download() (v1.1.0)
  • 📦 Mínimas dependencias - Solo form-data para Node.js
  • 🔄 Cache control granular y personalizable
  • 🌐 Detección automática del tag <base> HTML o fallback inteligente
  • Singleton pattern con instancia global
  • 🔍 Respuestas flexibles - Maneja JSON, texto plano y HTML (v1.0.5)
  • 🌍 Multiplataforma - Node.js y navegador con mismo código

📦 Instalación

npm install @jysperu/helper-fetcher

⚡ Inicio Rápido

import { fetcher } from "@jysperu/helper-fetcher";

// Configuración global con autenticación
fetcher.configure({
  base: "https://api.ejemplo.com",
  timeout: 5000,
  bearerToken: "tu-jwt-token", // Automáticamente añade Authorization: Bearer
  headers: {
    "X-Custom-Header": "valor-personalizado",
  },
});

import { api, api_get, api_post } from "@jysperu/helper-fetcher";

// GET request con tipado
interface User {
  id: number;
  name: string;
  email: string;
}

const usuarios = await api_get<User[]>("/usuarios");
if (usuarios.success) {
  console.log("Usuarios:", usuarios.data);
} else {
  console.error("Error:", usuarios.log);
}

// POST request
const nuevoUsuario = await api_post<User>("/usuarios", {
  name: "Juan Pérez",
  email: "[email protected]",
});

// Usando la función principal con cache control
const result = await api<User[]>("/usuarios", {}, false, "GET"); // Sin cache

// 🆕 Nuevos métodos HTTP directos (v1.0.2)
const users = await fetcher.get<User[]>("/usuarios"); // GET
const newUser = await fetcher.post<User>("/usuarios", { name: "Ana" }); // POST
const updated = await fetcher.put<User>("/usuarios/1", { name: "Ana García" }); // PUT
await fetcher.delete("/usuarios/1"); // DELETE
const patched = await fetcher.patch<User>("/usuarios/1", { email: "[email protected]" }); // PATCH

// Método genérico con detección automática
const data = await fetcher.request<User[]>("/usuarios", "GET", { active: true });

📚 API Reference

🏭 Clase Fetcher

La clase principal que maneja todas las peticiones HTTP.

class Fetcher {
  static instance: Fetcher; // Instancia singleton

  constructor(config?: Partial<RequestConfig>);
  configure(config: Partial<RequestConfig>): void;
  buildUrl(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null): string;
  fetch<T>(url: string, options: RequestInit, timeout?: number): Promise<ApiResponse<T>>;
  fetchWithParams<T>(
    endpoint: string,
    method?: string,
    params?: ApiParams,
    cachecode?: string | number | boolean | null,
    timeout?: number
  ): Promise<ApiResponse<T>>;
  fetchWithBody<T>(endpoint: string, method?: string, body?: ApiBody, timeout?: number): Promise<ApiResponse<T>>;

  // Métodos HTTP directos (nuevos en v1.0.2)
  request<T>(
    endpoint: string,
    method?: HttpMethod,
    data?: ApiParams | ApiBody,
    cachecode?: string | number | boolean | null
  ): Promise<ApiResponse<T>>;
  get<T>(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null): Promise<ApiResponse<T>>;
  post<T>(endpoint: string, body?: ApiBody): Promise<ApiResponse<T>>;
  put<T>(endpoint: string, body?: ApiBody): Promise<ApiResponse<T>>;
  delete<T>(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null): Promise<ApiResponse<T>>;
  patch<T>(endpoint: string, body?: ApiBody): Promise<ApiResponse<T>>;
}

🌐 detectBaseUrl

Detecta automáticamente la URL base desde el tag <base> del documento con fallback inteligente.

function detectBaseUrl(): string;

Comportamiento avanzado:

  • ✅ Detecta tag <base href="..."> en el documento
  • ✅ Fallback a window.location.origin
  • ✅ Compatible con SSR (retorna cadena vacía)
  • ✅ Normalización automática (elimina barras finales)
  • ✅ Validación de URLs válidas

⚙️ fetcher.configure

Configura los ajustes globales con soporte para autenticación.

interface RequestConfig {
  timeout?: number;      // Timeout en ms (debe ser entero positivo)
  base?: string;         // URL base para peticiones
  headers?: Record<string, string>; // Headers HTTP por defecto
  bearerToken?: string;  // Token Bearer para Authorization header
  headerToken?: string;  // Token personalizado para header Token
}

fetcher.configure(config: Partial<RequestConfig>): void;

Ejemplos de configuración:

// Autenticación Bearer
fetcher.configure({
  bearerToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  base: "https://api.miapp.com",
});
// Automáticamente añade: Authorization: Bearer <token>

// Headers personalizados
fetcher.configure({
  headers: {
    "X-API-Key": "mi-api-key",
    "Content-Type": "application/json",
  },
  headerToken: "custom-token-value",
});
// Añade: Token: custom-token-value

Validaciones automáticas:

  • ✅ Timeout debe ser entero positivo
  • ✅ Headers debe ser objeto válido
  • ✅ Tokens deben ser strings

🆕 Métodos HTTP Directos (Nuevo en v1.0.2)

La clase Fetcher ahora incluye métodos HTTP directos para un código más limpio y expresivo:

fetcher.get() - Peticiones GET

function fetcher.get<T>(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null, timeout?: number): Promise<ApiResponse<T>>;
// GET básico
const users = await fetcher.get<User[]>("/users");

// GET con parámetros
const activeUsers = await fetcher.get<User[]>("/users", { active: true, limit: 10 });

// GET con cache
const cachedData = await fetcher.get("/config", {}, true);

// GET con timeout personalizado (5 segundos)
const quickData = await fetcher.get<Data>("/quick-data", {}, false, 5000);

fetcher.post() - Peticiones POST

function fetcher.post<T>(endpoint: string, body?: ApiBody, timeout?: number): Promise<ApiResponse<T>>;
// Crear nuevo usuario
const newUser = await fetcher.post<User>("/users", {
  name: "Juan Pérez",
  email: "[email protected]",
});

// POST con timeout personalizado (10 segundos para operaciones pesadas)
const uploadResult = await fetcher.post<Result>("/upload", largeFileData, 10000);

fetcher.put() - Peticiones PUT

function fetcher.put<T>(endpoint: string, body?: ApiBody, timeout?: number): Promise<ApiResponse<T>>;
// Actualizar usuario completo
const updatedUser = await fetcher.put<User>("/users/123", {
  name: "Juan Carlos Pérez",
  email: "[email protected]",
});

// PUT con timeout largo para operaciones de sincronización (30 segundos)
const syncResult = await fetcher.put<SyncStatus>("/sync-data", bulkData, 30000);

fetcher.delete() - Peticiones DELETE

function fetcher.delete<T>(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null, timeout?: number): Promise<ApiResponse<T>>;
// Eliminar usuario por ID
await fetcher.delete("/users/123");

// Eliminar con parámetros
await fetcher.delete("/users", { inactive: true });

// DELETE con timeout personalizado para limpieza de datos (20 segundos)
await fetcher.delete("/cleanup", {}, null, 20000);

fetcher.patch() - Peticiones PATCH

function fetcher.patch<T>(endpoint: string, body?: ApiBody, timeout?: number): Promise<ApiResponse<T>>;
// Actualizar parcialmente
const patched = await fetcher.patch<User>("/users/123", {
  email: "[email protected]",
});

// PATCH con timeout corto para actualizaciones críticas (2 segundos)
const urgentUpdate = await fetcher.patch<Status>("/status", { active: false }, 2000);

fetcher.request() - Método Genérico

function fetcher.request<T>(endpoint: string, method?: HttpMethod, data?: ApiParams | ApiBody, cachecode?: string | number | boolean | null, timeout?: number): Promise<ApiResponse<T>>;
// Detección automática: GET usa parámetros, POST usa cuerpo
const users = await fetcher.request<User[]>("/users", "GET", { active: true });
const newUser = await fetcher.request<User>("/users", "POST", { name: "Ana" });

// Con timeout personalizado según el tipo de operación
const quickSearch = await fetcher.request<SearchResult>("/search", "GET", { q: "test" }, null, 3000);
const dataImport = await fetcher.request<ImportResult>("/import", "POST", csvData, null, 60000);

🌟 Ventajas de los Métodos Directos

  • Más expresivo: fetcher.get() vs fetchWithParams()
  • Menos imports: Todo en la instancia principal
  • Mejor autocompletado: IntelliSense mejorado en IDEs
  • Compatibilidad total: Los métodos anteriores siguen funcionando
  • Timeout granular: Control individual del timeout por petición

⏱️ Control de Timeout Personalizado (v1.0.4)

Todos los métodos HTTP ahora soportan un parámetro timeout opcional para control granular:

// Timeouts según el tipo de operación
const quickData = await fetcher.get("/live-data", {}, false, 2000); // 2s - datos en vivo
const searchResults = await fetcher.get("/search", { q: "query" }, false, 5000); // 5s - búsqueda
const fileUpload = await fetcher.post("/upload", fileData, 30000); // 30s - subida de archivos
const bulkOperation = await fetcher.put("/bulk-update", data, 60000); // 60s - operaciones masivas
const cleanup = await fetcher.delete("/cleanup", {}, null, 45000); // 45s - limpieza de datos
const statusUpdate = await fetcher.patch("/status", { active: true }, 1000); // 1s - actualización crítica

// Si no se especifica timeout, usa la configuración global
const defaultTimeout = await fetcher.get("/data"); // Usa fetcher timeout configurado

Características del timeout:

  • Fallback inteligente: Usa configuración global si no se especifica
  • Validación automática: Debe ser un número entero positivo
  • Control granular: Cada petición puede tener su propio timeout
  • AbortController: Cancelación nativa de peticiones
  • Sin afectar global: No modifica la configuración general

🔄 Migración Fácil

// Antes
const users = await fetchWithParams("/users", "GET", { active: true });
const newUser = await fetchWithBody("/users", "POST", { name: "Juan" });

// Ahora (más limpio)
const users = await fetcher.get("/users", { active: true });
const newUser = await fetcher.post("/users", { name: "Juan" });

📤 Upload y Download con Progreso (v1.1.0)

fetcher.upload() - Subir archivos

function fetcher.upload<T>(
  endpoint: string,
  file: string | File | Blob,
  fileparam?: string,
  onprogress?: (event: ProgressEvent) => void,
  body?: ApiBody,
  timeout?: number,
  method?: string
): Promise<ApiResponse<T>>;

Navegador:

const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const result = await fetcher.upload(
  "/api/upload",
  file,
  "avatar",
  (e) => {
    console.log(`Progreso: ${e.percent.toFixed(2)}%`);
    console.log(`${e.loaded} / ${e.total} bytes`);
  },
  { userId: 123, category: "profile" }
);

Node.js:

const result = await fetcher.upload(
  "/api/upload",
  "./documents/invoice.pdf", // Ruta del archivo
  "document",
  (e) => console.log(`${e.percent.toFixed(2)}%`),
  { documentType: "invoice" }
);

fetcher.download() - Descargar archivos

function fetcher.download<T>(
  endpoint: string,
  filename?: string,
  onprogress?: (event: ProgressEvent) => void,
  params?: ApiParams,
  cachecode?: string | number | boolean | null,
  timeout?: number,
  method?: string
): Promise<ApiResponse<T>>;

Navegador (descarga automática):

const result = await fetcher.download("/api/files/report.pdf", "informe-mensual.pdf", (e) => {
  const percent = e.percent.toFixed(2);
  console.log(`Descargando: ${percent}%`);
});
// El archivo se descarga automáticamente

Node.js (guarda en disco):

const result = await fetcher.download("/api/files/backup.zip", "./downloads/backup.zip", (e) => console.log(`${e.loaded} / ${e.total} bytes`));
// Archivo guardado en ./downloads/backup.zip

appendObjectToForm() - Helper para FormData

import { appendObjectToForm } from "@jysperu/helper-fetcher";

const form = new FormData();
appendObjectToForm(form, {
  name: "Juan Pérez",
  age: 30,
  avatar: fileInput.files[0], // File
  metadata: { role: "admin", active: true }, // Se serializa a JSON
  optional: undefined, // Se omite
});

await fetcher.post("/api/profile", form);

📚 API Reference Clásica

Todos los métodos anteriores siguen siendo completamente compatibles:

🌐 Funciones de Petición

api_get - GET con parámetros

function api_get<T = unknown>(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null): Promise<ApiResponse<T>>;

api_post - POST con cuerpo

function api_post<T = unknown>(endpoint: string, body?: ApiBody): Promise<ApiResponse<T>>;

api_request - Método genérico

function api_request<T = unknown>(
  endpoint: string,
  method?: HttpMethod,
  data?: ApiParams | ApiBody,
  cacheCode?: string | number | boolean | null
): Promise<ApiResponse<T>>;

api - Función principal

function api<T = unknown>(
  endpoint: string,
  bodyOrParams?: ApiParams | ApiBody,
  cacheCode?: string | number | boolean | null,
  method?: HttpMethod
): Promise<ApiResponse<T>>;

🔧 Funciones de utilidad

buildUrl - Constructor de URLs

fetcher.buildUrl(endpoint: string, params?: ApiParams, cachecode?: string | number | boolean | null): string;

Maneja múltiples tipos de endpoints:

// URL completa - se usa directamente
fetcher.buildUrl("https://otra-api.com/datos");

// Ruta absoluta - concatena con base
fetcher.buildUrl("/api/usuarios"); // → https://mibase.com/api/usuarios

// Ruta relativa - agrega / automáticamente
fetcher.buildUrl("usuarios"); // → https://mibase.com/usuarios

fetch - Fetch con timeout

function fetch<T = unknown>(url: string, options: RequestInit, timeout?: number): Promise<ApiResponse<T>>;

processFetchJsonResponse - Normalizador

function processFetchJsonResponse<T = unknown>(json: any): ApiResponse<T>;

Características:

  • ✅ Convierte status: "success"success: true
  • ✅ Normaliza mensajes de error y éxito
  • ✅ Limpia propiedades redundantes
  • ✅ Validación de JSON inválido
  • ✅ Manejo robusto de tipos

📋 Tipos TypeScript

ApiResponse

interface ApiResponse<T = unknown> {
  success: boolean; // true si la operación fue exitosa
  log: string; // Mensaje de log/error
  data?: T; // Datos de respuesta (opcional)
  response?: string; // Texto original si la respuesta no es JSON (v1.0.5)
  [key: string]: unknown;
}

Manejo flexible de respuestas (v1.0.5):

// Respuesta JSON estándar
const result = await api_get<User[]>("/users");
// { success: true, log: "Operación exitosa", data: [...] }

// Respuesta de texto plano
const textResult = await api_get("/health-check");
// { success: true, log: "OK", response: "OK" }

// Error HTTP con JSON
const error = await api_post("/invalid-endpoint", {});
// { success: false, log: "Error message from server", data: {...} }

// Error HTTP con texto plano
const htmlError = await api_get("/not-found");
// { success: false, log: "HTTP 404: Not Found", response: "<!DOCTYPE html>..." }

Características del manejo de respuestas:

  • ✅ Detecta automáticamente JSON vs texto plano
  • ✅ Preserva contenido original en response si no es JSON
  • success siempre refleja response.ok (códigos HTTP 2xx)
  • ✅ Maneja errores con y sin cuerpo JSON
  • ✅ Compatible con APIs que retornan HTML o texto

ApiParams

interface ApiParams {
  [key: string]: string | number | boolean;
}

ApiBody

interface ApiBody {
  [key: string]: unknown;
}

HttpMethod

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

🌐 Detección Automática de Base URL

La librería detecta automáticamente la URL base de tu aplicación:

Tag Base HTML

<!DOCTYPE html>
<html>
  <head>
    <!-- La librería detecta automáticamente este tag -->
    <base href="https://api.miapp.com/v2/" />
  </head>
  <body>
    <!-- Tu aplicación -->
  </body>
</html>
import { detectBaseUrl, api_get } from "@jysperu/helper-fetcher";

// Detecta automáticamente: "https://api.miapp.com/v2"
console.log(detectBaseUrl());

// Todas las peticiones usan esta base automáticamente
const datos = await api_get("/usuarios"); // → https://api.miapp.com/v2/usuarios

Sin Tag Base

Si no hay tag <base>, usa window.location.origin:

// En https://miapp.com/dashboard
console.log(detectBaseUrl()); // "https://miapp.com"

const datos = await api_get("/api/datos"); // → https://miapp.com/api/datos

🌐 Compatibilidad

  • ✅ Todos los navegadores modernos (con soporte para fetch)
  • ✅ Compatible con frameworks (React, Vue, Angular, Svelte)
  • ✅ SSR friendly
  • ✅ TypeScript nativo
  • ✅ ES Modules y CommonJS

🔗 Enlaces y Recursos

📄 Licencia

MIT License - Consulta el archivo LICENSE para detalles completos.

MIT License - Copyright (c) 2025 JYS Perú

Desarrollado con ❤️ por JYS Perú