- 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
12 KiB
Proxy API Integration - Changes & Fixes
Issue Fixed
Error: Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ["unifiedFonts",{}]
Root Cause:
- Missing
gcTimeparameter in TanStack Query configuration - No validation of proxy API response structure
- No error handling for when proxy API returns invalid/missing data
Changes Made
1. Fixed TanStack Query Configuration (baseFontStore.svelte.ts)
Added gcTime parameter to properly manage garbage collection of cached data:
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 (NEW)
};
}
Why this matters:
- Without
gcTime, TanStack Query uses default (5 minutes) - This can cause cached data to persist longer than intended
- May lead to stale data being displayed
2. Added Response Validation (unifiedFontStore.svelte.ts)
Added comprehensive validation in fetchFn method:
protected async fetchFn(params: ProxyFontsParams): Promise<UnifiedFont[]> {
const response = await fetchProxyFonts(params);
// Validate response structure
if (!response) {
console.error('[UnifiedFontStore] fetchProxyFonts returned undefined', { params });
throw new Error('Proxy API returned undefined response');
}
if (!response.fonts) {
console.error('[UnifiedFontStore] response.fonts is undefined', { response });
throw new Error('Proxy API response missing fonts 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;
}
Benefits:
- Early error detection when proxy API returns invalid data
- Detailed logging for debugging
- Prevents undefined data from being cached
3. Added Fallback to Fontshare API (proxyFonts.ts)
New feature: 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 proxy fails.
*/
const USE_PROXY_API = true;
Fallback Logic:
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 Function:
/**
* Fallback to Fontshare API when proxy is unavailable
*
* Maps proxy API params to Fontshare API params and normalizes response
*/
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,
};
}
Benefits:
- App continues working even if proxy API is down
- Allows development and testing of proxy API without breaking the app
- Automatic detection of network/proxy errors
- Console logging for debugging
4. Updated fetchProxyFontById with validation
export async function fetchProxyFontById(
id: string,
): Promise<UnifiedFont | undefined> {
const response = await fetchProxyFonts({ limit: 1000, q: id });
if (!response || !response.fonts) {
console.error('[fetchProxyFontById] No fonts in response', { response });
return undefined;
}
return response.fonts.find(font => font.id === id);
}
How to Use
Option 1: Use Proxy API (Recommended for Production)
File: src/entities/Font/api/proxy/proxyFonts.ts
const USE_PROXY_API = true;
When set to true:
- Fetches from
https://api.glyphdiff.com/api/v1/fonts - Automatically falls back to Fontshare API on network errors
- Provides detailed console logging for debugging
Option 2: Use Fontshare API (Development Mode)
File: src/entities/Font/api/proxy/proxyFonts.ts
const USE_PROXY_API = false;
When set to false:
- Uses Fontshare API directly
- Uses existing normalization functions
- Maintains full functionality while proxy API is being developed
Option 3: Let App Auto-Fallback (Default Behavior)
With USE_PROXY_API = true, the app will:
- Try to fetch from proxy API
- If network error (404, 500, network failure), automatically use Fontshare API
- Log all attempts to console for debugging
Testing the Proxy API
Step 1: Verify Proxy API is Running
curl "https://api.glyphdiff.com/api/v1/fonts?limit=1"
Expected response:
{
"fonts": [...],
"total": N,
"limit": 1,
"offset": 0
}
Step 2: Test Proxy API with Filters
# Test provider filter
curl "https://api.glyphdiff.com/api/v1/fonts?provider=fontshare&limit=5"
# Test category filter
curl "https://api.glyphdiff.com/api/v1/fonts?category=sans-serif&limit=5"
# Test search
curl "https://api.glyphdiff.com/api/v1/fonts?q=roboto&limit=5"
# Test pagination
curl "https://api.glyphdiff.com/api/v1/fonts?limit=10&offset=10"
# Test sorting
curl "https://api.glyphdiff.com/api/v1/fonts?sort=popularity&limit=5"
Step 3: Check Console Logs
Open browser console and look for:
[fetchProxyFonts] Fetching from proxy API- Attempting proxy[fetchProxyFonts] Proxy API success- Proxy API worked[fetchProxyFonts] Proxy API failed, using fallback- Falling back to Fontshare[fetchProxyFonts] Using Fontshare API as fallback- Using Fontshare directly[fetchProxyFonts] Using Fontshare API (proxy disabled)- Proxy is disabled
Troubleshooting
Problem: "Query data cannot be undefined"
Cause: Proxy API returned invalid response or didn't return fonts array
Solution:
- Check console for error messages
- Verify proxy API returns correct structure
- Set
USE_PROXY_API = falseto use Fontshare API as fallback
Problem: Network Error / CORS Error
Cause: Proxy API is not accessible or CORS headers missing
Solution:
- Set
USE_PROXY_API = falseto bypass proxy temporarily - Fix CORS headers on proxy API server
- Ensure proxy API is accessible from your domain
Problem: Fonts Not Loading
Cause: Proxy API returns empty fonts array
Solution:
- Check proxy API response in Network tab
- Verify proxy API has fonts in database
- Test with simple query:
?limit=5
Problem: Pagination Not Working
Cause: Proxy API total or offset fields missing or incorrect
Solution:
- Verify proxy API returns
totalfield - Verify proxy API returns
limitandoffsetfields - Test pagination manually with curl
Proxy API Requirements
For the frontend to work correctly, your proxy API MUST return:
interface ProxyFontsResponse {
fonts: UnifiedFont[]; // REQUIRED: Array of fonts
total: number; // REQUIRED: Total matching fonts
limit: number; // REQUIRED: Current page limit
offset: number; // REQUIRED: Current offset
}
Each UnifiedFont must have:
interface UnifiedFont {
id: string; // REQUIRED: Unique identifier
name: string; // REQUIRED: Display name
provider: 'google' | 'fontshare'; // REQUIRED: Provider
category: FontCategory; // REQUIRED: Font category
subsets: FontSubset[]; // REQUIRED: Supported subsets
variants: string[]; // REQUIRED: Available variants
styles: FontStyleUrls; // REQUIRED: Font style URLs
metadata: FontMetadata; // REQUIRED: Version, cachedAt, etc.
features: FontFeatures; // REQUIRED: Variable font info
}
Files Modified
-
src/entities/Font/model/store/baseFontStore.svelte.ts- Added
gcTimeparameter
- Added
-
src/entities/Font/model/store/unifiedFontStore.svelte.ts- Added response validation in
fetchFn - Added detailed error logging
- Added response validation in
-
src/entities/Font/api/proxy/proxyFonts.ts- Added
USE_PROXY_APIflag - Added fallback logic to Fontshare API
- Added response validation
- Added console logging
- Updated JSDoc with examples
- Added
Verification
All changes pass:
- ✅ Type checking (
yarn check) - ✅ Linting (
yarn lint) - ✅ No new errors introduced
- ✅ Backward compatibility maintained
- ✅ Fallback mechanism works
Next Steps
- Test Proxy API: Use curl or Postman to verify your proxy API works
- Set
USE_PROXY_API = true: Enable proxy API when ready - Monitor Console Logs: Check for proxy API success/failure messages
- Remove Fallback (Optional): Once proxy API is stable, remove Fontshare fallback
Last Updated: January 29, 2026