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

typed-axios-manager

v1.0.4

Published

Type-safe axios manager with autocomplete and path binding support

Readme

Typed Axios Manager

Un gestor de axios fuertemente tipado con autocompletado inteligente, soporte para rutas dinámicas y parámetros de consulta opcionales.

Comentario del Creador: Por que cree este typed manager? Cuando inicie el camino en la programacion, me incertaron en la mente que hay que hacer las cosas de la manera más eficiente posible. Siempre buscar optimizar el codigo,funciones pero tambien nuestra forma de codear. Hoy con la IA podemos ser muy eficientes, pero no hay que dejar de crear las herramientas que mantengan organizado nuestros proyecto y mejorar nuestra experiencia de desarrollo.

Arranque creando archivos para todos los path. Rapidamente se convirtio en un sin fin de constantes.

Un claro ejemplo que seguramente todos recordaremos:

const PATH_CREATE_USER = "/api/user/"
const PATH_GET_USER = (id: number)=>`/api/user/${id}`
const PATH_UPDATE_USER = (id: number)=> `/api/user/${id}`
...

Y mientras buscaba proyectos mas grandes, surgio la necesidad de organizar esto diferente Probe con una carpeta API y archivos x endpoint

api/
├── auth/
│   ├── login.ts
│   ├── register.ts
│   ├── me.ts
├── users/
│   ├── getAll.ts
│   ├── getById.ts
│   ├── create.ts
│   ├── update.ts
│   ├── delete.ts
│   ├── search.ts
├── products/
│   ├── getAll.ts
│   ├── getById.ts
│   ├── search.ts

Pero no resolvia el problema de las constantes, y los archivos crecian sin parar. Sin contar que cuando empezas, tenes poca organizacion. Cambias objetos, se te rompen request y despues anda a acordarte donde esta y que cambio. Cosas como tener que cambiar el path en MIL lugares, cuando deberia cambiarlo en 1.

Despues conoci React - con sus miles de componentes (los que yo creaba) + Axios Y facilmente empece a crear axios.get("y un path") en un componente, y luego usar en otro, y asi sucesivamente.

Que archivo use? En que componente esta? en un child-component del form? Organizarme es fundamental. Los proyectos grandes requieren que seamos desarrolladores organizados.

En mi experiencia pase por Redux, Zustand, librerias que me gustan mucho por la organizacion Aunque el que tiene que mejorar en organizacion soy yo, me inspire en la forma en la que ellos tienen sus generadores.

Dije: esto, para un axios, estaria barbaro.

Empece a probar, genere varias versiones, y ahora me siento lo suficientemente confiado para mostrar este invento.

Lo tengo funcionando en 2 proyectos. Esta es la primera version que subo porque quiero que sea una dependencia en y no un conjunto de archivos dentro de /helper.

La idea es tener mas organizado los proyectos y la conexion con el backend, en un solo archivo Una unica fuente de la verdad Menos erroes, mas proyectos, mas rapido, mas $$$$

Si a alguien le sirve, encantado de saberlo. Happy Coding!

🚀 Características

  • TypeScript First: Tipado fuerte con autocompletado inteligente
  • Fluent API: Definición de rutas clara y escalable que permite inferencia correcta de tipos
  • Query Params Opcionales: No más objetos vacíos obligatorios
  • Path Bindings: Soporte para rutas dinámicas con {param}
  • Framework Agnostic: Funciona con React, Vue, Node.js, Next.js, Astro
  • Sin Dependencias de Estado: No requiere Redux ni ningún estado específico
  • Testing Ready: Tests incluidos con Vitest
  • ESM + CommonJS: Compatible con ambos sistemas de módulos
  • Hooks de React Opcionales: Usa los hooks si quieres, no es obligatorio

📦 Instalación

npm install typed-axios-manager axios

🔧 Uso Básico

1. Define tus rutas

import { createRouteConfig, get, post, put, del } from 'typed-axios-manager';

type User = { name: string; email: string };
type Product = { name: string; price: number };
type ProductSearchRequest = { name: string; limit?: number; page: number };

const apiRoutes = createRouteConfig({
  auth: {
    login: post('/auth/login').body<{ email: string; password: string }>().response<{ token: string }>(),
    register: post('/auth/register').body<{ email: string; password: string }>(),
    me: get('/auth/me').response<User>(),
  },
  users: {
    getAll: get('/users').response<User[]>(),
    getById: get('/users/{id}').response<User>(), // Ruta dinámica con inferencia de {id}
    create: post('/users').body<{ name: string; email: string }>().response<User>(),
    update: put('/users/{id}').body<Partial<User>>(),
    delete: del('/users/{id}'),
    search: get('/users/search').query<{ q: string; limit?: number }>().response<User[]>(), // Con query params
  },
  products: {
    getAll: get('/products').response<Product[]>(),
    getById: get('/products/{id}').response<Product>(),
    search: get('/products/search').query<ProductSearchRequest>().response<Product[]>(), // query params opcionales
  },
});

