- 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.
214 lines
5.5 KiB
Markdown
214 lines
5.5 KiB
Markdown
# 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)
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
import { authService, soundsService } from '@/lib/api'
|
|
|
|
// Direct service imports
|
|
const user = await authService.login(credentials)
|
|
const sounds = await soundsService.list()
|
|
```
|
|
|
|
### Direct Client Usage
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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`:
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
// 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:**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
import { authService, soundsService } from '@/lib/api'
|
|
|
|
const user = await authService.login(credentials)
|
|
const sounds = await soundsService.list()
|
|
```
|
|
|
|
3. **For custom requests, use the client:**
|
|
```typescript
|
|
import { apiClient } from '@/lib/api'
|
|
|
|
const data = await apiClient.get<MyType>('/api/v1/custom-endpoint')
|
|
``` |