Using Queries
Learn how to use useQuery and useMutation in your React components
Using Queries
Once you've set up @hypequery/react, you can use useQuery and useMutation in your components with full type safety.
useQuery
Fetch data from your hypequery API with automatic caching and refetching:
import { useQuery } from '@/lib/analytics'; function RevenueChart() { const { data, error, isLoading } = useQuery('weeklyRevenue', { startDate: '2025-01-01', }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return <div>Total: ${data.total}</div>; }
Type Safety
The query name and input are fully typed:
// ✅ Type-safe const { data } = useQuery('weeklyRevenue', { startDate: '2025-01-01', }); // ❌ TypeScript error - invalid query name const { data } = useQuery('invalidQuery', { ... }); // ❌ TypeScript error - invalid input const { data } = useQuery('weeklyRevenue', { invalidField: 'value', });
TanStack Query Options
All TanStack Query options are supported:
const { data } = useQuery('weeklyRevenue', { startDate: '2025-01-01' }, { staleTime: 5 * 60 * 1000, // 5 minutes refetchOnWindowFocus: false, enabled: isAuthenticated, // Conditional fetching } );
useMutation
Execute write operations or actions:
import { useMutation } from '@/lib/analytics'; function RebuildButton() { const rebuild = useMutation('rebuildMetrics'); return ( <button onClick={() => rebuild.mutate({ force: true })} disabled={rebuild.isPending} > {rebuild.isPending ? 'Rebuilding...' : 'Rebuild Metrics'} </button> ); }
Handling Success and Errors
const rebuild = useMutation('rebuildMetrics', { onSuccess: (data) => { console.log('Rebuild complete:', data); // Invalidate related queries queryClient.invalidateQueries({ queryKey: ['weeklyRevenue'] }); }, onError: (error) => { console.error('Rebuild failed:', error); }, });
Optimistic Updates
const updateMetric = useMutation('updateMetric', { onMutate: async (newData) => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: ['metrics'] }); // Snapshot current value const previous = queryClient.getQueryData(['metrics']); // Optimistically update queryClient.setQueryData(['metrics'], (old) => ({ ...old, ...newData, })); return { previous }; }, onError: (err, variables, context) => { // Rollback on error queryClient.setQueryData(['metrics'], context.previous); }, onSettled: () => { // Refetch after success or error queryClient.invalidateQueries({ queryKey: ['metrics'] }); }, });
Error Handling
Errors from your hypequery API are structured:
const { data, error } = useQuery('weeklyRevenue', { startDate: '2025-01-01' }); if (error) { // Validation errors from hypequery if (error.status === 400) { return <div>Invalid input: {error.message}</div>; } // Network errors if (error.name === 'TypeError') { return <div>Network error. Please check your connection.</div>; } // Generic error return <div>Something went wrong: {error.message}</div>; }
Common Patterns
Dependent Queries
Execute queries in sequence:
function UserRevenue({ userId }: { userId: string }) { const { data: user } = useQuery('getUser', { userId }); const { data: revenue } = useQuery( 'userRevenue', { userId }, { enabled: !!user } // Only run after user is loaded ); return <div>{revenue?.total}</div>; }
Polling
Automatically refetch data at intervals:
const { data } = useQuery( 'liveMetrics', {}, { refetchInterval: 5000 } // Poll every 5 seconds );
Manual Refetch
Trigger refetch on demand:
function MetricsPanel() { const { data, refetch } = useQuery('metrics', {}); return ( <div> <div>{data?.total}</div> <button onClick={() => refetch()}>Refresh</button> </div> ); }