# thinkrank/1.0.0/src/admin/utils/apiClient.js

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.0.0. 266 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/utils/apiClient.js
- Raw: https://pluginprobe.com/plugins/thinkrank/1.0.0/raw/src/admin/utils/apiClient.js
- Modified: 2025-08-10T07:57:44+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/utils/apiClient.js#L10-L20`.

```javascript
/**
 * API Client Utility
 * 
 * Standardized API client for ThinkRank admin interface.
 * Provides consistent API interaction patterns extracted from Site Identity
 * with proper error handling, request/response formatting, and caching.
 * 
 * @package ThinkRank
 * @since 1.0.0
 */

import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';

/**
 * Create API client for a specific tab/module
 * 
 * @param {string} basePath Base API path (e.g., 'site-identity', 'homepage-seo')
 * @param {Object} options Client configuration options
 * @param {boolean} options.enableCaching Enable response caching (default: false)
 * @param {number} options.cacheTimeout Cache timeout in milliseconds (default: 300000 - 5 minutes)
 * @param {boolean} options.enableRetry Enable automatic retry on failure (default: true)
 * @param {number} options.maxRetries Maximum retry attempts (default: 2)
 * 
 * @return {Object} API client methods
 */
export const createApiClient = (basePath, options = {}) => {
    const {
        enableCaching = false,
        cacheTimeout = 300000, // 5 minutes
        enableRetry = true,
        maxRetries = 2
    } = options;

    const apiBasePath = `/thinkrank/v1/${basePath}`;
    const cache = new Map();

    /**
     * Get cache key for request
     */
    const getCacheKey = (endpoint, method, data) => {
        return `${method}:${endpoint}:${JSON.stringify(data || {})}`;
    };

    /**
     * Check if cached response is valid
     */
    const isCacheValid = (cacheEntry) => {
        return cacheEntry && (Date.now() - cacheEntry.timestamp) < cacheTimeout;
    };

    /**
     * Enhanced error handling following Site Identity patterns
     */
    const handleApiError = (error, endpoint) => {
        console.error(`API Error [${basePath}${endpoint}]:`, error);
        
        let errorMessage = __('Request failed. Please try again.', 'thinkrank');
        
        if (error.message) {
            if (error.message.includes('API key')) {
                errorMessage = __('AI optimization requires an API key. Please configure your API key in ThinkRank settings.', 'thinkrank');
            } else if (error.message.includes('rate limit')) {
                errorMessage = __('Rate limit reached. Please try again in a few minutes.', 'thinkrank');
            } else if (error.message.includes('network')) {
                errorMessage = __('Network error. Please check your connection and try again.', 'thinkrank');
            } else if (error.message.includes('permission')) {
                errorMessage = __('Permission denied. Please check your user permissions.', 'thinkrank');
            }
        }
        
        return {
            success: false,
            error: errorMessage,
            originalError: error
        };
    };

    /**
     * Make API request with retry logic
     */
    const makeRequest = async (endpoint, options, retryCount = 0) => {
        try {
            const response = await apiFetch({
                path: `${apiBasePath}${endpoint}`,
                ...options
            });
            
            return response;
        } catch (error) {
            if (enableRetry && retryCount < maxRetries) {
                // Wait before retry (exponential backoff)
                const delay = Math.pow(2, retryCount) * 1000;
                await new Promise(resolve => setTimeout(resolve, delay));
                return makeRequest(endpoint, options, retryCount + 1);
            }
            
            throw error;
        }
    };

    /**
     * GET request
     */
    const get = async (endpoint, params = {}) => {
        const cacheKey = getCacheKey(endpoint, 'GET', params);
        
        // Check cache if enabled
        if (enableCaching && cache.has(cacheKey)) {
            const cacheEntry = cache.get(cacheKey);
            if (isCacheValid(cacheEntry)) {
                return cacheEntry.data;
            }
            cache.delete(cacheKey);
        }

        try {
            // Add query parameters if provided
            let fullEndpoint = endpoint;
            if (Object.keys(params).length > 0) {
                const queryString = new URLSearchParams(params).toString();
                fullEndpoint += `?${queryString}`;
            }

            const response = await makeRequest(fullEndpoint, {
                method: 'GET'
            });

            // Cache successful responses
            if (enableCaching && response.success) {
                cache.set(cacheKey, {
                    data: response,
                    timestamp: Date.now()
                });
            }

            return response;
        } catch (error) {
            return handleApiError(error, endpoint);
        }
    };

    /**
     * POST request
     */
    const post = async (endpoint, data = {}) => {
        try {
            const response = await makeRequest(endpoint, {
                method: 'POST',
                data
            });

            // Clear related cache entries on successful POST
            if (enableCaching && response.success) {
                // Clear cache entries that might be affected by this update
                for (const key of cache.keys()) {
                    if (key.includes(endpoint) || key.includes('GET:')) {
                        cache.delete(key);
                    }
                }
            }

            return response;
        } catch (error) {
            return handleApiError(error, endpoint);
        }
    };

    /**
     * PUT request
     */
    const put = async (endpoint, data = {}) => {
        try {
            const response = await makeRequest(endpoint, {
                method: 'PUT',
                data
            });

            // Clear related cache entries on successful PUT
            if (enableCaching && response.success) {
                for (const key of cache.keys()) {
                    if (key.includes(endpoint)) {
                        cache.delete(key);
                    }
                }
            }

            return response;
        } catch (error) {
            return handleApiError(error, endpoint);
        }
    };

    /**
     * DELETE request
     */
    const del = async (endpoint) => {
        try {
            const response = await makeRequest(endpoint, {
                method: 'DELETE'
            });

            // Clear related cache entries on successful DELETE
            if (enableCaching && response.success) {
                for (const key of cache.keys()) {
                    if (key.includes(endpoint)) {
                        cache.delete(key);
                    }
                }
            }

            return response;
        } catch (error) {
            return handleApiError(error, endpoint);
        }
    };

    /**
     * Clear cache
     */
    const clearCache = (pattern = null) => {
        if (!enableCaching) return;
        
        if (pattern) {
            for (const key of cache.keys()) {
                if (key.includes(pattern)) {
                    cache.delete(key);
                }
            }
        } else {
            cache.clear();
        }
    };

    /**
     * Get cache statistics
     */
    const getCacheStats = () => {
        if (!enableCaching) return null;
        
        return {
            size: cache.size,
            keys: Array.from(cache.keys())
        };
    };

    return {
        get,
        post,
        put,
        delete: del,
        clearCache,
        getCacheStats
    };
};

/**
 * Default API client for general use
 */
export const apiClient = createApiClient('', {
    enableCaching: true,
    enableRetry: true
});

export default createApiClient;

```
