Global Error Handling Examples
1. React App - Complete Setup
import ApiClient, { parseResponseMessage } from "hmm-api";
import { toast } from "sonner";
// Configure ONCE - handles ALL API calls
export const api = new ApiClient({
baseUrl: "https://api.example.com",
returnParsedError: true,
onError: (response) => {
// Handle ALL errors globally
toast.error(response.error);
// Global auth handling
if (response.status === 401) {
localStorage.removeItem("token");
window.location.href = "/login";
}
// Global server error handling
if (response.status >= 500) {
toast.error("Server error. Please try again later.");
}
},
onSuccess: (response) => {
// Optional: Global success feedback
console.log("✅ Request successful");
},
});
// Usage - No error handling needed anywhere!
const App = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
// GET request - errors handled automatically
const fetchUsers = async () => {
setLoading(true);
const response = await api.get("/users", {
finally: () => setLoading(false),
});
if (response.success) {
setUsers(response.data);
}
};
// POST request - errors handled automatically
const createUser = async (userData) => {
const response = await api.post("/users", userData);
if (response.success) {
setUsers((prev) => [...prev, response.data]);
toast.success("User created!");
}
};
return <div>{/* Your UI */}</div>;
};
2. Form Validation - Global Handling
import ApiClient, { parseResponseMessage } from "hmm-api";
import { toast } from "@/hooks/use-toast";
// Configure for form validation
export const api = new ApiClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL,
returnParsedError: true, // Clean validation messages
onError: (response) => {
// Handle ALL form errors globally
if (response.status === 422) {
toast({
title: "Validation Error",
description: response.error,
variant: "destructive",
});
} else if (response.status === 401) {
window.location.href = "/login";
} else {
toast({
title: "Error",
description: response.error,
variant: "destructive",
});
}
},
});
// Usage - No error handling needed!
const ContactForm = () => {
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (formData) => {
setIsSubmitting(true);
const response = await api.post("/contact", formData, {
finally: () => setIsSubmitting(false),
});
if (response.success) {
toast({ title: "Success!", description: "Message sent!" });
resetForm();
}
// No error handling needed - global config handles it!
};
return (
<form onSubmit={handleSubmit}>
<button disabled={isSubmitting}>
{isSubmitting ? "Sending..." : "Send"}
</button>
</form>
);
};
3. Using parseResponseMessage Utility
import { parseResponseMessage } from "hmm-api";
// Use in your own error handling
const customErrorHandler = (error) => {
const parsed = parseResponseMessage(error);
// Now you have a clean message
console.log(parsed.message); // "User not found" instead of complex object
// Use in your UI
showErrorDialog(parsed.message);
setErrorMessage(parsed.message);
};
// Example: Custom validation
const validateForm = (serverError) => {
const { message } = parseResponseMessage(serverError);
// Clean message for user display
setFieldError(message);
};
// Example: Logging
const logError = (error) => {
const { message } = parseResponseMessage(error);
logger.error("API Error", {
cleanMessage: message,
originalError: error,
});
};
4. Authentication - Global Handling
import ApiClient from "hmm-api";
import { toast } from "react-hot-toast";
// Configure auth handling globally
const api = new ApiClient({
baseUrl: "https://api.example.com",
credentials: "include",
returnParsedError: true,
onError: (response) => {
// Handle ALL auth errors globally
if (response.status === 401) {
localStorage.removeItem("token");
toast.error("Session expired. Please login again.");
window.location.href = "/login";
} else if (response.status === 403) {
toast.error("Access denied.");
window.location.href = "/dashboard";
} else {
toast.error(response.error);
}
},
});
// Usage - Auth handled automatically everywhere!
const Dashboard = () => {
const [profile, setProfile] = useState(null);
const [orders, setOrders] = useState([]);
useEffect(() => {
// Both requests protected automatically
loadProfile();
loadOrders();
}, []);
const loadProfile = async () => {
const response = await api.get("/user/profile");
if (response.success) {
setProfile(response.data);
}
// No auth error handling needed!
};
const loadOrders = async () => {
const response = await api.get("/user/orders");
if (response.success) {
setOrders(response.data);
}
// No auth error handling needed!
};
return <div>{/* Your protected UI */}</div>;
};
5. File Upload with Progress (New Feature)
import ApiClient from "hmm-api";
import { toast } from "sonner";
const api = new ApiClient({
baseUrl: "https://api.example.com",
timeout: 60000, // 1 minute for uploads
onError: (response) => {
toast.error(response.error);
},
});
const FileUploader = () => {
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const uploadFile = async (file: File) => {
setUploading(true);
const formData = new FormData();
formData.append("file", file);
const response = await api.post("/upload", formData, {
onUploadProgress: (progress) => {
setProgress(progress.percentage);
},
finally: () => setUploading(false),
});
if (response.success) {
toast.success("File uploaded!");
setProgress(0);
}
};
return (
<div>
<input type="file" onChange={(e) => uploadFile(e.target.files[0])} />
{uploading && <ProgressBar value={progress} />}
</div>
);
};
6. Real-time Polling (New Feature)
import ApiClient from "hmm-api";
const api = new ApiClient({
baseUrl: "https://api.example.com",
onError: (response) => {
console.error("Polling error:", response.error);
},
});
const OrderTracker = () => {
const [orderStatus, setOrderStatus] = useState(null);
const [polling, setPolling] = useState(null);
const startTracking = async (orderId: string) => {
const result = await api.get(`/orders/${orderId}`, {
poll: {
interval: 5000, // Check every 5 seconds
stopCondition: (response) => {
return response.data?.status === "delivered";
},
onPollSuccess: (response) => {
setOrderStatus(response.data);
},
},
});
// Store the stop function if it's a polling result
if ("stop" in result) {
setPolling({ stop: result.stop });
}
};
const stopTracking = () => {
if (polling) {
polling.stop();
setPolling(null);
}
};
return (
<div>
<button onClick={() => startTracking("123")}>Start Tracking</button>
<button onClick={stopTracking}>Stop</button>
<OrderStatus status={orderStatus} />
</div>
);
};
Key Benefits of Global Error Handling + New Features
Before hmm-api v2.0.0 (Repetitive)
// Every request needs error handling
const response1 = await fetch("/users");
if (!response1.ok) {
toast.error("Failed to load users");
if (response1.status === 401) redirectToLogin();
}
const response2 = await fetch("/posts");
if (!response2.ok) {
toast.error("Failed to load posts");
if (response2.status === 401) redirectToLogin();
}
// Repeat for every API call...
After hmm-api v2.0.0 (Global + New Features)
// Configure once
const api = new ApiClient({
timeout: 10000, // Global timeout
onError: (response) => {
toast.error(response.error);
if (response.status === 401) redirectToLogin();
},
});
// Clean usage everywhere - with new features!
const users = await api.get("/users");
const posts = await api.get("/posts");
const upload = await api.post("/upload", formData, {
onUploadProgress: (p) => console.log(`${p.percentage}%`),
});
// Automatic polling
const result = await api.get("/status", {
poll: {
interval: 5000,
stopCondition: (r) => r.data.ready,
},
});
New Features in v2.0.0
- Polling - Automatic data fetching with stop conditions
- Upload Progress - Track file upload progress like Axios
- Download Progress - Monitor download progress
- Timeout Support - Global and per-request timeouts
- Global Error Handling - Configure once, benefit everywhere
7. Multiple Independent Polling Operations
import ApiClient from "hmm-api";
const api = new ApiClient({
baseUrl: "https://api.example.com",
onError: (response) => {
console.error("API Error:", response.error);
},
});
const Dashboard = () => {
const [orderStatus, setOrderStatus] = useState(null);
const [inventoryStatus, setInventoryStatus] = useState(null);
const [systemHealth, setSystemHealth] = useState(null);
useEffect(() => {
// Start multiple independent polling operations simultaneously
startMultiplePolling();
}, []);
const startMultiplePolling = async () => {
// All three polling operations run independently
const [orderResult, inventoryResult, healthResult] = await Promise.all([
// Poll 1: Order status every 5 seconds
api.get("/orders/status", {
poll: {
interval: 5000,
maxAttempts: 20,
onPollSuccess: (response, attempt) => {
setOrderStatus(response.data);
console.log(`Order poll attempt ${attempt}`);
},
stopCondition: (response) => {
return response.data?.allOrdersProcessed === true;
},
},
}),
// Poll 2: Inventory levels every 10 seconds
api.get("/inventory/levels", {
poll: {
interval: 10000,
maxAttempts: 15,
onPollSuccess: (response, attempt) => {
setInventoryStatus(response.data);
console.log(`Inventory poll attempt ${attempt}`);
},
stopCondition: (response) => {
return response.data?.lowStockItems?.length === 0;
},
},
}),
// Poll 3: System health every 3 seconds
api.get("/system/health", {
poll: {
interval: 3000,
maxAttempts: 30,
onPollSuccess: (response, attempt) => {
setSystemHealth(response.data);
console.log(`Health poll attempt ${attempt}`);
},
stopCondition: (response) => {
return response.data?.status === "all_systems_operational";
},
},
}),
]);
// Each polling operation completes independently
console.log("All polling operations completed!");
};
return (
<div>
<OrderStatusWidget status={orderStatus} />
<InventoryWidget status={inventoryStatus} />
<SystemHealthWidget status={systemHealth} />
</div>
);
};
Key Benefits: Independent Polling Operations
- No Interference: Each polling operation runs independently with its own interval and conditions
- Different Intervals: Poll 1 (5s), Poll 2 (10s), Poll 3 (3s) - all work simultaneously
- Individual Stop Conditions: Each poll can stop based on its own criteria
- Separate Callbacks: Each polling operation has its own success/error handling
- Parallel Execution: All polling operations run concurrently using Promise.all
Configure once, benefit everywhere with powerful new features!