type ApiRoutes = typeof apiRoutes;

2. Crea el manager

const manager = createAxiosManager<ApiRoutes>({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'X-API-Key': 'your-api-key'
  }
});

// Crea las funciones tipadas
const api = manager.createTypedRoutes(apiRoutes);

3. Usa las funciones

// Query params realmente opcionales
const users = await api.users.getAll(); // No requiere argumentos

//  Con query params cuando los necesites
const products = await api.products.search({ 
  q: 'laptop', 
  limit: 10,
  sort: 'price_asc' 
});

//  Path bindings funcionan perfectamente
const user = await api.users.getById({ id: 123 });

//  Body params bien tipados
const newUser = await api.auth.login({
  email: '[email protected]',
  password: 'password123'
});

🧩 Ejemplos de Query Params

Declarar rutas con tipos de query

import { createRouteConfig, get, post, patch } from 'typed-axios-manager';

const routes = createRouteConfig({
  users: {
    search: get('/users/search').query<{ q: string; limit?: number }>().response<unknown>(),
    getById: get('/users/{id}').query<{ include?: 'profile' | 'roles' }>().response<unknown>(),
    create: post('/users').body<{ name: string; email: string }>().query<{ sendWelcomeEmail?: boolean }>().response<unknown>(),
    partialUpdate: patch('/users/{id}').body<{ name?: string; email?: string }>().query<{ notify?: boolean }>().response<unknown>(),
  },
});

Llamadas con y sin query

// GET sin query
await api.users.search();

// GET con query tipada
await api.users.search({ q: 'neo', limit: 10 });

// GET con bindings y query
await api.users.getById({ include: 'profile' }, { id: 1 });

// POST con body, sin query
await api.users.create({ name: 'John', email: '[email protected]' });

// POST con body y query
await api.users.create(
  { name: 'John', email: '[email protected]' },
  { sendWelcomeEmail: true }
);

// PATCH con body y bindings
await api.users.partialUpdate({ name: 'John' }, { id: 1 });

// PATCH con body, query y bindings
await api.users.partialUpdate(
  { name: 'John' },
  { notify: true },
  { id: 1 }
);

💡 Evolución y Fluent API

Originalmente, esta librería usaba genéricos directos como get<Response>('/path'). Sin embargo, esto causaba un problema en TypeScript: al especificar un tipo genérico (Response), TypeScript dejaba de inferir automáticamente el literal del path string.

El problema anterior:

// TypeScript infiere Path como 'string' genérico, perdiendo los params {id}
get<Product>('/products/{id}') 
// Resultado: api.products.getById() <- No pide argumentos

La solución Fluent API:

// 1. Infiere el path primero ('/products/{id}')
// 2. Añade tipos incrementalmente
get('/products/{id}').response<Product>()
// Resultado: api.products.getById({ id: 1 }) <- Correctamente tipado

Esta nueva sintaxis permite mantener la inferencia precisa de los parámetros de ruta mientras se definen fuertemente los tipos de respuesta, body y query params.

📋 Orden de argumentos y bindings

Los helpers aceptan argumentos en un orden consistente que determina cómo se construye la request de axios:

// GET / DELETE
// Sin bindings: (query?)
await api.users.getAll();
await api.users.search({ q: 'neo', limit: 10 });

// Con bindings: (bindings) o (query, bindings)
await api.users.getById({ id: 1 });
await api.users.getById({ include: 'profile' }, { id: 1 });

// POST / PUT / PATCH
// Sin bindings: (body, query?)
await api.users.create({ name: 'John', email: '[email protected]' });
await api.users.create({ name: 'John', email: '[email protected]' }, { sendWelcomeEmail: true });

// Con bindings: (body, bindings) o (body, query, bindings)
await api.users.update({ name: 'Jane' }, { id: 3 });
await api.users.partialUpdate({ name: 'Neo' }, { notify: true }, { id: 99 });
  • bindings reemplaza variables de ruta {id} en el url (ver src/core/AxiosManager.ts:113-121).
  • query se envía como axios params (ver GET/DELETE en src/core/AxiosManager.ts:75-86).
  • body se envía como axios data (ver POST/PUT/PATCH en src/core/AxiosManager.ts:101-106).

Esto permite que las firmas sean limpias y que el path se infiera directamente del argumento de la función, evitando repetirlo en los genéricos.

