Callback System (v2.0.0)

Learn how to use the powerful callback system introduced in hmm-api v2.0.0.

Callback System in hmm-api v2.0.0

The callback system is one of the most powerful new features in v2.0.0, giving you complete control over how your application responds to API calls.

Global Callbacks

Set up callbacks that run for every API request across your application.

onSuccess Callback

import ApiClient from "hmm-api";

const api = new ApiClient({
  onSuccess: (response) => {
    console.log("✅ Request succeeded:", response.status);

    // Update global loading state
    hideGlobalLoader();

    // Track successful requests
    analytics.track("api_success", {
      endpoint: response.response?.url,
      status: response.status,
    });
  },
});

onError Callback

const api = new ApiClient({
  onError: (response) => {
    console.error("❌ Request failed:", response.error);

    // Handle authentication errors globally
    if (response.status === 401) {
      redirectToLogin();
    }

    // Log errors to monitoring service
    errorLogger.log({
      error: response.error,
      status: response.status,
      endpoint: response.response?.url,
    });
  },
});

Per-Request Callbacks

Override global callbacks for specific requests when you need custom behavior.

Success Override

// Global success callback still runs, but this runs after
const response = await api.post("/users", userData, {
  onSuccess: (response) => {
    // Specific success handling for user creation
    showWelcomeModal(response.data.name);
    sendWelcomeEmail(response.data.email);
    redirectToOnboarding();
  },
});

Error Override

// Global error callback still runs, but this runs after
const response = await api.get("/optional-data", {
  onError: (response) => {
    // Silently handle this specific error
    console.log("Optional data not available, using defaults");
    useDefaultData();
  },
});

Combining with Finally Callbacks

The finally callback runs after both success and error callbacks, perfect for cleanup.

const response = await api.post("/upload", formData, {
  onSuccess: (response) => {
    showSuccessMessage("File uploaded successfully!");
    updateFileList(response.data);
  },
  onError: (response) => {
    showErrorMessage("Upload failed: " + response.error);
    highlightDropZone("error");
  },
  finally: () => {
    // Always runs regardless of success or failure
    hideUploadProgress();
    enableUploadButton();
    clearFileInput();
  },
});

Real-World Examples

Authentication Flow

const api = new ApiClient({
  baseUrl: "https://api.example.com",
  onSuccess: (response) => {
    // Reset failed attempt counter on any success
    resetFailedAttempts();
  },
  onError: (response) => {
    if (response.status === 401) {
      // Handle token expiration
      clearAuthToken();
      redirectToLogin();
    } else if (response.status >= 500) {
      // Handle server errors
      showMaintenanceMessage();
    }
  },
});

// Login with specific handling
const loginResponse = await api.post("/auth/login", credentials, {
  onSuccess: (response) => {
    setAuthToken(response.data.token);
    redirectToDashboard();
    showWelcomeMessage(response.data.user.name);
  },
  onError: (response) => {
    incrementFailedAttempts();
    if (getFailedAttempts() >= 3) {
      showCaptcha();
    }
  },
});

Form Submission with Validation

const api = new ApiClient({
  returnParsedError: true, // Get clean error messages
  onError: (response) => {
    // Global error logging
    logError(response.error);
  },
});

const submitForm = async (formData) => {
  setSubmitting(true);

  const response = await api.post("/forms/contact", formData, {
    onSuccess: (response) => {
      showSuccessMessage("Form submitted successfully!");
      resetForm();
      trackConversion("contact_form");
    },
    onError: (response) => {
      // Handle validation errors specifically
      if (response.status === 422) {
        highlightFieldErrors(response.error);
      } else {
        showGenericErrorMessage();
      }
    },
    finally: () => {
      setSubmitting(false);
    },
  });
};

File Upload with Progress

const uploadFile = async (file) => {
  const formData = new FormData();
  formData.append("file", file);

  showUploadProgress(0);

  const response = await api.post("/upload", formData, {
    onSuccess: (response) => {
      showUploadProgress(100);
      addToFileList(response.data);
      showSuccessToast("File uploaded successfully!");
    },
    onError: (response) => {
      showUploadError(response.error);
      highlightDropZone("error");
    },
    finally: () => {
      setTimeout(() => hideUploadProgress(), 1000);
    },
  });
};

Best Practices

1. Keep Global Callbacks Generic

// ✅ Good - Generic global handling
const api = new ApiClient({
  onSuccess: (response) => {
    hideGlobalLoader();
    logApiCall(response);
  },
  onError: (response) => {
    hideGlobalLoader();
    handleAuthErrors(response);
    logApiError(response);
  },
});

2. Use Per-Request Callbacks for Specific Logic

// ✅ Good - Specific per-request handling
const response = await api.post("/orders", orderData, {
  onSuccess: (response) => {
    redirectToOrderConfirmation(response.data.orderId);
    sendOrderConfirmationEmail();
  },
});

3. Always Use Finally for Cleanup

// ✅ Good - Cleanup in finally
const response = await api.get("/data", {
  finally: () => {
    hideLoadingSpinner();
    enableRefreshButton();
  },
});

The callback system in v2.0.0 gives you unprecedented control over your API interactions while maintaining clean, readable code. Use global callbacks for consistent behavior and per-request callbacks for specific use cases.