UNPKG

echofi-client

Version:

A TypeScript/Node.js client library for EchoFi services with Level 3 Ultimate API and gRPC-Web clients for music streaming, user management, activity tracking, and authentication.

677 lines (529 loc) โ€ข 17.6 kB
# echofi-client A TypeScript/Node.js client library for EchoFi services with **Level 3 Ultimate API**, gRPC-Web, and WebSocket clients for music streaming, user management, activity tracking, and real-time communication. ## ๐Ÿš€ New in v2.4.0: Level 3 Ultimate API **67% code reduction** with the new simplified JSON parameter syntax: ```typescript // Before (Level 2) const request = Helpers.Music.createListArtistsRequest({ offset: 0, limit: 20 }); const response = await client.music.listArtists(request); // After (Level 3) โœจ const response = await client.listArtists({ offset: 0, limit: 20 }); ``` **34 enhanced methods** across all services with smart type detection and full backward compatibility! ## ๐Ÿ“ฆ Installation ```bash npm install echofi-client ``` ## ๐Ÿš€ Proto-Generated Client This client uses generated code directly from your proto files, ensuring it's always in sync with your service definitions. Simply run `npm run update` after updating your proto files! ## Features - ๐ŸŽต **Music Service**: Artists, albums, tracks, and genres management - ๐Ÿ‘ค **User Service**: User management and operations - ๐Ÿ“Š **Activity Service**: Listening history, favorites, playlists, and point rates - ๐Ÿ“ˆ **Stats Service**: User statistics, points, and boost management - ๐Ÿ”Œ **WebSocket Client**: Real-time listening sessions and point updates - ๐Ÿค– **gRPC-Web Client**: Browser-compatible with automatic buffer conversion - ๐Ÿ›ก๏ธ **Error Handling**: Comprehensive error handling and logging - ๐Ÿ“ **TypeScript Ready**: Full TypeScript support with generated types - ๐ŸŒ **Browser Ready**: Works in all modern browsers with automatic protobuf serialization - ๐Ÿš€ **Simple Setup**: Unified client for all environments ## Installation ```bash # Clone the repository git clone <repository-url> cd client # Install dependencies npm install ``` ## ๐Ÿ”ง Quick Setup ### Database and Backend Setup 1. **Initialize the database** (from the backend directory): ```bash cd backend make init-db make load-sample-data ``` 2. **Start the backend server**: ```bash cd backend make run # Or with environment variables: DB_HOST=localhost DB_PORT=5432 DB_USER=testing DB_PASSWORD=suckupyourmouth DB_NAME=echofi2 ./server ``` The backend will start: - **gRPC Server**: `localhost:50051` (for Node.js clients) - **gRPC-Web Server**: `localhost:8080` (for browser clients) - **WebSocket Server**: `localhost:8080/ws` (for real-time updates) ### Client Setup 1. **Install dependencies:** ```bash npm install ``` 2. **Update proto files (if needed):** ```bash npm run update ``` 3. **Test the client:** ```bash npm test ``` 4. **Run examples:** ```bash # Basic usage npm run example # Comprehensive demo npm run example:comprehensive ``` ## ๐Ÿค– Proto Update Workflow When you update your proto files, simply run: ```bash npm run update ``` This will: - ๐Ÿ“„ Generate new JavaScript code from protos - ๐Ÿงช Run tests to verify everything works **Available update commands:** ```bash npm run update # Generate proto + test npm run update-proto # Force proto regeneration npm run generate-proto # Generate proto files with change detection ``` ## Quick Start ### ๐Ÿค– gRPC Client ```javascript const { GrpcClient } = require('echofi-client'); async function main() { // Create and initialize gRPC client const client = new GrpcClient({ host: 'localhost', port: 50051 }); await client.initialize(); // Use the services with proto method names const user = await client.user.callAsync('getUser', { id: 'user123' }); const artists = await client.music.callAsync('listArtists', { offset: 0, limit: 10 }); const stats = await client.stats.callAsync('getUserStatistics', { user_id: 'user123' }); // Always close when done client.close(); } main().catch(console.error); ``` ### ๐Ÿ”Œ WebSocket Client ```javascript const { WebSocketClient } = require('echofi-client'); async function main() { // Create WebSocket client for real-time features const wsClient = new WebSocketClient({ url: 'ws://localhost:8080/ws', autoReconnect: true }); // Setup event listeners wsClient.on('point_update', (pointData) => { console.log(`Earned ${pointData.points_earned} points!`); }); wsClient.on('session_update', (sessionData) => { console.log('Session updated:', sessionData); }); // Connect with authentication await wsClient.connect('your-auth-token'); // Start listening session await wsClient.startListeningSession(123); // Always disconnect when done wsClient.disconnect(); } main().catch(console.error); ``` ### ๐ŸŒ Using Both Clients Together ```javascript const { GrpcClient, WebSocketClient } = require('echofi-client'); async function main() { // Create both clients const grpcClient = new GrpcClient({ host: 'localhost', port: 50051 }); const wsClient = new WebSocketClient({ url: 'ws://localhost:8080/ws' }); // Initialize gRPC client await grpcClient.initialize(); // Connect WebSocket for real-time updates await wsClient.connect('your-auth-token'); // Use gRPC for data operations const user = await grpcClient.user.callAsync('getUser', { id: 'user123' }); // Use WebSocket for real-time features await wsClient.startListeningSession(123); // Cleanup grpcClient.close(); wsClient.disconnect(); } main().catch(console.error); ``` ## Configuration ### gRPC Client Configuration ```javascript const { GrpcClient } = require('echofi-client'); // Basic configuration const client = new GrpcClient({ host: 'localhost', // Server host port: 50051, // Server port }); // Advanced configuration with SSL const grpc = require('@grpc/grpc-js'); const client = new GrpcClient({ host: 'your-server.com', port: 443, credentials: grpc.credentials.createSsl(), // Use SSL in production options: { 'grpc.keepalive_time_ms': 30000, 'grpc.keepalive_timeout_ms': 5000, 'grpc.keepalive_permit_without_calls': true } }); ``` ### WebSocket Client Configuration ```javascript const { WebSocketClient } = require('echofi-client'); // Basic configuration const wsClient = new WebSocketClient({ url: 'ws://localhost:8080/ws', autoReconnect: true, reconnectInterval: 5000, maxReconnectAttempts: 10 }); // Advanced configuration const wsClient = new WebSocketClient({ url: 'wss://your-server.com/ws', // Use WSS in production autoReconnect: true, reconnectInterval: 3000, maxReconnectAttempts: 5, heartbeatInterval: 30000 }); ``` ## Services Overview ### User Service ```javascript // Get user by ID const user = await client.user.callAsync('getUser', { id: 'user123' }); // List users with pagination const users = await client.user.callAsync('listUsers', { offset: 0, limit: 10 }); ``` ### Music Service ```javascript // Get artist information const artist = await client.music.callAsync('getArtist', { artist_id: 1 }); // List tracks with filters const tracks = await client.music.callAsync('listTracks', { offset: 0, limit: 20, artist_id: 1, genre_id: 5 }); // List artists const artists = await client.music.callAsync('listArtists', { offset: 0, limit: 10 }); ``` ### Activity Service ```javascript // Get user's listening history const history = await client.activity.callAsync('listListeningHistory', { offset: 0, limit: 50, user_id: 'user123' }); // Get user favorites const favorites = await client.activity.callAsync('listUserFavorites', { offset: 0, limit: 10, user_id: 'user123' }); // Get user playlists const playlists = await client.activity.callAsync('listPlaylists', { offset: 0, limit: 10, created_by: 'user123', is_public: true }); // Get playlist tracks const playlistTracks = await client.activity.callAsync('listPlaylistTracks', { playlist_id: 1, offset: 0, limit: 10 }); ``` ### Stats Service ```javascript // Get user statistics const stats = await client.stats.callAsync('getUserStatistics', { user_id: 'user123' }); // Get user points const points = await client.stats.callAsync('getUserPoints', { user_id: 'user123' }); // Get point boosts const boosts = await client.stats.callAsync('listPointBoosts', { offset: 0, limit: 10, user_id: 'user123', is_active: true }); ``` ## ๐Ÿ”Œ WebSocket Real-time Features The WebSocket client provides real-time communication for listening sessions and point updates: ```javascript const { WebSocketClient } = require('echofi-client'); const wsClient = new WebSocketClient({ url: 'ws://localhost:8080/ws', autoReconnect: true }); // Setup event listeners wsClient.on('point_update', (pointData) => { console.log(`Earned ${pointData.points_earned} points!`); updatePointsDisplay(pointData.total_points); }); wsClient.on('session_update', (sessionData) => { console.log('Session status:', sessionData.status); updateSessionDisplay(sessionData); }); wsClient.on('connected', () => { console.log('WebSocket connected'); }); wsClient.on('disconnected', (data) => { console.log('WebSocket disconnected:', data); }); // Connect and start session await wsClient.connect('your-auth-token'); await wsClient.startListeningSession(trackId); ``` ## ๐Ÿ” Authentication The EchoFi backend uses NextAuth.js session tokens for authentication. Here's how to integrate with your frontend: ### Getting Session Tokens ```javascript // In your Next.js API route or server component import { getServerSession } from "next-auth/next" import { authOptions } from "@/lib/auth" const session = await getServerSession(authOptions) const sessionToken = session?.sessionToken ``` ### Making Authenticated Requests ```javascript const { GrpcClient } = require('echofi-client'); const client = new GrpcClient(); await client.initialize(); // Pass session token in metadata for protected endpoints const user = await client.user.callAsync('GetUser', { id: '' }, // Empty ID returns authenticated user's data { authorization: sessionToken } ); // Public endpoints don't require authentication const artists = await client.music.callAsync('ListArtists', { offset: 0, limit: 10 }); ``` ### Authentication Helper Class ```javascript class EchoFiAuthenticatedClient { constructor(config = {}) { this.client = new GrpcClient(config); this.sessionToken = null; } async initialize() { await this.client.initialize(); } setSessionToken(token) { this.sessionToken = token; } // Helper for authenticated requests async authenticatedCall(service, method, request = {}) { if (!this.sessionToken) { throw new Error('No session token provided'); } return await this.client[service].callAsync(method, request, { authorization: this.sessionToken }); } // Helper for public requests async publicCall(service, method, request = {}) { return await this.client[service].callAsync(method, request); } } ``` ### Error Handling ```javascript try { const user = await client.user.callAsync('GetUser', { id: '' }, { authorization: sessionToken } ); } catch (error) { if (error.code === 16) { // UNAUTHENTICATED // Redirect to login or refresh session console.log('Authentication required'); } else { console.error('Request failed:', error.message); } } ``` ### Protected vs Public Endpoints **Protected Endpoints (require authentication):** - `user.UserService/GetUser` - `activity.ActivityService/*` (listening sessions, favorites, playlists) - `stats.StatsService/*` (user statistics, points) **Public Endpoints (no authentication required):** - `music.MusicService/*` (artists, albums, tracks, genres) ## Examples ### Available Examples ```bash npm run example # Basic gRPC usage npm run example:frontend # Using both gRPC and WebSocket clients npm run example:websocket # WebSocket-only example npm test # Test gRPC client ``` ### Example Files - `examples/basic-usage.js` - Basic gRPC client usage - `examples/frontend-usage.js` - Using both clients together - `examples/websocket-example.js` - WebSocket client features - `examples/test-client.js` - Client testing and validation ### Testing ```bash npm test ``` ## API Reference ### GrpcClient #### Constructor ```javascript new GrpcClient(config) ``` **Parameters:** - `config.host` (string): Server hostname (default: 'localhost') - `config.port` (number): Server port (default: 8080) - `config.credentials` (grpc.Credentials): gRPC credentials (default: insecure) - `config.options` (object): Additional gRPC options #### Methods - `initialize()`: Initialize all service connections - `close()`: Close all connections - `isInitialized()`: Check if client is initialized ### WebSocketClient #### Constructor ```javascript new WebSocketClient(config) ``` **Parameters:** - `config.url` (string): WebSocket URL (default: 'ws://localhost:8080/ws') - `config.autoReconnect` (boolean): Enable auto-reconnection (default: true) - `config.reconnectInterval` (number): Reconnection interval in ms (default: 5000) - `config.maxReconnectAttempts` (number): Max reconnection attempts (default: 10) - `config.heartbeatInterval` (number): Heartbeat interval in ms (default: 30000) #### Methods - `connect(token)`: Connect to WebSocket server with auth token - `disconnect()`: Disconnect from server - `startListeningSession(trackId)`: Start a listening session - `pauseListeningSession()`: Pause current session - `endListeningSession()`: End current session - `sendHeartbeat()`: Send manual heartbeat - `getConnectionStatus()`: Get current connection status #### Events - `connected`: WebSocket connected - `disconnected`: WebSocket disconnected - `error`: Connection or protocol error - `point_update`: Real-time point updates - `session_update`: Listening session updates - `heartbeat`: Heartbeat received - `server_error`: Server-side error ### Service Methods All service methods return Promises and support the following patterns: #### Pagination Most list methods support pagination: ```javascript { offset: 0, // Starting position limit: 10 // Number of items to return } ``` #### Filtering Many methods support filtering options: ```javascript // Activity service client.activity.callAsync('listListeningHistory', { user_id: 'user123', offset: 0, limit: 50 }); // Music service client.music.callAsync('listTracks', { artist_id: 1, album_id: 2, genre_id: 3, offset: 0, limit: 20 }); ``` ## Error Handling The client includes comprehensive error handling: ```javascript try { const user = await client.user.callAsync('getUser', { id: 'nonexistent' }); } catch (error) { console.error('Error:', error.message); // Handle specific gRPC errors if (error.code === grpc.status.NOT_FOUND) { console.log('User not found'); } } ``` ## Development ### Generate Proto Files ```bash npm run generate-proto ``` ### Available Scripts ```bash # Proto update commands npm run update # Generate proto + test npm run update-proto # Force regenerate proto files npm run generate-proto # Generate proto files with change detection # Example commands npm run example # Basic gRPC usage example npm run example:frontend # Frontend client with WebSocket npm run example:websocket # WebSocket-only example npm run example:advanced # Advanced workflows npm run example:comprehensive # All features demo npm run example:errors # Error handling patterns # Testing npm test # Test client functionality ``` ### Project Structure ``` โ”œโ”€โ”€ package.json # Project configuration โ”œโ”€โ”€ README.md # This file โ”œโ”€โ”€ LICENSE # MIT License โ”œโ”€โ”€ .gitignore # Git ignore rules โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ client.js # Main gRPC client class โ”‚ โ”œโ”€โ”€ frontend-client.js # Enhanced frontend client with WebSocket โ”‚ โ”œโ”€โ”€ websocket-client.js # WebSocket-only client โ”‚ โ”œโ”€โ”€ react-hooks.js # React-like hooks for frontend integration โ”‚ โ”œโ”€โ”€ index.js # Main exports โ”‚ โ””โ”€โ”€ generated/ # Auto-generated proto files โ”œโ”€โ”€ examples/ โ”‚ โ”œโ”€โ”€ basic-usage.js # Basic gRPC usage examples โ”‚ โ”œโ”€โ”€ frontend-usage.js # Frontend client examples โ”‚ โ”œโ”€โ”€ websocket-example.js # WebSocket-only examples โ”‚ โ”œโ”€โ”€ advanced-usage.js # Advanced workflows โ”‚ โ”œโ”€โ”€ comprehensive-example.js # All features demo โ”‚ โ”œโ”€โ”€ error-handling.js # Error handling patterns โ”‚ โ””โ”€โ”€ test-client.js # Client testing โ”œโ”€โ”€ docs/ โ”‚ โ”œโ”€โ”€ FRONTEND_CLIENT_GUIDE.md # Complete frontend integration guide โ”‚ โ””โ”€โ”€ [other backend docs] # Backend documentation โ”œโ”€โ”€ proto/ # Protocol buffer definitions โ”‚ โ”œโ”€โ”€ activity/ # Activity service protos โ”‚ โ”œโ”€โ”€ music/ # Music service protos โ”‚ โ”œโ”€โ”€ stats/ # Stats service protos โ”‚ โ””โ”€โ”€ user/ # User service protos โ””โ”€โ”€ scripts/ โ””โ”€โ”€ generate-proto.js # Proto generation with change detection ``` ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## License This project is licensed under the MIT License - see the LICENSE file for details.