Files
thrilltrack-explorer/src/docs/API_PATTERNS.md
2025-10-31 12:03:22 +00:00

4.3 KiB

API and Cache Patterns

Mutation Pattern (PREFERRED)

Always use useMutation hooks for data modifications instead of direct Supabase calls.

CORRECT Pattern

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { toast } from 'sonner';
import { getErrorMessage } from '@/lib/errorHandler';
import { useQueryInvalidation } from '@/lib/queryInvalidation';

export function useMyMutation() {
  const queryClient = useQueryClient();
  const { invalidateRelatedCache } = useQueryInvalidation();

  return useMutation({
    mutationFn: async (params) => {
      const { error } = await supabase
        .from('table')
        .insert(params);
      
      if (error) throw error;
    },
    onMutate: async (params) => {
      // Optional: Optimistic updates
      await queryClient.cancelQueries({ queryKey: ['my-data'] });
      const previous = queryClient.getQueryData(['my-data']);
      
      queryClient.setQueryData(['my-data'], (old) => {
        // Update optimistically
      });
      
      return { previous };
    },
    onError: (error, variables, context) => {
      // Rollback optimistic updates
      if (context?.previous) {
        queryClient.setQueryData(['my-data'], context.previous);
      }
      
      toast.error("Error", {
        description: getErrorMessage(error),
      });
    },
    onSuccess: () => {
      invalidateRelatedCache();
      toast.success("Success", {
        description: "Operation completed successfully.",
      });
    },
  });
}

INCORRECT Pattern (Direct Supabase)

// DON'T DO THIS
const handleSubmit = async () => {
  try {
    const { error } = await supabase.from('table').insert(data);
    if (error) throw error;
    toast.success('Success');
  } catch (error) {
    toast.error(error.message);
  }
};

Error Handling Pattern

CORRECT: Use onError callback

const mutation = useMutation({
  mutationFn: async (data) => {
    const { error } = await supabase.from('table').insert(data);
    if (error) throw error;
  },
  onError: (error: unknown) => {
    toast.error("Error", {
      description: getErrorMessage(error),
    });
  },
});

INCORRECT: try/catch in component

// Avoid this pattern
const handleSubmit = async () => {
  try {
    await mutation.mutateAsync(data);
  } catch (error) {
    // Error already handled in mutation
  }
};

Query Keys Pattern

CORRECT: Use centralized queryKeys

import { queryKeys } from '@/lib/queryKeys';

const { data } = useQuery({
  queryKey: queryKeys.parks.detail(slug),
  queryFn: fetchParkDetail,
});

INCORRECT: Inline query keys

// Don't do this
const { data } = useQuery({
  queryKey: ['parks', 'detail', slug],
  queryFn: fetchParkDetail,
});

Cache Invalidation Pattern

CORRECT: Use invalidation helpers

import { useQueryInvalidation } from '@/lib/queryInvalidation';

const { invalidateParks, invalidateHomepageData } = useQueryInvalidation();

// In mutation onSuccess:
onSuccess: () => {
  invalidateParks();
  invalidateHomepageData('parks');
}

INCORRECT: Manual invalidation

// Avoid this
queryClient.invalidateQueries({ queryKey: ['parks'] });

Benefits of This Pattern

  1. Automatic retry logic: Failed mutations can be retried automatically
  2. Loading states: isPending flag for UI feedback
  3. Optimistic updates: Update UI before server confirms
  4. Consistent error handling: Centralized error handling
  5. Cache coordination: Proper invalidation timing
  6. Testing: Easier to mock and test
  7. Type safety: Better TypeScript support

Migration Checklist

When migrating a component:

  • Create custom mutation hook in appropriate directory
  • Use useMutation instead of direct Supabase calls
  • Implement onError callback with toast notifications
  • Implement onSuccess callback with cache invalidation
  • Use centralized queryKeys for query identification
  • Use useQueryInvalidation helpers for cache management
  • Replace loading state with mutation.isPending
  • Remove try/catch blocks from component
  • Test optimistic updates if applicable