- 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.
5.5 KiB
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
New API (Recommended)
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 userregister(userData)- Register new usergetMe()- Get current userlogout()- Sign out usergetOAuthUrl(provider)- Get OAuth authorization URLgetOAuthProviders()- Get available OAuth providersexchangeOAuthToken(request)- Exchange OAuth code for auth cookies
SoundsService (api.sounds)
list(params?)- Get paginated soundsget(id)- Get specific soundcreate(data)- Create new soundupdate(id, data)- Update sounddelete(id)- Delete soundupload(file, metadata?)- Upload sound file
PlaylistsService (api.playlists)
list(params?)- Get paginated playlistsget(id)- Get specific playlistcreate(data)- Create new playlistupdate(id, data)- Update playlistdelete(id)- Delete playlistaddSound(playlistId, soundData)- Add sound to playlistremoveSound(playlistId, soundId)- Remove sound from playlist
UsersService (api.users)
list(params?)- Get paginated users (admin only)get(id)- Get specific userupdate(id, data)- Update userdelete(id)- Delete user (admin only)changePassword(userId, data)- Change user passworduploadAvatar(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:
-
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() -
Or import services directly:
import { authService, soundsService } from '@/lib/api' const user = await authService.login(credentials) const sounds = await soundsService.list() -
For custom requests, use the client:
import { apiClient } from '@/lib/api' const data = await apiClient.get<MyType>('/api/v1/custom-endpoint')