@haykal/core-client
v1.0.0
Published
Shared HTTP client infrastructure for Haykal frontend applications.
Readme
@haykal/core-client
Shared HTTP client infrastructure for Haykal frontend applications.
Features
- Singleton HTTP client — single Axios instance shared across all domain packages
- Interceptor pipeline — auth token injection, response envelope unwrap, RFC 7807 error transform, retry with exponential backoff
- Platform-agnostic token storage — auto-detects Web (
localStorage) / React Native (AsyncStorage) / Node (in-memory) - React Query integration — pre-configured
QueryClientfactory with smart retry logic - React Provider —
<HaykalProvider>wraps both client init andQueryClientProvider - Custom Orval mutator — bridges Orval-generated hooks with the shared client
- Event system — observe requests, errors, and auth events for analytics/debugging
- Typed errors —
ApiErrorclass with convenience helpers (isValidationError,getFieldErrors(), etc.)
Quick Start
import { HaykalProvider } from '@haykal/core-client';
function App() {
return (
<HaykalProvider config={{ baseURL: '/api' }}>
<YourApp />
</HaykalProvider>
);
}Or initialize manually:
import { HaykalClient, createHaykalQueryClient } from '@haykal/core-client';
// Initialize once at app startup
const client = HaykalClient.getInstance({
baseURL: 'https://api.example.com',
retry: { enabled: true, maxRetries: 3 },
onTokenRefresh: async () => {
/* refresh logic */
},
onAuthFailure: () => {
/* redirect to login */
},
});
const queryClient = createHaykalQueryClient();Error Handling
import { ApiError } from '@haykal/core-client';
try {
await someApiCall();
} catch (error) {
if (ApiError.isApiError(error)) {
if (error.isValidationError) {
const fields = error.getFieldErrors();
// { email: 'must be valid', password: 'too short' }
}
if (error.is('RESOURCE_NOT_FOUND')) {
// handle specific error code
}
console.log(error.getUserMessage()); // user-friendly message
}
}Token Storage
import { tokenStorage } from '@haykal/core-client';
// Tokens are managed automatically by the auth interceptor,
// but you can also access them directly:
const token = await tokenStorage.getAccessToken();
// Use a custom storage adapter (e.g., expo-secure-store):
tokenStorage.setAdapter(mySecureStorageAdapter);