# API Client Complete documentation of the Axios-based API client and all available endpoints. ## Overview The API client is a centralized Axios instance with request/response interceptors for authentication and error handling. **Location:** `frontend/src/lib/api/` ``` lib/api/ ├── client.ts # Axios instance with interceptors ├── index.ts # Re-exports all API modules ├── activities.ts # Activity log endpoints ├── auth.ts # Authentication endpoints ├── domains.ts # Domain management ├── favorites.ts # User favorites ├── games.ts # Game search/details ├── github.ts # GitHub integration ├── jobs.ts # Background jobs ├── platforms.ts # Platform list ├── playTracking.ts # Play session tracking ├── playlists.ts # Playlist management ├── roles.ts # Role/permission management ├── ruffle.ts # Ruffle emulator settings ├── settings.ts # System settings ├── sharedPlaylists.ts # Shared playlist access ├── statistics.ts # User statistics ├── tags.ts # Tag list ├── updates.ts # Update checking ├── userPlaylists.ts # User playlist operations └── users.ts # User management ``` ## Client Configuration ```typescript const api = axios.create({ baseURL: '/api', headers: { 'Content-Type': 'application/json', }, withCredentials: true, // Enable cookie-based auth (refresh token) }); ``` **Important:** `withCredentials: true` enables automatic cookie handling. The backend sets the refresh token in an HTTP-only cookie during login, and the browser automatically includes it in requests. ## Interceptors ### Request Interceptor Automatically adds JWT token to all requests: ```typescript api.interceptors.request.use((config) => { const token = useAuthStore.getState().accessToken; if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); ``` ### Response Interceptor Handles 401 errors and automatic token refresh: ```typescript api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { // Refresh token is sent automatically via withCredentials (HTTP-only cookie) // No need to manually send refresh token const tokens = await authApi.refreshToken(); useAuthStore.getState().updateAccessToken(tokens.accessToken); originalRequest.headers.Authorization = `Bearer ${tokens.accessToken}`; return api(originalRequest); } catch (refreshError) { useAuthStore.getState().clearAuth(); return Promise.reject(refreshError); } } return Promise.reject(error); } ); ``` **Note:** The refresh token is stored in an HTTP-only cookie (sent automatically by the browser when `withCredentials: true`). The `refreshToken()` method takes no parameters and relies on the cookie being present. ## API Modules ### gamesApi ```typescript gamesApi.search(filters: GameFilters): Promise> gamesApi.getFilterOptions(): Promise gamesApi.getById(id: string): Promise gamesApi.getRelated(id: string, limit?: number): Promise gamesApi.getRandom(library?: string): Promise gamesApi.getLaunchData(id: string): Promise gamesApi.downloadGame(id: string, gameDataId?: number) gamesApi.cancelDownload(id: string) ``` ### authApi ```typescript authApi.login(credentials: LoginCredentials): Promise authApi.register(userData: RegisterData): Promise authApi.logout(): Promise authApi.refreshToken(): Promise authApi.getMe(): Promise ``` **Note:** `logout()` and `refreshToken()` no longer take parameters. The refresh token is automatically sent via HTTP-only cookie when `withCredentials: true` is set on the axios instance. ### playlistsApi ```typescript playlistsApi.getAll(): Promise playlistsApi.getById(id: string): Promise playlistsApi.create(playlist): Promise playlistsApi.addGames(playlistId: string, gameIds: string[]): Promise playlistsApi.removeGames(playlistId: string, gameIds: string[]): Promise playlistsApi.delete(playlistId: string): Promise ``` ### usersApi ```typescript usersApi.getAll(page?: number, limit?: number): Promise usersApi.getById(id: number): Promise usersApi.create(userData: CreateUserData): Promise usersApi.update(id: number, userData: UpdateUserData): Promise usersApi.delete(id: number): Promise usersApi.changePassword(id: number, passwordData: ChangePasswordData) usersApi.getThemeSettings(): Promise<{ mode: string; primaryColor: string }> usersApi.updateThemeSettings(mode: string, primaryColor: string) ``` ### rolesApi ```typescript rolesApi.getAll(): Promise rolesApi.getById(id: number): Promise rolesApi.getPermissions(): Promise rolesApi.create(roleData: CreateRoleData): Promise rolesApi.update(id: number, roleData: UpdateRoleData): Promise rolesApi.updatePermissions(id: number, permissionIds: number[]): Promise rolesApi.delete(id: number): Promise ``` ### playTrackingApi ```typescript playTrackingApi.startSession(gameId: string, gameTitle: string): Promise playTrackingApi.endSession(sessionId: string): Promise playTrackingApi.getStats(): Promise playTrackingApi.getGameStats(limit?: number, offset?: number) playTrackingApi.getHistory(limit?: number, offset?: number) playTrackingApi.getTopGames(limit?: number): Promise playTrackingApi.getActivityOverTime(days?: number): Promise playTrackingApi.getGamesDistribution(limit?: number): Promise ``` ### domainsApi ```typescript domainsApi.getAll(): Promise domainsApi.add(hostname: string): Promise domainsApi.delete(id: number): Promise domainsApi.setDefault(id: number): Promise ``` ## Usage with Custom Hooks All API calls should use custom hooks wrapping TanStack Query: ```typescript // Instead of calling API directly const games = await gamesApi.search(filters); // Use custom hook const { data: games, isLoading, error } = useGames(filters); ``` ## Error Handling ```typescript const { data, isLoading, error } = useGames(filters); if (error) { // Handle error toast.error('Failed to load games'); } ``` ## Type Safety All API responses are fully typed: ```typescript interface Game { id: string; title: string; platform: string; developer: string; publisher: string; releaseDate: string; // ... more fields } interface PaginatedResult { games: T[]; total: number; page: number; limit: number; totalPages: number; } ``` ## Further Reading - [Backend API Documentation](../06-api-reference/README.md) - [Custom Hooks](./custom-hooks.md)