HTTP Status Codes
The Lightyshare API uses standard HTTP status codes to indicate the success or failure of requests:Status Code | Description | Meaning |
---|---|---|
200 | OK | Request successful |
201 | Created | Resource created successfully |
400 | Bad Request | Invalid request data |
401 | Unauthorized | Missing or invalid authentication |
403 | Forbidden | Access denied to resource |
404 | Not Found | Resource doesn’t exist |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server error |
Error Response Format
Most error responses follow this format:Copy
{
"success": false,
"errors": [
{
"field": "field_name",
"code": "ERROR_CODE",
"message": "Human-readable error message"
}
]
}
Authentication Errors (401)
Missing Token
Copy
{
"error": "Invalid or missing token"
}
Invalid Token
Copy
{
"error": "Invalid or missing token"
}
Handling Authentication Errors
Copy
async function handleAuthError(response) {
if (response.status === 401) {
// Token is invalid or expired
console.error('Authentication failed');
// Refresh token or redirect to login
await refreshToken();
// Retry the request
return retryRequest();
}
}
async function refreshToken() {
// Implement token refresh logic
const newToken = await getNewToken();
localStorage.setItem('api_token', newToken);
}
Validation Errors (400)
Missing Required Fields
Copy
{
"success": false,
"errors": [
{
"field": "title",
"code": "REQUIRED",
"message": "Title is required"
},
{
"field": "rent",
"code": "REQUIRED",
"message": "Rental price is required"
}
]
}
Invalid Data Types
Copy
{
"success": false,
"errors": [
{
"field": "rent",
"code": "INVALID_TYPE",
"message": "Rental price must be a number"
}
]
}
Value Constraints
Copy
{
"success": false,
"errors": [
{
"field": "rent",
"code": "MIN_VALUE",
"message": "Rental price must be at least 0.01"
},
{
"field": "quantity",
"code": "MIN_VALUE",
"message": "Quantity must be at least 1"
}
]
}
String Length Limits
Copy
{
"success": false,
"errors": [
{
"field": "title",
"code": "MAX_LENGTH",
"message": "Title must be 255 characters or less"
}
]
}
Handling Validation Errors
Copy
async function handleValidationErrors(response) {
if (response.status === 400) {
const errorData = await response.json();
if (errorData.errors) {
// Display field-specific errors
errorData.errors.forEach(error => {
displayFieldError(error.field, error.message);
});
} else {
// Display general error
displayGeneralError(errorData.error || 'Validation failed');
}
}
}
function displayFieldError(field, message) {
const fieldElement = document.querySelector(`[name="${field}"]`);
if (fieldElement) {
fieldElement.classList.add('error');
fieldElement.nextElementSibling.textContent = message;
}
}
Access Control Errors (403)
Resource Access Denied
Copy
{
"error": "Access denied to this resource"
}
Handling Access Errors
Copy
async function handleAccessError(response) {
if (response.status === 403) {
console.error('Access denied to this resource');
// Check if user has proper permissions
await checkUserPermissions();
// Show appropriate message to user
showAccessDeniedMessage();
}
}
Not Found Errors (404)
Resource Not Found
Copy
{
"error": "Resource not found"
}
Handling Not Found Errors
Copy
async function handleNotFoundError(response) {
if (response.status === 404) {
console.error('Resource not found');
// Show 404 page or message
showNotFoundMessage();
// Optionally redirect to list page
// window.location.href = '/products';
}
}
Rate Limiting Errors (429)
Rate Limit Exceeded
Copy
{
"error": "Request limit exceeded"
}
Rate Limit Headers
Copy
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 60
Handling Rate Limit Errors
Copy
async function handleRateLimitError(response) {
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
console.log(`Rate limited. Waiting ${waitTime}ms before retry...`);
// Show user-friendly message
showRateLimitMessage(waitTime);
// Wait and retry
await delay(waitTime);
return retryRequest();
}
}
function showRateLimitMessage(waitTime) {
const seconds = Math.ceil(waitTime / 1000);
const message = `Too many requests. Please wait ${seconds} seconds before trying again.`;
// Display message to user
displayNotification(message, 'warning');
}
Comprehensive Error Handler
Here’s a complete error handling function:Copy
class APIErrorHandler {
constructor() {
this.retryAttempts = 0;
this.maxRetries = 3;
}
async handleResponse(response, requestData = null) {
if (response.ok) {
return await response.json();
}
switch (response.status) {
case 400:
return this.handleValidationError(response);
case 401:
return this.handleAuthError(response);
case 403:
return this.handleAccessError(response);
case 404:
return this.handleNotFoundError(response);
case 429:
return this.handleRateLimitError(response);
case 500:
return this.handleServerError(response);
default:
return this.handleUnknownError(response);
}
}
async handleValidationError(response) {
const errorData = await response.json();
if (errorData.errors) {
const fieldErrors = {};
errorData.errors.forEach(error => {
fieldErrors[error.field] = error.message;
});
throw new ValidationError('Validation failed', fieldErrors);
} else {
throw new APIError('Bad request', errorData.error || 'Invalid request data');
}
}
async handleAuthError(response) {
const errorData = await response.json();
// Clear invalid token
localStorage.removeItem('api_token');
// Redirect to login
window.location.href = '/login';
throw new AuthError('Authentication failed', errorData.error);
}
async handleAccessError(response) {
const errorData = await response.json();
// Show access denied message
this.showNotification('Access denied to this resource', 'error');
throw new AccessError('Access denied', errorData.error);
}
async handleNotFoundError(response) {
const errorData = await response.json();
// Show 404 message
this.showNotification('Resource not found', 'error');
throw new NotFoundError('Resource not found', errorData.error);
}
async handleRateLimitError(response) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
if (this.retryAttempts < this.maxRetries) {
this.retryAttempts++;
// Wait and retry
await this.delay(waitTime);
// Retry the request
return this.retryRequest();
} else {
this.showNotification('Rate limit exceeded. Please try again later.', 'error');
throw new RateLimitError('Rate limit exceeded');
}
}
async handleServerError(response) {
this.showNotification('Server error. Please try again later.', 'error');
throw new ServerError('Internal server error');
}
async handleUnknownError(response) {
this.showNotification('An unexpected error occurred.', 'error');
throw new APIError('Unknown error', `HTTP ${response.status}`);
}
showNotification(message, type = 'info') {
// Implement your notification system
console.log(`${type.toUpperCase()}: ${message}`);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Custom error classes
class APIError extends Error {
constructor(message, details = null) {
super(message);
this.name = 'APIError';
this.details = details;
}
}
class ValidationError extends APIError {
constructor(message, fieldErrors = {}) {
super(message);
this.name = 'ValidationError';
this.fieldErrors = fieldErrors;
}
}
class AuthError extends APIError {
constructor(message, details = null) {
super(message);
this.name = 'AuthError';
this.details = details;
}
}
class AccessError extends APIError {
constructor(message, details = null) {
super(message);
this.name = 'AccessError';
this.details = details;
}
}
class NotFoundError extends APIError {
constructor(message, details = null) {
super(message);
this.name = 'NotFoundError';
this.details = details;
}
}
class RateLimitError extends APIError {
constructor(message, details = null) {
super(message);
this.name = 'RateLimitError';
this.details = details;
}
}
class ServerError extends APIError {
constructor(message, details = null) {
super(message);
this.name = 'ServerError';
this.details = details;
}
}
Usage Example
Copy
const errorHandler = new APIErrorHandler();
async function createProduct(productData) {
try {
const response = await fetch('https://lightyshare.com/api/token-secured/product', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ product: productData })
});
const result = await errorHandler.handleResponse(response, productData);
return result;
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation errors
Object.entries(error.fieldErrors).forEach(([field, message]) => {
displayFieldError(field, message);
});
} else if (error instanceof AuthError) {
// Handle authentication errors
console.error('Authentication failed:', error.message);
} else {
// Handle other errors
console.error('API Error:', error.message);
}
throw error;
}
}
Best Practices
1. Always Handle Errors
Never ignore API errors - always implement proper error handling:Copy
// ❌ Don't do this
fetch('/api/product').then(response => response.json());
// ✅ Do this
fetch('/api/product')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
})
.catch(error => {
console.error('Request failed:', error);
handleError(error);
});
2. Provide User Feedback
Always inform users when errors occur:Copy
function showUserFriendlyError(error) {
let message = 'An error occurred. Please try again.';
if (error instanceof ValidationError) {
message = 'Please check your input and try again.';
} else if (error instanceof AuthError) {
message = 'Please log in again.';
} else if (error instanceof RateLimitError) {
message = 'Too many requests. Please wait a moment.';
}
displayNotification(message, 'error');
}
3. Implement Retry Logic
For transient errors, implement retry logic:Copy
async function retryRequest(fn, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error instanceof RateLimitError && attempt < maxRetries - 1) {
await delay(Math.pow(2, attempt) * 1000);
continue;
}
throw error;
}
}
}
4. Log Errors Appropriately
Log errors for debugging but don’t expose sensitive information:Copy
function logError(error, context = {}) {
const logData = {
timestamp: new Date().toISOString(),
error: error.name,
message: error.message,
context: {
...context,
// Don't log sensitive data like tokens
hasToken: !!context.token
}
};
console.error('API Error:', logData);
// Send to error tracking service
if (process.env.NODE_ENV === 'production') {
sendToErrorTracking(logData);
}
}
5. Graceful Degradation
Design your application to work even when some API calls fail:Copy
async function loadUserData() {
try {
const user = await fetchUser();
const products = await fetchProducts();
return { user, products };
} catch (error) {
// Show partial data if possible
if (error instanceof AuthError) {
return { user: null, products: [] };
}
// Show cached data if available
const cachedData = getCachedData();
if (cachedData) {
return cachedData;
}
throw error;
}
}