All files / web/src/lib queryClient.ts

100% Statements 89/89
80% Branches 4/5
100% Functions 3/3
100% Lines 89/89

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 901x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 91x 91x 91x 91x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 91x 91x  
import { type DefaultOptions, QueryClient } from '@tanstack/react-query'
import { cache } from 'react'
 
const queryConfig: DefaultOptions = {
  queries: {
    // Default stale time of 5 minutes
    staleTime: 1000 * 60 * 5,
    // Retry failed requests once
    retry: 1,
    // Refetch on window focus in production only
    refetchOnWindowFocus: process.env.NODE_ENV === 'production',
  },
}
 
/**
 * Creates a new QueryClient instance with default configuration.
 * All API routes are expected to be prefixed with /api.
 */
export function createQueryClient() {
  return new QueryClient({
    defaultOptions: queryConfig,
  })
}
 
/**
 * Server-side query client, memoized per-request via React cache().
 * Use this in Server Components for SSR data prefetching.
 *
 * @example
 * ```tsx
 * // In a Server Component
 * const queryClient = getQueryClient()
 * await queryClient.prefetchQuery({
 *   queryKey: ['user', id],
 *   queryFn: () => getUser(id),
 * })
 * return (
 *   <HydrationBoundary state={dehydrate(queryClient)}>
 *     <ClientComponent />
 *   </HydrationBoundary>
 * )
 * ```
 */
export const getQueryClient = cache(() => createQueryClient())
 
/**
 * Helper function to construct API URLs with the /api prefix.
 * Use this for consistency when making API calls.
 *
 * @param path - The API path without the /api prefix (e.g., 'users', 'posts/123')
 * @returns The full API URL (e.g., '/api/users', '/api/posts/123')
 *
 * @example
 * ```ts
 * const url = apiUrl('users') // '/api/users'
 * const url = apiUrl('posts/123') // '/api/posts/123'
 * ```
 */
export function apiUrl(path: string): string {
  // Remove leading slash if present to avoid double slashes
  const cleanPath = path.startsWith('/') ? path.slice(1) : path
  return `/api/${cleanPath}`
}
 
/**
 * Wrapper around fetch that automatically prefixes paths with /api.
 * Provides a consistent way to make API calls throughout the application.
 *
 * @param path - The API path without the /api prefix (e.g., 'users', 'posts/123')
 * @param options - Standard fetch options (method, headers, body, etc.)
 * @returns Promise with the fetch Response
 *
 * @example
 * ```ts
 * // GET request
 * const response = await api('users')
 * const users = await response.json()
 *
 * // POST request
 * const response = await api('users', {
 *   method: 'POST',
 *   headers: { 'Content-Type': 'application/json' },
 *   body: JSON.stringify({ name: 'John' })
 * })
 * ```
 */
export function api(path: string, options?: RequestInit): Promise<Response> {
  return fetch(apiUrl(path), options)
}