📘 Uso de TypeScript

Tipar rutas y obtener funciones tipadas

const routes = createRouteConfig({
  products: {
    // Definir respuesta en la configuración (Recomendado)
    getAll: get('/products').response<Product[]>(),
    // Definir query params
    search: get('/products/search').query<{ q: string; limit?: number }>().response<Product[]>(),
  },
});

type Routes = typeof routes;
const manager = createAxiosManager<Routes>({ baseURL: 'https://api.example.com' });
const api = manager.createTypedRoutes(routes);

Tipar la respuesta al llamar (Override)

Si no defines el tipo de respuesta en la configuración, o quieres sobrescribirlo:

type Product = { id: number; name: string };

// Si la ruta se definió como: get('/products') -> response es unknown
const products = await api.products.getAll<Product[]>();
// products: Product[]

// También funciona para sobrescribir el tipo definido
const products = await api.products.getAll<CustomProductType[]>();

Tipos auxiliares disponibles

import type { QueryParams, PathBindings, ResponseWrapper } from 'typed-axios-manager';

const qp: QueryParams = { q: 'neo', limit: 10 };
const pb: PathBindings = { id: 1 };
const res: ResponseWrapper<{ ok: boolean }> = { code: 'SUCCESS', httpStatus: 200, message: 'OK', data: { ok: true } };

🎯 Uso con React (Opcional)

Con Hooks

import { useApiCall, useAuth } from 'typed-axios-manager';

function UserProfile({ userId }: { userId: number }) {
  const { execute, loading, error, data: user } = useApiCall(manager);

  useEffect(() => {
    execute(() => api.users.getById({ id: userId }));
  }, [userId, execute]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return <div>No user found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Con Context Provider

import { createAxiosContext } from 'typed-axios-manager';

const { Provider: AxiosProvider, useApi, useManager } = createAxiosContext<ApiRoutes>();

function App() {
  return (
    <AxiosProvider manager={manager}>
      <YourComponents />
    </AxiosProvider>
  );
}

function YourComponent() {
  const api = useApi();
  const manager = useManager();
  
  // Usa api.auth.login(), api.users.getAll(), etc.
}

🛠️ Uso con Node.js/Next.js/Astro

// server/api.ts
import { createAxiosManager, createRouteConfig, get, post } from 'typed-axios-manager';

const routes = createRouteConfig({
  posts: {
    getAll: get('/posts'),
    create: post('/posts'),
  },
});

const apiManager = createAxiosManager<typeof routes>({
  baseURL: process.env.API_URL || 'https://api.example.com',
});

export const api = apiManager.createTypedRoutes(routes);

// pages/api/posts.ts (Next.js)
import { api } from '../server/api';

export default async function handler(req, res) {
  try {
    const posts = await api.posts.getAll();
    res.json(posts);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
}

⚙️ Configuración Avanzada

Headers Dinámicos

// Establecer token de autenticación
manager.setAuthToken('your-jwt-token');

// Agregar headers personalizados
manager.setHeader('X-Custom-Header', 'value');

// Remover token
manager.removeAuthToken();

Múltiples Instancias

const publicApi = createAxiosManager({ 
  baseURL: 'https://api.public.com' 
});

const privateApi = createAxiosManager({ 
  baseURL: 'https://api.private.com' 
});

const publicRoutes = publicApi.createTypedRoutes(publicRouteConfig);
const privateRoutes = privateApi.createTypedRoutes(privateRouteConfig);

📚 API Reference

createAxiosManager(config)

Crea una nueva instancia del manager.

Parámetros:

  • baseURL?: string - URL base para todas las peticiones
  • timeout?: number - Timeout en milisegundos (default: 5000)
  • headers?: Record<string, string> - Headers por defecto

Retorna: AxiosManager<T>

createRouteConfig(config)

Crea una configuración de rutas tipadas.

Retorna: T (tipado según tu configuración)

Helpers de rutas

  • get(path) - GET request
  • post(path) - POST request
  • put(path) - PUT request
  • del(path) - DELETE request
  • patch(path) - PATCH request

Manager Methods

  • createTypedRoutes(config) - Crea las funciones tipadas
  • setHeader(key, value) - Establece un header
  • setAuthToken(token) - Establece token de auth
  • removeAuthToken() - Remueve el token
  • getInstance() - Obtiene la instancia de axios

React Hooks (opcionales)

  • useApiCall(manager) - Hook para llamadas con loading/error states
  • useAuth(manager) - Hook para autenticación
  • createAxiosContext() - Crea context provider

🤝 Contribuir

¡Las contribuciones son bienvenidas! Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

📝 Licencia

Este proyecto está bajo la Licencia MIT.