@dbs-portal/core-api
Version:
HTTP client and API utilities for DBS Portal
197 lines • 6.59 kB
JavaScript
/**
* TanStack Query client configuration and setup
*/
import { QueryClient } from '@tanstack/react-query';
const { VITE_NODE_ENV: NODE_ENV } = import.meta.env;
/**
* Default query client configuration
*/
const defaultQueryConfig = {
defaultOptions: {
queries: {
// Cache data for 5 minutes by default
staleTime: 5 * 60 * 1000,
// Keep data in cache for 10 minutes
gcTime: 10 * 60 * 1000,
// Retry failed requests 3 times
retry: (failureCount, error) => {
// Don't retry on 4xx errors (client errors)
if (error && typeof error === 'object' && 'status' in error) {
const status = error.status;
if (typeof status === 'number' && status >= 400 && status < 500) {
return false;
}
}
return failureCount < 3;
},
// Retry with exponential backoff
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
// Refetch on window focus in production
refetchOnWindowFocus: NODE_ENV === 'production',
// Don't refetch on reconnect by default
refetchOnReconnect: false,
// Network mode
networkMode: 'online',
},
mutations: {
// Retry mutations once
retry: 1,
// Retry delay for mutations
retryDelay: 1000,
// Network mode for mutations
networkMode: 'online',
},
},
};
/**
* Create a configured QueryClient instance
*/
export function createQueryClient(apiClient, config = {}) {
const mergedConfig = {
...defaultQueryConfig,
...config,
defaultOptions: {
...defaultQueryConfig.defaultOptions,
...config.defaultOptions,
queries: {
...defaultQueryConfig.defaultOptions?.queries,
...config.defaultOptions?.queries,
// Add global error handling
throwOnError: error => {
// Log errors in development
if (NODE_ENV === 'development') {
console.error('Query error:', error);
}
// Always throw errors so they can be handled by the UI
return true;
},
},
mutations: {
...defaultQueryConfig.defaultOptions?.mutations,
...config.defaultOptions?.mutations,
// Add global mutation error handling
throwOnError: error => {
// Log mutation errors in development
if (NODE_ENV === 'development') {
console.error('Mutation error:', error);
}
// Always throw mutation errors so they can be handled by the UI
return true;
},
},
},
};
const queryClient = new QueryClient(mergedConfig);
// Add global error handler
queryClient.setMutationDefaults(['default'], {
mutationFn: async (variables) => {
if (!apiClient) {
throw new Error('API client not configured');
}
return variables;
},
});
return queryClient;
}
/**
* Default query client instance
*/
export const queryClient = createQueryClient();
/**
* Query key factory for consistent key generation
*/
export const queryKeys = {
// Base keys
all: ['api'],
// Resource-based keys
users: () => [...queryKeys.all, 'users'],
user: (id) => [...queryKeys.users(), id],
userProfile: (id) => [...queryKeys.user(id), 'profile'],
posts: () => [...queryKeys.all, 'posts'],
post: (id) => [...queryKeys.posts(), id],
postComments: (id) => [...queryKeys.post(id), 'comments'],
// Search and filters
search: (query) => [...queryKeys.all, 'search', query],
filtered: (resource, filters) => [...queryKeys.all, resource, 'filtered', filters],
// Paginated queries
paginated: (resource, page, limit) => [...queryKeys.all, resource, 'paginated', { page, limit }],
// Infinite queries
infinite: (resource, filters) => [...queryKeys.all, resource, 'infinite', filters],
// Custom query key generator
custom: (key, ...params) => [...queryKeys.all, key, ...params],
};
/**
* Query options factory for common patterns
*/
export const queryOptions = {
// Standard fetch with caching
standard: (key, fetcher) => ({
queryKey: key,
queryFn: fetcher,
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes
}),
// Real-time data (short cache)
realtime: (key, fetcher) => ({
queryKey: key,
queryFn: fetcher,
staleTime: 30 * 1000, // 30 seconds
gcTime: 2 * 60 * 1000, // 2 minutes
refetchInterval: 30 * 1000, // Refetch every 30 seconds
}),
// Static data (long cache)
static: (key, fetcher) => ({
queryKey: key,
queryFn: fetcher,
staleTime: 60 * 60 * 1000, // 1 hour
gcTime: 24 * 60 * 60 * 1000, // 24 hours
}),
// User-specific data
user: (key, fetcher) => ({
queryKey: key,
queryFn: fetcher,
staleTime: 2 * 60 * 1000, // 2 minutes
gcTime: 5 * 60 * 1000, // 5 minutes
}),
// Background sync
background: (key, fetcher) => ({
queryKey: key,
queryFn: fetcher,
staleTime: 0, // Always stale
gcTime: 5 * 60 * 1000, // 5 minutes
refetchOnWindowFocus: true,
refetchOnReconnect: true,
}),
};
/**
* Mutation options factory
*/
export const mutationOptions = {
// Standard mutation
standard: (mutationFn) => ({
mutationFn,
retry: 1,
retryDelay: 1000,
}),
// Optimistic mutation
optimistic: (mutationFn, optimisticUpdate) => ({
mutationFn,
retry: 1,
retryDelay: 1000,
...(optimisticUpdate && {
onMutate: optimisticUpdate,
}),
}),
// Critical mutation (no retry on client errors)
critical: (mutationFn) => ({
mutationFn,
retry: (failureCount, error) => {
if (error?.status >= 400 && error?.status < 500) {
return false;
}
return failureCount < 2;
},
retryDelay: 2000,
}),
};
//# sourceMappingURL=query-client.js.map