Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/zulfikarrosadi/juadah-backend/llms.txt

Use this file to discover all available pages before exploring further.

Overview

While the Juadah API does not currently implement rate limiting, following best practices will ensure optimal performance and prepare your application for future rate limit implementations.
Rate limiting is not currently enforced on the Juadah API. However, excessive or abusive usage may be subject to throttling or blocking without notice.

Best Practices

Request Optimization

Optimize your API requests to reduce unnecessary calls and improve performance.

Cache Responses

Cache API responses locally to minimize redundant requests

Use Pagination

Leverage pagination for large datasets instead of fetching all data

Batch Operations

Combine multiple operations when possible

Conditional Requests

Only fetch data when necessary

Caching Strategy

Implement intelligent caching to reduce API calls:
Recommendation: Cache product listings for 5-10 minutesProducts don’t change frequently, making them ideal for caching.
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
let productsCache = null;
let cacheTimestamp = 0;

async function getProducts() {
  const now = Date.now();
  
  if (productsCache && (now - cacheTimestamp) < CACHE_DURATION) {
    return productsCache;
  }
  
  const response = await fetch('https://juadah-backend.vercel.app/api/products', {
    credentials: 'include'
  });
  
  productsCache = await response.json();
  cacheTimestamp = now;
  
  return productsCache;
}

Pagination Usage

The Products API supports infinite scroll pagination to efficiently handle large datasets.
1

Initial Request

Fetch the first batch of products (50 items):
GET https://juadah-backend.vercel.app/api/products
Response:
{
  "status": "success",
  "data": {
    "meta": {
      "lastProductId": 50
    },
    "products": [
      // ... 50 products
    ]
  }
}
2

Load More

Use the lastProductId to fetch the next batch:
GET https://juadah-backend.vercel.app/api/products?last_id=50
This returns products 51-100.
3

Continue Pagination

Repeat with the new lastProductId until no more products are returned.
Implementation Example:
class ProductLoader {
  constructor() {
    this.lastProductId = null;
    this.hasMore = true;
  }
  
  async loadMore() {
    if (!this.hasMore) return [];
    
    const url = this.lastProductId
      ? `https://juadah-backend.vercel.app/api/products?last_id=${this.lastProductId}`
      : 'https://juadah-backend.vercel.app/api/products';
    
    const response = await fetch(url, {
      credentials: 'include'
    });
    
    const data = await response.json();
    
    if (data.status === 'success') {
      const { products, meta } = data.data;
      
      this.lastProductId = meta.lastProductId;
      this.hasMore = products.length === 50; // Assuming 50 per page
      
      return products;
    }
    
    return [];
  }
}

// Usage
const loader = new ProductLoader();
const firstBatch = await loader.loadMore();
const secondBatch = await loader.loadMore();

Connection Management

Manage HTTP connections efficiently:
Use persistent HTTP connections to reduce overhead.
// Good: Use a single session
const session = new Session();

async function makeRequest(endpoint) {
  return session.get(endpoint);
}
// Avoid: Creating new connections for each request
async function makeRequest(endpoint) {
  return fetch(endpoint); // New connection each time
}
Ensure cookies are included in all requests:
// Always include credentials for authenticated requests
fetch('https://juadah-backend.vercel.app/api/products', {
  credentials: 'include' // Important!
});
The API uses cookie-based authentication, so this is critical for maintaining your session.
Set reasonable timeouts to avoid hanging requests:
async function fetchWithTimeout(url, options = {}, timeout = 10000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(id);
    return response;
  } catch (error) {
    clearTimeout(id);
    if (error.name === 'AbortError') {
      throw new Error('Request timeout');
    }
    throw error;
  }
}

Error Handling & Retries

Implement smart retry logic for transient failures:
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      const data = await response.json();
      
      // Only retry on server errors (500+)
      if (data.status === 'fail' && data.errors.code >= 500) {
        throw new Error(data.errors.message);
      }
      
      return data;
    } catch (error) {
      lastError = error;
      
      // Don't retry on client errors (400-499)
      if (error.code && error.code < 500) {
        throw error;
      }
      
      // Wait before retrying (exponential backoff)
      if (i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }
  
  throw lastError;
}

// Usage
try {
  const data = await fetchWithRetry('https://juadah-backend.vercel.app/api/products', {
    credentials: 'include'
  });
  console.log(data);
} catch (error) {
  console.error('Request failed after retries:', error);
}

Performance Optimization

Token Refresh Strategy

The API uses short-lived access tokens (10 minutes) with automatic refresh capability.
1

Proactive Refresh

