Files
frontend-svelte/FIX_SUMMARY.md
Ilia Mashkov c06aad1a8a fix: Correct dynamic import paths in fallback function
- Use  path aliases instead of relative paths
- Fixes module resolution errors when importing from other files
- Ensures fallback to Fontshare API works correctly
2026-01-29 15:23:59 +03:00

13 KiB

Fix Applied: Query Data Cannot Be Undefined

Summary

Successfully fixed the TanStack Query error: "Query data cannot be undefined. Please make sure to return a value other than undefined from your query function."

Root Causes

  1. Missing gcTime parameter in TanStack Query configuration
  2. No response validation when proxy API returns invalid/missing data
  3. Incorrect generic type constraint in FontVirtualList.svelte

Changes Applied

1. Fixed TanStack Query Configuration

File: src/entities/Font/model/store/baseFontStore.svelte.ts

Added gcTime parameter to properly manage cached data lifecycle:

private getOptions(params = this.params): QueryObserverOptions<UnifiedFont[], Error> {
    return {
        queryKey: this.getQueryKey(params),
        queryFn: () => this.fetchFn(params),
        staleTime:5 * 60 *1000,  // 5 minutes
        gcTime: 10 * 60 * 1000,      // 10 minutes ✅ ADDED
    };
}

Impact:

  • Prevents stale data from persisting too long
  • Explicit control over garbage collection timing
  • Better memory management

2. Added Response Validation

File: src/entities/Font/model/store/unifiedFontStore.svelte.ts

Added comprehensive validation in fetchFn:

protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
    const response = await fetchProxyFonts(params);

    // Validate response exists
    if (!response) {
        console.error('[UnifiedFontStore] fetchProxyFonts returned undefined', { params });
        throw new Error('Proxy API returned undefined response');
    }

    // Validate fonts array exists
    if (!response.fonts) {
        console.error('[UnifiedFontStore] response.fonts is undefined', { response });
        throw new Error('Proxy API response missing fonts array');
    }

    // Validate fonts is an array
    if (!Array.isArray(response.fonts)) {
        console.error('[UnifiedFontStore] response.fonts is not an array', { fonts: response.fonts });
        throw new Error('Proxy API fonts is not an array');
    }

    // Store pagination metadata separately for derived values
    this.#paginationMetadata = {
        total: response.total ?? 0,
        limit: response.limit ?? this.params.limit ?? 50,
        offset: response.offset ?? this.params.offset ?? 0,
    };

    return response.fonts;
}

Impact:

  • Early detection of invalid API responses
  • Detailed error logging for debugging
  • Prevents undefined data from being cached
  • Fallback to default values for pagination metadata

3. Added Fallback to Fontshare API

File: src/entities/Font/api/proxy/proxyFonts.ts

Implemented automatic fallback to Fontshare API when proxy fails:

/**
 * Whether to use proxy API (true) or fallback (false)
 *
 * Set to true when your proxy API is ready:
 *   const USE_PROXY_API = true;
 *
 * Set to false to use Fontshare API as fallback during development:
 *   const USE_PROXY_API = false;
 *
 * The app will automatically fall back to Fontshare API if the proxy fails.
 */
const USE_PROXY_API = true;

