
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
typed-axios-manager
Advanced tools
Type-safe axios manager with autocomplete and path binding support
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!
{param}npm install typed-axios-manager axios
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;
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);
// 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: 'user@example.com',
password: 'password123'
});
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>(),
},
});
// 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: 'john@example.com' });
// POST con body y query
await api.users.create(
{ name: 'John', email: 'john@example.com' },
{ 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 }
);
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.
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: 'john@example.com' });
await api.users.create({ name: 'John', email: 'john@example.com' }, { 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.
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);
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[]>();
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 } };
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>
);
}
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.
}
// 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 });
}
}
// Establecer token de autenticación
manager.setAuthToken('your-jwt-token');
// Agregar headers personalizados
manager.setHeader('X-Custom-Header', 'value');
// Remover token
manager.removeAuthToken();
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);
createAxiosManager(config)Crea una nueva instancia del manager.
Parámetros:
baseURL?: string - URL base para todas las peticionestimeout?: number - Timeout en milisegundos (default: 5000)headers?: Record<string, string> - Headers por defectoRetorna: AxiosManager<T>
createRouteConfig(config)Crea una configuración de rutas tipadas.
Retorna: T (tipado según tu configuración)
get(path) - GET requestpost(path) - POST requestput(path) - PUT requestdel(path) - DELETE requestpatch(path) - PATCH requestcreateTypedRoutes(config) - Crea las funciones tipadassetHeader(key, value) - Establece un headersetAuthToken(token) - Establece token de authremoveAuthToken() - Remueve el tokengetInstance() - Obtiene la instancia de axiosuseApiCall(manager) - Hook para llamadas con loading/error statesuseAuth(manager) - Hook para autenticacióncreateAxiosContext() - Crea context provider¡Las contribuciones son bienvenidas! Por favor:
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)Este proyecto está bajo la Licencia MIT.
FAQs
Type-safe axios manager with autocomplete and path binding support
The npm package typed-axios-manager receives a total of 3 weekly downloads. As such, typed-axios-manager popularity was classified as not popular.
We found that typed-axios-manager demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.