Files
sbd2-frontend/src/lib/api/README.md
JSC 6ce83c8317 Refactor API structure and integrate new modular API client
- Replaced legacy apiService with a new modular api client structure.
- Updated AuthContext, OAuthButtons, and AuthCallbackPage to use the new api client.
- Created separate services for auth, sounds, playlists, and users.
- Implemented centralized API configuration and error handling.
- Added support for OAuth providers and token exchange.
- Introduced a Toaster component for notifications in App.
- Updated API endpoints and request handling for better maintainability.
2025-07-26 19:21:36 +02:00

5.5 KiB

API Library

A generic, maintainable API client library for the Soundboard application.

Features

  • Generic HTTP Client: Supports all REST methods with proper TypeScript typing
  • Automatic Token Refresh: Handles JWT token refresh automatically
  • Error Handling: Comprehensive error classes for different scenarios
  • Modular Services: Separate services for different API domains
  • Configuration Management: Centralized API configuration
  • Backward Compatibility: Legacy API service for existing code

Usage

import { api } from '@/lib/api'

// Authentication
const user = await api.auth.login({ email: 'user@example.com', password: 'password' })
const currentUser = await api.auth.getMe()
await api.auth.logout()

// Sounds
const sounds = await api.sounds.list({ page: 1, size: 20 })
const sound = await api.sounds.get(1)
const newSound = await api.sounds.create({ title: 'My Sound', file: audioFile })

// Playlists
const playlists = await api.playlists.list()
const playlist = await api.playlists.create({ name: 'My Playlist' })

// Users (admin only)
const users = await api.users.list()

Alternative Import Style

import { authService, soundsService } from '@/lib/api'

// Direct service imports
const user = await authService.login(credentials)
const sounds = await soundsService.list()

Direct Client Usage

import { apiClient } from '@/lib/api'

// Generic HTTP requests
const data = await apiClient.get<MyType>('/api/v1/custom-endpoint')
const result = await apiClient.post<ResponseType>('/api/v1/data', requestData)

Services

AuthService (api.auth)

  • login(credentials) - Authenticate user
  • register(userData) - Register new user
  • getMe() - Get current user
  • logout() - Sign out user
  • getOAuthUrl(provider) - Get OAuth authorization URL
  • getOAuthProviders() - Get available OAuth providers
  • exchangeOAuthToken(request) - Exchange OAuth code for auth cookies

SoundsService (api.sounds)

  • list(params?) - Get paginated sounds
  • get(id) - Get specific sound
  • create(data) - Create new sound
  • update(id, data) - Update sound
  • delete(id) - Delete sound
  • upload(file, metadata?) - Upload sound file

PlaylistsService (api.playlists)

  • list(params?) - Get paginated playlists
  • get(id) - Get specific playlist
  • create(data) - Create new playlist
  • update(id, data) - Update playlist
  • delete(id) - Delete playlist
  • addSound(playlistId, soundData) - Add sound to playlist
  • removeSound(playlistId, soundId) - Remove sound from playlist

UsersService (api.users)

  • list(params?) - Get paginated users (admin only)
  • get(id) - Get specific user
  • update(id, data) - Update user
  • delete(id) - Delete user (admin only)
  • changePassword(userId, data) - Change user password
  • uploadAvatar(userId, file) - Upload user avatar

Error Handling

The library provides specific error classes for different scenarios:

import { 
  ApiError, 
  NetworkError, 
  TimeoutError, 
  ValidationError,
  AuthenticationError,
  AuthorizationError,
  NotFoundError,
  ServerError 
} from '@/lib/api'

try {
  await api.auth.login(credentials)
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Handle login failure
  } else if (error instanceof ValidationError) {
    // Handle validation errors
    console.log(error.fields) // Field-specific errors
  } else if (error instanceof NetworkError) {
    // Handle network issues
  }
}

Configuration

API configuration is centralized in config.ts:

export const API_CONFIG = {
  BASE_URL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000',
  TIMEOUT: 30000,
  RETRY_ATTEMPTS: 1,
  ENDPOINTS: {
    AUTH: { /* ... */ },
    SOUNDS: { /* ... */ },
    // ...
  }
}

Request Configuration

All API methods accept optional configuration:

// Custom timeout
await api.sounds.list({}, { timeout: 60000 })

// Skip authentication
await api.auth.getOAuthProviders({}, { skipAuth: true })

// Custom headers
await api.sounds.create(data, { 
  headers: { 'X-Custom-Header': 'value' }
})

// Query parameters
await api.sounds.list({}, { 
  params: { search: 'query', page: 1 }
})

File Structure

src/lib/api/
├── index.ts          # Main exports
├── client.ts         # Core HTTP client
├── config.ts         # API configuration
├── types.ts          # TypeScript types
├── errors.ts         # Error classes
├── services/
│   ├── auth.ts       # Authentication service
│   ├── sounds.ts     # Sounds service
│   ├── playlists.ts  # Playlists service
│   └── users.ts      # Users service
└── README.md         # This file

Migration Guide

If migrating from an older API structure:

  1. Use the main API object:

    import { api } from '@/lib/api'
    
    // Authentication
    const user = await api.auth.login(credentials)
    
    // Resources
    const sounds = await api.sounds.list()
    const playlists = await api.playlists.list()
    
  2. Or import services directly:

    import { authService, soundsService } from '@/lib/api'
    
    const user = await authService.login(credentials)
    const sounds = await soundsService.list()
    
  3. For custom requests, use the client:

    import { apiClient } from '@/lib/api'
    
    const data = await apiClient.get<MyType>('/api/v1/custom-endpoint')