# Frontend Integration ## Network setup When testing from a phone or another device on the same LAN: 1. Set `HOST=0.0.0.0` 2. Set `PUBLIC_BASE_URL` to the machine IP, not `localhost` 3. Add frontend origins to `CORS_ORIGINS` 4. Keep `GOOGLE_CALLBACK_URL` on the same reachable host if Google auth is used Example: ```env HOST=0.0.0.0 PUBLIC_BASE_URL=http://192.168.1.12:4000 CORS_ORIGINS=http://192.168.1.12:3000,http://192.168.1.12:5173 GOOGLE_CALLBACK_URL=http://192.168.1.12:4000/api/v1/auth/google/callback ``` With `PUBLIC_BASE_URL` configured, file fields such as `avatar`, `coverImage`, `imageUrls`, `videoUrl`, `hlsUrl`, `audioUrl`, `thumbnailUrl`, `mediaUrl`, and marketplace images are returned as absolute URLs. ## Pagination contract List endpoints now keep the legacy fields and also return a unified `pagination` object. Example: ```json { "items": [], "count": 0, "page": 1, "limit": 20, "total": 0, "totalPages": 1, "nextCursor": null, "pagination": { "mode": "offset", "page": 1, "limit": 20, "count": 0, "total": 0, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false, "nextPage": null, "previousPage": null, "currentCursor": null, "nextCursor": null } } ``` Notes: - Offset endpoints use `page` and `limit` - Cursor-aware endpoints also return `nextCursor` - Existing clients can keep reading the old top-level fields - `sortOrder=asc|desc` is available on paginated endpoints ## Filter and sorting contract Boolean query filters are parsed consistently now. Send: - `?isActive=true` - `?isActive=false` - `?read=true` - `?read=false` - `?followingOnly=true` Common conventions: - `page`, `limit`, `sortOrder` - `sortOrder` defaults to `desc` - `sortBy` is supported on selected endpoints with endpoint-specific allowed values Supported filters: - `GET /feed/me` - defaults to followed accounts and the viewer's own posts - use `followingOnly=false` to widen the home feed to public discovery posts - `preferredPostType`, `followingOnly`, `radiusKm`, `includeSuggestions`, `suggestionInterval` - `GET /marketplace/home` - `listingsLimit`, `instrumentsLimit`, `repairShopsLimit`, `onlyActive` - `GET /users` - `q`, `isVerified`, `musicRole`, `experienceLevel`, `isPrivate`, `hasAvatar`, `sortBy` - `sortBy`: `createdAt`, `name`, `username`, `followersCount`, `postsCount` - `GET /users/discover` - `q`, `musicRole`, `experienceLevel`, `hasAvatarOnly`, `includeRoleBuckets`, `sortBy`, `sortOrder` - `GET /users/:id/profile-overview` - returns `stats`, `contentCounts`, `tabs`, and `viewerState` - `GET /posts/user/:userId` - `visibility`, `postType`, `q`, `hashtag`, `sortBy` - `sortBy`: `createdAt`, `updatedAt`, `likesCount`, `commentsCount`, `savesCount`, `shareCount`, `viewCount`, `playCount` - `GET /posts/reels` - `visibility`, `authorId`, `q`, `sortBy` - `GET /marketplace/listings` - `q`, `minPrice`, `maxPrice`, `isActive`, `listingCategory`, `condition`, `instrumentType`, `sortBy` - `listingCategory`: `musical_instrument`, `accessory`, `audio_gear`, `sheet_music`, `other` - `GET /marketplace/instruments` - `q`, `minPrice`, `maxPrice`, `isActive`, `condition`, `instrumentType`, `sortBy` - this route is now a musical-instruments-only view of marketplace listings - `sortBy`: `createdAt`, `updatedAt`, `price`, `title` - `GET /marketplace/repair-shops` - `q`, `isActive`, `sortBy` - `sortBy`: `createdAt`, `updatedAt`, `name` - `GET /notifications` - `read`, `type`, `resourceType`, `sortOrder` - `GET /comments/post/:postId` - `page`, `limit`, `sortOrder` - `PATCH /comments/:commentId` - JSON body: `content`, `mentionUsernames` ## WebSocket auth Both namespaces accept the JWT access token in one of these places: - `auth.token` - `Authorization` header as `Bearer ` ## Chat namespace Namespace: `/chat` Client emits: - `join_conversation` - payload: `{ "conversationId": "..." }` - `send_message` - payload matches `SendMessageDto` - `typing` - payload: `{ "conversationId": "...", "isTyping": true }` - `mark_seen` - payload: `{ "messageId": "...", "conversationId": "..." }` Server emits: - `presence` - payload: `{ "userId": "...", "online": true }` - `joined_conversation` - payload: `{ "conversationId": "..." }` - `new_message` - payload: message object - `typing` - payload: `{ "conversationId": "...", "userId": "...", "isTyping": true }` - `message_seen` - payload: `{ "messageId": "...", "userId": "..." }` ## Notifications namespace Namespace: `/notifications` Server emits: - `notification_created` - payload: notification object - `notifications_unread_count` - payload: `{ "unreadCount": 3 }` Notification types currently used: - `like` - `comment` - `follow` - `message` - `save` - `share` - `mention` ## Mentions Posts and comments support: - `taggedUserIds`: official tagged users by Mongo id - `mentionUsernames`: usernames such as `["rami_sabry"]` The backend also extracts `@username` from `content` automatically and emits mention notifications for matched users. ## Posts Upload Contract Post creation and updates now require `multipart/form-data` for all post payloads, including text-only posts and posts that use external media links. - `POST /posts` - `PATCH /posts/:postId` - `POST /posts/reels` Send media uploads as files: - `imageFiles` - `videoFile` - `audioFile` Send link-based fields as text fields inside the same form-data payload: - `imageUrls` - `videoUrl` - `audioUrl` - `thumbnailUrl` Array fields may be sent either as repeated form keys or JSON text: - `taggedUserIds` - `mentionUsernames` - `imageUrls` - `waveformPeaks` ## Video optimization When `VIDEO_PROCESSING_ENABLED=true` and `ffmpeg` is available on the server: - uploaded post/reel videos are converted to optimized `mp4` - `+faststart` is applied so playback begins faster on mobile/web - uploaded post/reel videos also produce an HLS playlist at `hlsUrl` when `VIDEO_PROCESSING_GENERATE_HLS=true` - local storage responses serve `mp4`, `m3u8`, `m4s`, and `ts` files with explicit media `Content-Type` headers and `Accept-Ranges: bytes` - a thumbnail image is generated automatically if the client does not send `thumbnailUrl` If `ffmpeg` is not installed or video processing is disabled, uploads still work and the original video file is stored as-is. Recommended client behavior: - use `hlsUrl` first when present for adaptive/streaming playback - fall back to `videoUrl` for progressive `mp4` playback ## Audio and image delivery Managed uploads are stored under stable UUID-based paths, so local storage responses now send cache-friendly headers: - images (`jpg`, `jpeg`, `png`, `webp`, `gif`) are served with explicit `Content-Type` and long-lived immutable `Cache-Control` - audio (`mp3`, `wav`, `m4a`, `aac`, `ogg`) is served with explicit `Content-Type`, long-lived immutable `Cache-Control`, and `Accept-Ranges: bytes` Recommended client behavior: - for audio, stream `audioUrl` directly and allow byte-range playback/resume - for images, render `imageUrls` directly and rely on URL-based caching for repeat views ## Marketplace split Marketplace is now separated from musical instruments at the API contract level: - `GET /marketplace/listings` - general sale listings across all listing categories - `GET /marketplace/instruments` - only musical instruments - `GET /marketplace/repair-shops` - service providers and maintenance shops - `GET /marketplace/home` - frontend-friendly grouped payload with `categories`, `summary`, `filters.listingCategories`, `featuredShops`, and `sections` Admin endpoints follow the same split: - `POST /marketplace/admin/listings` - `GET /marketplace/admin/listings/me` - `PATCH /marketplace/admin/listings/:id` - `DELETE /marketplace/admin/listings/:id` The older `/marketplace/admin/instruments*` routes still exist and always force `listingCategory=musical_instrument` for backward compatibility. Marketplace shop ownership rule: - each admin can own one repair-shop / marketplace shop record only - if the admin already created one, the frontend should call update endpoints instead of create Listing responses now also include: - `shop` - `{ adminId, name, username, avatar }` - `storeName` - direct shortcut for list cards - `condition` - `new`, `like_new`, `used`, `refurbished` - `instrumentType` - free-text type such as `oud`, `piano`, or `violin` Search on marketplace listings now matches: - listing title - listing description - instrument type - shop name ## Talent discovery The talents screen can now be built from: - `GET /users/discover` - paginated talent cards - `roleBuckets` counts for tabs such as instrumentalists, singers, producers, and lyricists - `GET /users/:id/profile-overview` - summary for the profile header and tab counts `profile-overview` returns: - `stats.followersCount` - `stats.followingCount` - `stats.postsCount` - `stats.collaborationsCount` - `contentCounts.reels` - `contentCounts.audio` - `contentCounts.other` - `viewerState.following` ## Smoke / e2e Run: ```bash npm run test:e2e ``` The smoke suite covers health, auth verification, profile setup, image upload, post creation, feed retrieval, notifications, and comment mentions.