JAVASCRIPT
Robust API Error Handling with Custom JavaScript Errors
Learn to create a reusable JavaScript `fetch` wrapper that gracefully handles various HTTP status codes and API error messages, throwing custom error types for better control.
class ApiError extends Error {
constructor(message, status, data = null) {
super(message);
this.name = 'ApiError';
this.status = status;
this.data = data;
Object.setPrototypeOf(this, ApiError.prototype); // Fix for instanceof check
}
}
async function fetchData(url, options = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
const errorData = await response.json().catch(() => ({ message: 'Unknown error format' }));
let errorMessage = errorData.message || `API Error: ${response.status} ${response.statusText}`;
if (response.status === 401) {
errorMessage = errorData.message || 'Authentication required.';
// Potentially redirect to login or refresh token
} else if (response.status === 403) {
errorMessage = errorData.message || 'Permission denied.';
} else if (response.status === 404) {
errorMessage = errorData.message || 'Resource not found.';
} else if (response.status >= 500) {
errorMessage = errorData.message || 'Server error occurred.';
}
throw new ApiError(errorMessage, response.status, errorData);
}
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
throw error; // Re-throw custom API errors
}
// Handle network errors or other unexpected errors
throw new ApiError(`Network or unexpected error: ${error.message}`, 0, null);
}
}
// Example Usage:
// fetchData('https://api.example.com/data')
// .then(data => console.log('Success:', data))
// .catch(error => {
// console.error('Caught error:', error.message);
// if (error instanceof ApiError) {
// console.error('API Status:', error.status);
// console.error('API Error Data:', error.data);
// if (error.status === 401) {
// // Handle unauthorized specifically
// }
// }
// });
// Example usage with a POST request:
// fetchData('https://api.example.com/items', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ name: 'New Item' })
// }).then(data => console.log('Item created:', data))
// .catch(error => console.error('Error creating item:', error));
How it works: This snippet provides a robust `fetchData` wrapper around the native `fetch` API. It defines a custom `ApiError` class to carry HTTP status and API-specific error data. The `fetchData` function checks `response.ok` and, if false, attempts to parse the error body, constructing an `ApiError` with a user-friendly message based on common status codes. This allows consuming code to catch and handle different types of API errors specifically, improving user experience and debugging.