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>
  );
}

Next Steps

Continue: Advanced Patterns - HTTP methods and advanced configuration

Or explore: Query Building - Learn the query builder API