Skip to content

API Integration Guide

This guide covers data loading patterns, API client usage, and refresh mechanisms for integrating with the Waldur MasterMind backend.

API Data Loading and Refresh Patterns

The application utilizes React Query efficiently for loading data from REST APIs, rendering forms, and handling real-time data refresh operations.

Data Loading Patterns

React Query/TanStack Query (Modern Approach)

The preferred pattern for new components uses React Query for efficient data fetching:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const {
  data: projects,
  isLoading,
  error,
  refetch: refetchProjects,
} = useQuery({
  queryKey: ['CustomerProjects', selectedCustomer?.uuid],
  queryFn: () => fetchCustomerProjects(selectedCustomer.uuid),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

Key Features:

  • Automatic Caching: 5-minute stale time for most queries
  • Built-in Loading States: isLoading, error, data
  • Manual Refresh: refetch() function for explicit updates
  • Query Invalidation: Cache invalidation through query keys
  • Background Refetching: Automatic background updates

Custom Hook Pattern

Centralized data fetching logic wrapped in reusable hooks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export const useOrganizationGroups = () => {
  const user = useSelector(getUser);
  const query = useQuery({
    queryKey: ['organizationGroups'],
    queryFn: () => getAllPages((page) =>
      organizationGroupsList({ query: { page } })
    ).then(items => items.map(item => ({ ...item, value: item.url }))),
    staleTime: 5 * 60 * 1000,
  });

  const disabled = query.data?.length === 0 && !user.is_staff;
  const tooltip = disabled ? translate('Access policies cannot be configured...') : undefined;

  return { ...query, disabled, tooltip };
};

Benefits:

  • Business Logic Integration: Transforms data for UI consumption
  • Computed Properties: Adds disabled states and tooltips
  • Reusability: Shared across multiple components
  • Centralized Error Handling: Consistent error management

Data Refresh Mechanisms

CRUD Operations Refresh

Create Operations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const onSubmit = async (formData: ProjectFormData) => {
  try {
    const response = await projectsCreate({
      body: {
        name: formData.name,
        description: formData.description,
        customer: formData.customer.url,
      },
    });

    if (refetch) {
      await refetch(); // Refresh parent data
    }

    showSuccess(translate('Project has been created.'));
    closeDialog();
  } catch (e) {
    showErrorResponse(e, translate('Unable to create project.'));
  }
};

Edit Operations:

1
2
3
// Manual refresh after edit
await updateResource(resourceData);
refetch(); // Refresh data

Delete Operations:

1
2
3
4
5
6
await marketplaceProviderOfferingsRemoveOfferingComponent({
  path: { uuid: offering.uuid },
  body: { uuid: component.uuid },
});
refetch(); // Refresh parent data
dispatch(showSuccess(translate('Component has been removed.')));

Refresh Strategies

Strategy Implementation Use Case
Explicit Refetch const { refetch } = useQuery(...); await refetch(); Manual refresh after CRUD operations
Table Refresh Button <TableRefreshButton onClick={() => props.fetch(true)} /> User-initiated refresh
Automatic Polling refetchInterval in React Query Real-time data updates
Query Invalidation queryClient.invalidateQueries(['queryKey']) Cache invalidation

Error Handling and Loading States

Consistent Error Display

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{groupsLoading ? (
  <LoadingSpinner />
) : groupsError ? (
  <LoadingErred
    loadData={refetchGroups}
    message={translate('Unable to load organization groups.')}
  />
) : (
  <SelectField options={organizationGroups} />
)}

Global Error Handling

1
2
3
4
5
6
7
8
9
export const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error: any) => {
      if (error?.response?.status == 404) {
        router.stateService.go('errorPage.notFound');
      }
    },
  }),
});

API Integration Patterns

Waldur JS Client Integration

Primary API client with typed endpoints:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import {
  projectsCreate,
  projectsList,
  customersList
} from 'waldur-js-client';

// Typed API calls with request/response types
const response = await projectsCreate({
  body: {
    name: formData.name,
    customer: formData.customer.url,
  },
});

Async Form Field Loading

Dynamic data loading for form fields:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<Field
  component={Select}
  name="customer"
  loadOptions={(query, prevOptions, page) =>
    organizationAutocomplete(query, prevOptions, page, {
      field: ['uuid', 'name', 'url'],
      o: 'name',
    })
  }
  getOptionLabel={(option) => option.name}
  getOptionValue={(option) => option.url}
/>

Caching Strategies

React Query Cache

  • Query-based caching: Uses query keys for cache management
  • Automatic background refetching: Keeps data fresh
  • Configurable stale time: Typically 5 minutes for most queries
  • Request deduplication: Prevents duplicate requests

Redux Store Cache

  • Table data cached: In Redux state for tables
  • Manual cache invalidation: Explicit cache clearing
  • Optimistic updates: Immediate UI updates for CRUD operations

Best Practices

  1. New Components: Use React Query with custom hooks
  2. Error Handling: Consistent use of LoadingErred component with retry functionality
  3. Caching: 5-minute stale time for most queries, longer for static data
  4. Refresh Strategy: Always call refetch() after successful CRUD operations
  5. Loading States: Use isLoading state for UI feedback
  6. API Integration: Prefer waldur-js-client over direct fetch calls
  7. Form Validation: Use async validation with API dependency checking

This data loading architecture demonstrates the application's comprehensive utilization of modern React patterns for seamless API integrations.