Refresh the access token before it expires:
const TOKEN_LIFETIME = 10 * 60 * 1000; // 10 minutes
const REFRESH_BEFORE = 2 * 60 * 1000; // Refresh 2 minutes before expiry

let tokenTimestamp = Date.now();

async function ensureValidToken() {
  const now = Date.now();
  const age = now - tokenTimestamp;
  
  if (age >= TOKEN_LIFETIME - REFRESH_BEFORE) {
    await refreshToken();
    tokenTimestamp = Date.now();
  }
}

async function refreshToken() {
  const response = await fetch('https://juadah-backend.vercel.app/api/refresh', {
    credentials: 'include'
  });
  return response.json();
}
2

Handle 401 Responses

If you receive a 401, refresh and retry:
async function makeAuthenticatedRequest(url, options = {}) {
  let response = await fetch(url, {
    ...options,
    credentials: 'include'
  });
  
  // If unauthorized, try refreshing token
  if (response.status === 401) {
    await refreshToken();
    
    // Retry original request
    response = await fetch(url, {
      ...options,
      credentials: 'include'
    });
  }
  
  return response.json();
}

Reduce Payload Size

Minimize the amount of data transferred:
For File Uploads:
  • Compress images before uploading
  • Respect the 5-image limit per product
  • Use supported formats only (PNG, JPG)
// Compress image before upload
async function compressImage(file, maxWidth = 1200) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ratio = Math.min(maxWidth / img.width, 1);
        
        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        
        canvas.toBlob(resolve, 'image/jpeg', 0.8);
      };
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });
}

Monitoring & Logging

Track your API usage to identify optimization opportunities:
class ApiMonitor {
  constructor() {
    this.requests = [];
  }
  
  logRequest(endpoint, method, duration, status) {
    this.requests.push({
      endpoint,
      method,
      duration,
      status,
      timestamp: Date.now()
    });
  }
  
  getStats(timeWindow = 60000) { // Last minute
    const now = Date.now();
    const recent = this.requests.filter(
      req => now - req.timestamp < timeWindow
    );
    
    return {
      totalRequests: recent.length,
      averageDuration: recent.reduce((sum, r) => sum + r.duration, 0) / recent.length,
      errorRate: recent.filter(r => r.status >= 400).length / recent.length,
      byEndpoint: this.groupByEndpoint(recent)
    };
  }
  
  groupByEndpoint(requests) {
    return requests.reduce((acc, req) => {
      if (!acc[req.endpoint]) {
        acc[req.endpoint] = 0;
      }
      acc[req.endpoint]++;
      return acc;
    }, {});
  }
}

// Usage
const monitor = new ApiMonitor();

async function monitoredFetch(url, options) {
  const start = Date.now();
  const response = await fetch(url, options);
  const duration = Date.now() - start;
  
  monitor.logRequest(url, options.method || 'GET', duration, response.status);
  
  return response;
}

// Check stats periodically
setInterval(() => {
  const stats = monitor.getStats();
  console.log('API Stats:', stats);
}, 60000);

Future Rate Limits

While not currently implemented, the API may introduce rate limits in the future:
Prepare for Future LimitsWhen rate limits are implemented, you can expect:
  • Per-user limits based on authentication
  • Response headers indicating limit status
  • 429 status code when limits are exceeded
  • Retry-After header for cooldown period

Expected Rate Limit Headers

When rate limiting is implemented, responses will likely include:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1234567890

Handling Rate Limits

Prepare your application to handle rate limits:
async function fetchWithRateLimitHandling(url, options) {
  const response = await fetch(url, options);
  
  // Check for rate limit
  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`);
    await new Promise(resolve => setTimeout(resolve, waitTime));
    
    // Retry request
    return fetchWithRateLimitHandling(url, options);
  }
  
  // Log remaining quota
  const remaining = response.headers.get('X-RateLimit-Remaining');
  if (remaining) {
    console.log(`API calls remaining: ${remaining}`);
  }
  
  return response;
}

Recommendations Summary

Cache Aggressively

Cache responses for 5-10 minutes to reduce redundant requests

Use Pagination

Load products in batches of 50 using the pagination API

Retry Server Errors

Implement exponential backoff for 500+ errors

Refresh Tokens Proactively

Refresh access tokens before they expire (10-minute lifetime)

Monitor Usage

Track request patterns to identify optimization opportunities

Handle Errors Gracefully

Don’t retry client errors (400-499), only server errors

Next Steps

Authentication

Learn about JWT authentication and token management

Error Handling

Handle API errors effectively

Products API

Explore the Products API with pagination

API Reference

Browse all available endpoints

Build docs developers (and LLMs) love