/** * Proxy API client * * Handles API requests to GlyphDiff proxy API for fetching font metadata. * Provides error handling, pagination support, and type-safe responses. * * Proxy API normalizes font data from Google Fonts and Fontshare into a single * unified format, eliminating the need for client-side normalization. * * @see https://api.glyphdiff.com/api/v1/fonts */ import { api } from '$shared/api/api'; import { buildQueryString } from '$shared/lib/utils'; import type { QueryParams } from '$shared/lib/utils'; import type { UnifiedFont } from '../../model/types'; import type { FontCategory, FontSubset, } from '../../model/types'; /** * Proxy API base URL */ const PROXY_API_URL = 'https://api.glyphdiff.com/api/v1/fonts' as const; /** * Proxy API parameters * * Maps directly to the proxy API query parameters */ export interface ProxyFontsParams extends QueryParams { /** * Font provider filter ("google" or "fontshare") * Omit to fetch from both providers */ provider?: 'google' | 'fontshare'; /** * Font category filter */ category?: FontCategory; /** * Character subset filter */ subset?: FontSubset; /** * Search query (e.g., "roboto", "satoshi") */ q?: string; /** * Sort order for results * "name" - Alphabetical by font name * "popularity" - Most popular first * "lastModified" - Recently updated first */ sort?: 'name' | 'popularity' | 'lastModified'; /** * Number of items to return (pagination) */ limit?: number; /** * Number of items to skip (pagination) * Use for pagination: offset = (page - 1) * limit */ offset?: number; } /** * Proxy API response * * Includes pagination metadata alongside font data */ export interface ProxyFontsResponse { /** Array of unified font objects */ fonts: UnifiedFont[]; /** Total number of fonts matching the query */ total: number; /** Limit used for this request */ limit: number; /** Offset used for this request */ offset: number; } /** * Fetch fonts from proxy API * * @param params - Query parameters for filtering and pagination * @returns Promise resolving to proxy API response * @throws ApiError when request fails * * @example * ```ts * // Fetch all sans-serif fonts from Google * const response = await fetchProxyFonts({ * provider: 'google', * category: 'sans-serif', * limit: 50, * offset: 0 * }); * * // Search fonts across all providers * const searchResponse = await fetchProxyFonts({ * q: 'roboto', * limit: 20 * }); * * // Fetch fonts with pagination * const page1 = await fetchProxyFonts({ limit: 50, offset: 0 }); * const page2 = await fetchProxyFonts({ limit: 50, offset: 50 }); * ``` */ export async function fetchProxyFonts( params: ProxyFontsParams = {}, ): Promise { const queryString = buildQueryString(params); const url = `${PROXY_API_URL}${queryString}`; try { const response = await api.get(url); return response.data; } catch (error) { // Re-throw ApiError with context if (error instanceof Error) { throw error; } throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`); } } /** * Fetch font by ID * * Convenience function for fetching a single font by ID * Note: This fetches a page and filters client-side, which is not ideal * For production, consider adding a dedicated endpoint to the proxy API * * @param id - Font ID (family name for Google, slug for Fontshare) * @returns Promise resolving to font or undefined * * @example * ```ts * const roboto = await fetchProxyFontById('Roboto'); * const satoshi = await fetchProxyFontById('satoshi'); * ``` */ export async function fetchProxyFontById( id: string, ): Promise { const response = await fetchProxyFonts({ limit: 1000, q: id }); return response.fonts.find(font => font.id === id); }