@authuser/http-react-native
v1.0.2
Published
Authuser HTTP React Native package for integrating with React Query, Async Storage, and MMKV.
Downloads
238
Maintainers
Readme
@authuser/http-react-native
Adaptador de React Native para @authuser/http-core, basado en @tanstack/react-query.
Incluye hooks declarativos (useHttpQuery, useHttpMutation) con soporte para:
- Caché (memoria o persistente)
- Reintentos automáticos
- Rate limiting
- Deduplicación de peticiones simultáneas
- Middleware extensible
- Tipado completo en TypeScript
📦 Instalación
npm install @authuser/http-react-nativeNota: Este paquete incluye todas las dependencias necesarias (@authuser/http-core, @tanstack/react-query, @react-native-async-storage/async-storage, axios). No necesitas instalar nada más.
🚀 Uso básico
1. Crea el HttpService
// src/lib/http.ts
import { HttpService, AxiosHttpClient } from '@authuser/http-core';
import {
AsyncStorageCache,
AsyncStorageSession,
AsyncStoragePersistence,
} from '@authuser/http-react-native';
import Config from 'react-native-config';
// Usar el Bundle ID de tu app como prefijo (recomendado)
const APP_ID = 'com.mycompany.myapp'; // ← Tu Bundle ID
export const http = new HttpService({
client: new AxiosHttpClient(),
session: new AsyncStorageSession(),
cache: new AsyncStorageCache(`${APP_ID}.cache.`), // caché temporal con TTL
persistence: new AsyncStoragePersistence(`${APP_ID}.persist.`), // persistencia permanente
config: {
baseUrl: Config.BASE_URL ?? '',
cache: { enabled: true, ttlMs: 60_000 },
retry: {
enabled: true,
maxAttempts: 1,
delay: 500,
backoff: 'exponential',
skipOnEndpoint: ['/auth/login'],
skipOnStatusCode: [401, 429],
},
},
});2. Envuelve tu aplicación
// App.tsx
import { HttpProvider } from '@authuser/http-react-native';
import { http } from './lib/http';
import MainApp from './MainApp';
export default function App() {
return (
<HttpProvider service={http}>
<MainApp />
</HttpProvider>
);
}💡 Nota:
HttpProviderya incluye internamenteQueryClientProvider, por lo que solo necesitas un provider.
Configuración avanzada (opcional)
Si necesitas personalizar el QueryClient:
import { HttpProvider } from '@authuser/http-react-native';
import { QueryClient } from '@tanstack/react-query';
import { http } from './lib/http';
import MainApp from './MainApp';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000, // 1 minuto
cacheTime: 300_000, // 5 minutos
},
},
});
export default function App() {
return (
<HttpProvider service={http} queryClient={queryClient}>
<MainApp />
</HttpProvider>
);
}🧪 Hooks disponibles
🔍 useHttpQuery
| Característica | Valor |
| ------------------ | ------------------------------------ |
| Basado en | useQuery |
| Caché automática | ✅ Sí (memoiza resultados por clave) |
| Reintentos | ✅ Sí (según config retry) |
| Refetch automático | ✅ Sí (focus / reconexión) |
| Uso típico | GET / datos |
import { useHttpQuery } from '@authuser/http-react-native';
// Ejemplo básico con caché temporal
const { data, isLoading, error } = useHttpQuery<User[]>({ url: '/users', cache: true });
// Con persistencia offline-first
const { data, isLoading, error } = useHttpQuery<User[]>({
url: '/user/profile',
persist: true,
persistKey: 'user-profile',
});
// Con query params y opciones de React Query
const { data, isLoading, error } = useHttpQuery<User[]>(
{ url: '/users', query: { role: 'admin' } },
{ staleTime: 30_000, refetchOnWindowFocus: false }
);Parámetros:
req: ObjetoHttpRequestsinmethod(o conmethod: 'GET'opcional)opts: Opciones deuseQueryde React Query (omitiendoqueryKeyyqueryFn)
✏️ useHttpMutation
| Característica | Valor |
| -------------- | --------------------------- |
| Basado en | useMutation |
| Caché | ❌ No |
| Refetch | ❌ No automático |
| Uso típico | POST / PUT / PATCH / DELETE |
import { useHttpMutation } from '@authuser/http-react-native';
const { mutate, isPending, isSuccess } = useHttpMutation();
mutate({
url: '/users',
method: 'POST',
body: { name: 'Neo' },
});🔁 RetryConfig
type RetryConfig = {
enabled?: boolean;
maxAttempts?: number;
delay?: number;
backoff?: 'linear' | 'exponential' | 'fixed';
skipOnStatusCode?: number[];
skipOnEndpoint?: string[];
onRetry?: (retryCount: number, error: AxiosError, config: any) => void;
};🧱 Tipos avanzados
type User = { id: string; name: string };
type NewUser = { name: string };
const { data } = useHttpQuery<User[]>({ url: '/users' });
const { mutate } = useHttpMutation<User, NewUser>();🧠 Resumen práctico
| Caso | Hook recomendado |
| ----------------------------- | ----------------- |
| Obtener lista de productos | useHttpQuery |
| Obtener detalle de un vídeo | useHttpQuery |
| Crear nuevo usuario | useHttpMutation |
| Enviar formulario de contacto | useHttpMutation |
| Actualizar perfil de usuario | useHttpMutation |
| Borrar comentario | useHttpMutation |
🔑 Prefijos y Organización
Tanto AsyncStorageCache como AsyncStoragePersistence aceptan un prefijo opcional para organizar las claves en AsyncStorage:
// ✅ Opción 1: Sin prefijo (usa '__http_cache__' y 'http-persist:' por defecto)
const cache = new AsyncStorageCache();
const persistence = new AsyncStoragePersistence();
// ✅ Opción 2: Con Bundle ID (recomendado para apps en producción)
const APP_ID = 'com.mycompany.myapp';
const cache = new AsyncStorageCache(`${APP_ID}.cache.`);
const persistence = new AsyncStoragePersistence(`${APP_ID}.persist.`);
// ✅ Opción 3: Con prefijos personalizados
const cache = new AsyncStorageCache('myapp-cache-');
const persistence = new AsyncStoragePersistence('myapp-persist-');¿Por qué usar el Bundle ID?
- Evita colisiones: Si tienes múltiples apps o módulos
- Identificación clara: Las claves en AsyncStorage son fáciles de identificar
- Convención estándar: Similar a UserDefaults en iOS o SharedPreferences en Android
Ejemplo de claves generadas
// Con Bundle ID 'com.mycompany.myapp'
const cache = new AsyncStorageCache('com.mycompany.myapp.cache.');
const persistence = new AsyncStoragePersistence('com.mycompany.myapp.persist.');
// Generará claves como:
// 'com.mycompany.myapp.cache.:GET|/user/profile||'
// 'com.mycompany.myapp.persist.:user-profile'🌐 Persistencia Offline-First
El paquete incluye soporte para persistencia permanente de respuestas, ideal para apps móviles que necesitan funcionar sin conexión:
AsyncStoragePersistence
Almacena respuestas HTTP de forma permanente en AsyncStorage para acceso offline:
import { AsyncStoragePersistence } from '@authuser/http-react-native';
// En tu configuración de HttpService
const persistence = new AsyncStoragePersistence('myapp-');
const http = new HttpService({
client: new AxiosHttpClient(),
persistence,
config: { baseUrl: Config.BASE_URL, retry: { enabled: true } },
});Ejemplo de uso en pantallas
import { useHttpQuery } from '@authuser/http-react-native';
import { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
function UserProfileScreen() {
const [isOffline, setIsOffline] = useState(false);
// Petición con persistencia - funcionará offline
const {
data: profile,
isLoading,
error,
} = useHttpQuery<UserProfile>({
url: '/user/profile',
persist: true,
persistKey: 'user-profile',
requiresAuth: true,
});
useEffect(() => {
// Monitorear conexión
const unsubscribe = NetInfo.addEventListener((state) => {
setIsOffline(!state.isConnected);
});
return () => unsubscribe();
}, []);
if (isLoading) return <ActivityIndicator size="large" />;
return (
<View>
{isOffline && (
<View style={styles.offlineBanner}>
<Text>📴 Modo Offline - Mostrando datos guardados</Text>
</View>
)}
{profile && (
<View>
<Text style={styles.name}>{profile.name}</Text>
<Text style={styles.email}>{profile.email}</Text>
</View>
)}
</View>
);
}Casos de uso comunes en móviles
// 1. Perfil del usuario (siempre disponible)
const { data: profile } = useHttpQuery({
url: '/user/profile',
persist: true,
persistKey: 'user-profile',
requiresAuth: true,
});
// 2. Configuración de la app
const { data: config } = useHttpQuery({
url: '/config/app',
persist: true,
persistKey: 'app-config',
});
// 3. Catálogo de productos (e-commerce offline)
const { data: products } = useHttpQuery({
url: '/products',
query: { category: 'electronics' },
persist: true,
persistKey: 'products-electronics',
});
// 4. Contenido para lectura offline (noticias, artículos)
const { data: articles } = useHttpQuery({
url: '/articles/latest',
persist: true,
persistKey: 'latest-articles',
});
// 5. Datos de dashboard
const { data: metrics } = useHttpQuery({
url: '/dashboard/metrics',
persist: true,
persistKey: 'dashboard-metrics',
cache: true, // También usar caché para performance
});⚠️ Diferencia entre Cache y Persistencia
| | Cache | Persistencia | | -------------------- | -------------- | --------------------- | | Duración | Temporal (TTL) | Permanente | | Propósito | Performance | Soporte offline | | Expira | ✅ Sí | ❌ No | | Fallback offline | ❌ No | ✅ Sí | | Actualización | Después de TTL | Cada petición exitosa | | Storage | AsyncStorage | AsyncStorage |
Recomendación: Usa cache: true + persist: true para máximo performance Y soporte offline.
🔄 Sincronización automática
La persistencia se actualiza automáticamente cuando:
- ✅ La app recupera conexión
- ✅ El usuario hace pull-to-refresh
- ✅ React Query refetch automático
import { useHttpQuery } from '@authuser/http-react-native';
import { RefreshControl, ScrollView } from 'react-native';
function ProductList() {
const {
data: products,
isLoading,
refetch,
} = useHttpQuery({
url: '/products',
persist: true,
persistKey: 'products',
});
return (
<ScrollView
refreshControl={<RefreshControl refreshing={isLoading} onRefresh={() => refetch()} />}
>
{/* Renderiza productos */}
</ScrollView>
);
}🧩 Personalización
Usar tu propio SessionManagerPort:
new HttpService({
client: new AxiosHttpClient(),
session: new MyCustomSessionManager(),
config: { baseUrl: '/api' },
});Usar caché persistente:
new HttpService({
client,
cache: new AsyncStorageCache(), // implementa CachePort
config,
});🎯 Exportaciones del paquete
export { useHttpQuery } from './hooks/useHttpQuery';
export { useHttpMutation } from './hooks/useHttpMutation';
export { HttpProvider, useHttpService } from './provider/HttpContext';
export { AsyncStorageCache } from './infrastructure/AsyncStorageCache';
export { AsyncStoragePersistence } from './infrastructure/AsyncStoragePersistence';
export { AsyncStorageSession } from './infrastructure/AsyncStorageSession';📁 Estructura recomendada
src/
├─ lib/
│ ├─ http.ts ← instancia de HttpService
│ └─ session.ts ← implementación de SessionManager
├─ screens/
│ └─ Users.tsx ← useHttpQuery
├─ App.tsx
└─ main.tsx ← montaje de providers🧪 Testing
Mockea HttpService o usa msw + @tanstack/react-query/testing.
Puedes usar el helper createTestQueryClient() para pruebas unitarias.
🛠️ Dependencias
Dependencies (incluidas automáticamente)
@authuser/http-core: ^1.6.0 (incluyeaxios)@tanstack/react-query: ^5.0.0@react-native-async-storage/async-storage: ^2.2.0
Peer Dependencies (debes tenerlas en tu proyecto)
react: >=18.0.0react-native: >=0.70.0
✨ Instalación simple: Un solo comando instala todo lo necesario.