export async function fetchProxyFonts(
    params: ProxyFontsParams = {},
): Promise<ProxyFontsResponse> {
    // Try proxy API first if enabled
    if (USE_PROXY_API) {
        try {
            const queryString = buildQueryString(params);
            const url = `${PROXY_API_URL}${queryString}`;

            console.log('[fetchProxyFonts] Fetching from proxy API', { params, url });

            const response = await api.get<ProxyFontsResponse>(url);

            // Validate response has fonts array
            if (!response.data || !Array.isArray(response.data.fonts)) {
                console.error('[fetchProxyFonts] Invalid response from proxy API', response.data);
                throw new Error('Proxy API returned invalid response');
            }

            console.log('[fetchProxyFonts] Proxy API success', {
                count: response.data.fonts.length,
            });
            return response.data;
        } catch (error) {
            console.warn('[fetchProxyFonts] Proxy API failed, using fallback', error);

            // Check if it's a network error or proxy not available
            const isNetworkError = error instanceof Error
                && (error.message.includes('Failed to fetch')
                    || error.message.includes('Network')
                    || error.message.includes('404')
                    || error.message.includes('500'));

            if (isNetworkError) {
                // Fall back to Fontshare API
                console.log('[fetchProxyFonts] Using Fontshare API as fallback');
                return await fetchFontshareFallback(params);
            }

            // Re-throw other errors
            if (error instanceof Error) {
                throw error;
            }
            throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`);
        }
    }

    // Use Fontshare API directly
    console.log('[fetchProxyFonts] Using Fontshare API (proxy disabled)');
    return await fetchFontshareFallback(params);
}

/**
 * Fallback to Fontshare API when proxy is unavailable
 */
async function fetchFontshareFallback(
    params: ProxyFontsParams,
): Promise<ProxyFontsResponse> {
    // Import dynamically to avoid circular dependency
    const { fetchFontshareFonts } = await import('../fontshare/fontshare');
    const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');

    // Map proxy params to Fontshare params
    const fontshareParams = {
        q: params.q,
        categories: params.category ? [params.category] : undefined,
        page: params.offset ? Math.floor(params.offset / (params.limit || 50)) + 1 : undefined,
        limit: params.limit,
    };

    const response = await fetchFontshareFonts(fontshareParams);
    const normalizedFonts = normalizeFontshareFonts(response.fonts);

    return {
        fonts: normalizedFonts,
        total: response.count_total,
        limit: params.limit || response.count,
        offset: params.offset || 0,
    };
}

Impact:

  • App continues working even if proxy API is down
  • Allows development without breaking functionality
  • Automatic detection of network errors
  • Seamless fallback with console logging

4. Fixed Type Constraints

File: src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte

Fixed incorrect generic type constraint:

Before:

<script lang="ts" generics="T extends ({ id: string } | [{ id: string }, { id: string; }])">

After:

<script lang="ts" generics="T extends { id: string }">

Also simplified the font registration logic:

Before:

const slugs = visibleItems.map(item => {
    if (Array.isArray(item)) {
        return item.map(font => font.id);
    }
    return item.id;
}).flat();

After:

const slugs = visibleItems.map(item => item.id);

Impact:

  • Fixed TypeScript errors
  • Simplified code
  • Proper type safety

How It Works

Normal Flow (Proxy API Available)

  1. Request: Component requests fonts → Filter manager maps to params → unifiedFontStore.setParams()
  2. Fetch: unifiedFontStore.fetchFn() calls fetchProxyFonts()
  3. Proxy API: Request sent to https://api.glyphdiff.com/api/v1/fonts
  4. Validation: Response validated to ensure fonts array exists
  5. Cache: TanStack Query caches response for 5 minutes
  6. Render: Components render unifiedFontStore.fonts

Fallback Flow (Proxy API Unavailable)

  1. Request: Same as normal flow
  2. Fetch: fetchProxyFonts() attempts proxy API
  3. Error: Proxy API returns network error (404, 500, connection failed)
  4. Detection: Error caught and classified as network error
  5. Fallback: fetchFontshareFallback() called automatically
  6. Fontshare API: Request sent to Fontshare API
  7. Normalization: Fontshare response normalized to UnifiedFont format
  8. Render: Components render fonts seamlessly

Debug Flow (Enable Proxy API)

With USE_PROXY_API = true, console will show:

[fetchProxyFonts] Fetching from proxy API { params: {...}, url: "https://api.glyphdiff.com/api/v1/fonts?..." }
[fetchProxyFonts] Proxy API success { count: 50 }

Debug Flow (Disable Proxy API)

With USE_PROXY_API = false, console will show:

[fetchProxyFonts] Using Fontshare API (proxy disabled)

Debug Flow (Proxy API Fails)

When proxy API fails, console will show:

[fetchProxyFonts] Fetching from proxy API { params: {...}, url: "https://api.glyphdiff.com/api/v1/fonts?..." }
[fetchProxyFonts] Proxy API failed, using fallback Error: ...
[fetchProxyFonts] Using Fontshare API as fallback

Testing the Fix

1. With Proxy API Working

Prerequisites:

  • Your proxy API is running at https://api.glyphdiff.com/api/v1/fonts
  • Proxy API returns correct response structure

Test:

# Start dev server
yarn dev

# Open browser console
# Should see: "[fetchProxyFonts] Fetching from proxy API"
# Should see: "[fetchProxyFonts] Proxy API success { count: N }"
# Should see fonts loading correctly

Expected Behavior:

  • Fonts load from proxy API
  • Console shows successful fetch
  • No errors in console
  • All features working

2. With Proxy API Down

Prerequisites:

  • Proxy API not running or returning errors

Test:

# Start dev server
yarn dev

# Open browser console
# Should see: "[fetchProxyFonts] Proxy API failed, using fallback"
# Should see: "[fetchProxyFonts] Using Fontshare API as fallback"
# Should see fonts loading from Fontshare

Expected Behavior:

  • App falls back to Fontshare API automatically
  • Console shows fallback messages
  • Fonts load from Fontshare
  • All features working (no user-facing errors)

3. Disable Proxy API Manually

File: src/entities/Font/api/proxy/proxyFonts.ts

const USE_PROXY_API = false;

Test:

# Start dev server
yarn dev

# Open browser console
# Should see: "[fetchProxyFonts] Using Fontshare API (proxy disabled)"
# Should see fonts loading from Fontshare

Expected Behavior:

  • App uses Fontshare API directly
  • Console shows proxy disabled message
  • All features working

Next Steps

To Enable Proxy API (When Ready)

  1. Verify Proxy API:

    curl "https://api.glyphdiff.com/api/v1/fonts?limit=5"
    
  2. Check Response Format:

    {
      "fonts": [...],
      "total": N,
      "limit": 5,
      "offset": 0
    }
    
  3. Ensure Each Font Has Required Fields:

    • id, name, provider
    • category, subsets
    • variants, styles
    • metadata, features
  4. Test in App:

    • Open browser console
    • Check for success messages
    • Verify fonts load correctly

To Remove Fallback (When Proxy API is Stable)

Once proxy API is proven stable, you can:

  1. Remove Fallback Function:

    // Delete fetchFontshareFallback() function
    
  2. Remove Dynamic Imports:

    // No longer need dynamic imports
    const { fetchFontshareFonts } = await import('../fontshare/fontshare');
    const { normalizeFontshareFonts } = await import('../../lib/normalize/normalize');
    
  3. Remove Old API Exports:

    // Remove Fontshare API exports from index.ts
    // Remove normalization function exports
    

Troubleshooting

Issue: Still Seeing "Query data cannot be undefined"

Cause: Proxy API returns invalid response

Solutions:

  1. Check console for validation errors
  2. Verify proxy API response structure
  3. Check network tab in DevTools for actual response
  4. Set USE_PROXY_API = false to use fallback

Issue: Fonts Not Loading At All

Cause: Both proxy and Fontshare APIs failing

Solutions:

  1. Check console for error messages
  2. Verify network connectivity
  3. Check CORS settings on proxy API
  4. Check API endpoint URL is correct

Issue: Fallback Not Triggering

Cause: Error type not recognized as network error

Solutions:

  1. Add additional error message checks
  2. Check console for actual error message
  3. Manually set USE_PROXY_API = false

Files Modified

  1. src/entities/Font/model/store/baseFontStore.svelte.ts - Added gcTime parameter
  2. src/entities/Font/model/store/unifiedFontStore.svelte.ts - Added response validation
  3. src/entities/Font/api/proxy/proxyFonts.ts - Added fallback logic
  4. src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte - Fixed generic type

Commit

commit 471e186
fix: Fix undefined query data and add fallback to Fontshare API

- Add gcTime parameter to TanStack Query config
- Add response validation in fetchFn with detailed logging
- Add fallback to Fontshare API when proxy fails
- Add USE_PROXY_API flag for easy switching
- Fix FontVirtualList generic type constraint
- Simplify font registration logic
- Add comprehensive console logging for debugging

Fixes: Query data cannot be undefined error

Documentation

See PROXY_API_FIXES.md for detailed technical documentation on all changes.


Last Updated: January 29, 2026 Status: Fixed and Committed