Proper error handling is essential for building robust integrations with the Lightyshare API. This guide covers all error types and best practices for handling them.

HTTP Status Codes

The Lightyshare API uses standard HTTP status codes to indicate the success or failure of requests:
Status CodeDescriptionMeaning
200OKRequest successful
201CreatedResource created successfully
400Bad RequestInvalid request data
401UnauthorizedMissing or invalid authentication
403ForbiddenAccess denied to resource
404Not FoundResource doesn’t exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error

Error Response Format

Most error responses follow this format:
{
  "success": false,
  "errors": [
    {
      "field": "field_name",
      "code": "ERROR_CODE",
      "message": "Human-readable error message"
    }
  ]
}

Authentication Errors (401)

Missing Token

{
  "error": "Invalid or missing token"
}

Invalid Token

{
  "error": "Invalid or missing token"
}

Handling Authentication Errors

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

{
  "success": false,
  "errors": [
    {
      "field": "title",
      "code": "REQUIRED",
      "message": "Title is required"
    },
    {
      "field": "rent",
      "code": "REQUIRED", 
      "message": "Rental price is required"
    }
  ]
}

Invalid Data Types

{
  "success": false,
  "errors": [
    {
      "field": "rent",
      "code": "INVALID_TYPE",
      "message": "Rental price must be a number"
    }
  ]
}

Value Constraints

{
  "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

{
  "success": false,
  "errors": [
    {
      "field": "title",
      "code": "MAX_LENGTH",
      "message": "Title must be 255 characters or less"
    }
  ]
}

Handling Validation Errors

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

{
  "error": "Access denied to this resource"
}

Handling Access Errors

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

{
  "error": "Resource not found"
}

Handling Not Found Errors

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

{
  "error": "Request limit exceeded"
}

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 60

Handling Rate Limit Errors

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:
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

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:
// ❌ 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:
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:
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:
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:
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;
  }
}