POST Method - Global Error Handling

Why Use Global Error Handling?

Configure error handling once globally instead of repeating it in every POST request. Perfect for forms, user creation, and data submission.

Complete Example

import ApiClient, { parseResponseMessage } from "hmm-api";
import { toast } from "sonner";

// Configure once - handles ALL POST requests
const api = new ApiClient({
  baseUrl: "https://api.example.com",
  returnParsedError: true, // Get clean validation messages
  onError: (response) => {
    // Handle ALL form errors globally
    toast.error(response.error);

    // Global validation error handling
    if (response.status === 422) {
      console.log("Validation failed:", response.error);
    }

    if (response.status === 401) {
      redirectToLogin();
    }
  },
  onSuccess: (response) => {
    // Optional: Global success handling
    toast.success("Saved successfully!");
  },
});

// Usage - No error handling needed!
interface CreateUserRequest {
  name: string;
  email: string;
}

interface User {
  id: number;
  name: string;
  email: string;
}

const UserForm = () => {
  const [loading, setLoading] = useState(false);

  const createUser = async (userData: CreateUserRequest) => {
    setLoading(true);

    // Simple POST - errors handled automatically
    const response = await api.post<User>("/users", userData, {
      finally: () => setLoading(false),
    });

    if (response.success) {
      // Only handle success case
      redirectToUser(response.data.id);
      resetForm();
    }
  };

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

    // File upload - errors handled automatically
    const response = await api.post("/upload", formData);

    if (response.success) {
      console.log("File uploaded:", response.data.url);
    }
  };

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        createUser({ name: "John", email: "john@example.com" });
      }}
    >
      <button type="submit" disabled={loading}>
        {loading ? "Creating..." : "Create User"}
      </button>
    </form>
  );
};

Key Benefits

  • No repetitive error handling - Configure once for all forms
  • Consistent validation UX - All form errors shown the same way
  • Clean form code - Focus on form logic, not error handling
  • Global auth handling - 401 errors handled automatically

Advanced POST Features (v2.0.0)

File Upload with Progress

Track upload progress for better user experience:

const FileUploader = () => {
  const [uploadProgress, setUploadProgress] = useState(0);
  const [uploading, setUploading] = useState(false);

  const handleFileUpload = async (file: File) => {
    setUploading(true);
    setUploadProgress(0);

    const formData = new FormData();
    formData.append("file", file);
    formData.append("category", "documents");

    const response = await api.post("/files/upload", formData, {
      onUploadProgress: (progress) => {
        setUploadProgress(progress.percentage);
        console.log(`Upload: ${progress.percentage}%`);
        console.log(`${progress.loaded} / ${progress.total} bytes`);
      },
      timeout: 60000, // 1 minute timeout for uploads
      finally: () => {
        setUploading(false);
      },
    });

    if (response.success) {
      console.log("File uploaded:", response.data.url);
      resetUploadForm();
    }
  };

  return (
    <div>
      <input
        type="file"
        onChange={(e) => handleFileUpload(e.target.files[0])}
        disabled={uploading}
      />
      {uploading && (
        <div>
          <ProgressBar value={uploadProgress} />
          <span>{uploadProgress}% uploaded</span>
        </div>
      )}
    </div>
  );
};

POST with Polling

Submit data and poll for processing status:

const processDocument = async (documentData: any) => {
  // Step 1: Submit document for processing
  const submitResponse = await api.post("/documents/process", documentData);

  if (!submitResponse.success) return;

  const jobId = submitResponse.data.jobId;

  // Step 2: Poll for processing status
  const result = await api.get(`/jobs/${jobId}/status`, {
    poll: {
      interval: 2000, // Check every 2 seconds
      maxAttempts: 30,
      stopCondition: (response) => {
        return response.success && response.data.status === "completed";
      },
      onPollSuccess: (response, attempt) => {
        console.log(`Processing: ${response.data.progress}%`);
        updateProcessingProgress(response.data.progress);
      },
    },
  });

  if ("finalResponse" in result && result.finalResponse.success) {
    console.log("Document processed successfully!");
    return result.finalResponse.data;
  }
};

Batch Operations with Progress

Handle multiple POST requests with progress tracking:

const batchCreateUsers = async (users: CreateUserRequest[]) => {
  const results = [];
  let completed = 0;

  for (const userData of users) {
    const response = await api.post("/users", userData, {
      onUploadProgress: (progress) => {
        console.log(`User ${completed + 1}: ${progress.percentage}%`);
      },
      finally: () => {
        completed++;
        const overallProgress = (completed / users.length) * 100;
        updateBatchProgress(overallProgress);
      },
    });

    results.push(response);

    if (response.success) {
      console.log(`Created user: ${response.data.name}`);
    }
  }

  return results;
};

Form Submission with Cleanup

Proper form handling with automatic cleanup:

const ContactForm = () => {
  const [submitting, setSubmitting] = useState(false);
  const apiRef = useRef(null);

  useEffect(() => {
    apiRef.current = new ApiClient({
      baseUrl: "/api",
      onError: (response) => {
        toast.error(response.error);
      },
      onSuccess: (response) => {
        toast.success("Message sent successfully!");
      },
    });

    // Cleanup on unmount
    return () => {
      if (apiRef.current) {
        apiRef.current.destroy();
      }
    };
  }, []);

  const handleSubmit = async (formData: ContactFormData) => {
    setSubmitting(true);

    const response = await apiRef.current.post("/contact", formData, {
      timeout: 10000, // 10 second timeout for contact forms
      finally: () => {
        setSubmitting(false);
      },
    });

    if (response.success) {
      resetForm();
      redirectToThankYou();
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit" disabled={submitting}>
        {submitting ? "Sending..." : "Send Message"}
      </button>
    </form>
  );
};

When to Override Global Handling

Only override when you need specific behavior:

// Special case: Custom validation error display
const response = await api.post("/users", userData, {
  onError: (response) => {
    // Custom handling for this specific form
    highlightFieldErrors(response.error);
    showInlineValidation(response.error);
  },
});

// Special case: Silent submission for auto-save
const response = await api.post("/auto-save", draftData, {
  showError: false,
  showSuccess: false,
  timeout: 5000, // Quick timeout for auto-save
});