13 KiB
Frontend Integration
Network setup
When testing from a phone or another device on the same LAN:
- Set
HOST=0.0.0.0 - Set
PUBLIC_BASE_URLto the machine IP, notlocalhost - Add frontend origins to
CORS_ORIGINS - Keep
GOOGLE_CALLBACK_URLon the same reachable host if Google auth is used
Example:
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, imageVariants, videoUrl, hlsUrl, audioUrl, thumbnailUrl, thumbnailVariants, 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:
{
"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
pageandlimit - Cursor-aware endpoints also return
nextCursor - Existing clients can keep reading the old top-level fields
sortOrder=asc|descis 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,sortOrdersortOrderdefaults todescsortByis 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=falseto widen the home feed to public discovery posts preferredPostType,followingOnly,radiusKm,includeSuggestions,suggestionInterval
GET /marketplace/homelistingsLimit,instrumentsLimit,repairShopsLimit,onlyActive
GET /usersq,isVerified,musicRole,experienceLevel,isPrivate,hasAvatar,sortBysortBy:createdAt,name,username,followersCount,postsCount
GET /users/discoverq,musicRole,experienceLevel,hasAvatarOnly,includeRoleBuckets,sortBy,sortOrder
GET /users/:id/profile-overview- returns
stats,contentCounts,tabs, andviewerState
- returns
GET /posts/user/:userIdvisibility,postType,q,hashtag,sortBysortBy:createdAt,updatedAt,likesCount,commentsCount,savesCount,shareCount,viewCount,playCount
GET /posts/reelsvisibility,authorId,q,sortBy
GET /marketplace/listingsq,minPrice,maxPrice,isActive,listingCategory,condition,instrumentType,sortBylistingCategory:musical_instrument,accessory,audio_gear,sheet_music,other
GET /marketplace/instrumentsq,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-shopsq,isActive,sortBysortBy:createdAt,updatedAt,name
GET /notificationsread,type,resourceType,sortOrder
GET /comments/post/:postIdpage,limit,sortBy,sortOrdersortBy:createdAt,top
POST /comments/:commentId/replies- JSON body:
content - the route
commentIdis the parent comment; clients do not sendpostId
- JSON body:
GET /comments/:commentId/repliespage,limit,sortBy,sortOrdersortBy:createdAt,top
PATCH /comments/:commentId- JSON body:
content,mentionUsernames
- JSON body:
Comment list items include Instagram-like UI fields:
repliesCountrepliesPreviewlikesCountlikedByMecanEditcanDeletereplyToUser
WebSocket auth
Social upgrades
Reposts and quote posts
POST /posts/:postId/repost- body
{ "content": "" }creates a plain repost - body
{ "content": "My take" }creates a quote post - repost items expose
repostOfPostId; quote items exposequoteOfPostId
- body
Reactions
Existing likes endpoints now accept reactionType:
likelovehahawowsadangry
Example:
{
"targetId": "...",
"targetType": "post",
"reactionType": "love"
}
GET /likes/status/:targetType/:targetId returns reactionType and reactionSummary.
Private follow requests
When targetUser.isPrivate=true, POST /follows/toggle creates a pending request instead of following immediately.
GET /follows/requestsPATCH /follows/requests/:requestId/approvePATCH /follows/requests/:requestId/reject
GET /follows/status/:targetUserId returns requested.
Reports
POST /reportstargetType:user,post,comment,listing,repair_shoptargetId,reason,details
GET /reports/meGET /reports/superadminPATCH /reports/superadmin/:reportId/status
Blocks
Global block endpoints:
POST /blocks/:targetUserIdPATCH /blocks/:targetUserId/unblockGET /blocks/status/:targetUserId
Blocked users are excluded from feed/trending/explore visibility.
Explore
GET /feed/explore- public discovery feed using the trending ranking path
- respects global block visibility
Both namespaces accept the JWT access token in one of these places:
auth.tokenAuthorizationheader asBearer <token>
Chat namespace
Namespace: /chat
Client emits:
join_conversation- payload:
{ "conversationId": "..." }
- payload:
send_message- payload matches
SendMessageDto
- payload matches
typing- payload:
{ "conversationId": "...", "isTyping": true }
- payload:
mark_seen- payload:
{ "messageId": "...", "conversationId": "..." }
- payload:
Server emits:
presence- payload:
{ "userId": "...", "online": true }
- payload:
joined_conversation- payload:
{ "conversationId": "..." }
- payload:
new_message- payload: message object
typing- payload:
{ "conversationId": "...", "userId": "...", "isTyping": true }
- payload:
message_seen- payload:
{ "messageId": "...", "userId": "..." }
- payload:
Notifications namespace
Namespace: /notifications
Server emits:
notification_created- payload: notification object
notifications_unread_count- payload:
{ "unreadCount": 3 }
- payload:
Notification types currently used:
likecommentfollowmessagesavesharemention
Mentions
Posts and comments support:
taggedUserIds: official tagged users by Mongo idmentionUsernames: 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 /postsPATCH /posts/:postIdPOST /posts/reels
Send media uploads as files:
imageFilesvideoFileaudioFile
Send link-based fields as text fields inside the same form-data payload:
imageUrlsvideoUrlaudioUrlthumbnailUrl
Array fields may be sent either as repeated form keys or JSON text:
taggedUserIdsmentionUsernamesimageUrlswaveformPeaks
Video optimization
When VIDEO_PROCESSING_ENABLED=true and ffmpeg is available on the server:
- uploaded post/reel videos are converted to optimized
mp4 +faststartis applied so playback begins faster on mobile/web- uploaded post/reel videos also produce an HLS playlist at
hlsUrlwhenVIDEO_PROCESSING_GENERATE_HLS=true - when
ffprobeis available,hlsUrlpoints to a master playlist with multiple renditions so HLS players can downgrade quality automatically on weak networks - local storage responses serve
mp4,m3u8,m4s, andtsfiles with explicit mediaContent-Typeheaders andAccept-Ranges: bytes - a thumbnail image is generated automatically if the client does not send
thumbnailUrl - generated thumbnails also expose
thumbnailVariants.lowUrl,thumbnailVariants.mediumUrl,thumbnailVariants.highUrl, andthumbnailVariants.originalUrl
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
hlsUrlfirst when present for adaptive/streaming playback - fall back to
videoUrlfor progressivemp4playback - for poster frames on weak networks, prefer
thumbnailVariants.lowUrlorthumbnailVariants.mediumUrl
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 explicitContent-Typeand long-lived immutableCache-Control - audio (
mp3,wav,m4a,aac,ogg) is served with explicitContent-Type, long-lived immutableCache-Control, andAccept-Ranges: bytes - uploaded post images are normalized into responsive variants and posts now include
imageVariants[]
Recommended client behavior:
- for audio, stream
audioUrldirectly and allow byte-range playback/resume imageUrlsnow default to feed-friendly optimized images for backward compatibility- on weak networks, prefer
imageVariants[index].lowUrl - on normal networks, use
imageUrls[index]orimageVariants[index].mediumUrl - on detail screens or zoom views, use
imageVariants[index].highUrlorimageVariants[index].originalUrl
Instagram-style social controls
Posts now support:
- carousel metadata through
imageItems[]withurl,caption,altText, andorder collaboratorIds[]on create/update- profile pinning through
PATCH /posts/:postId/pin-profileand/unpin-profile - archive/restore through
PATCH /posts/:postId/archiveand/restore-archive - per-post comment settings through
PATCH /posts/:postId/comment-settings
Comment settings:
commentsDisabledcommentsFollowersOnlycommentFilterKeywords[]
Comments now support:
PATCH /comments/:commentId/pinPATCH /comments/:commentId/unpin- hidden offensive/filter matches via
hiddenByFilter
Chat now supports:
- message media upload through
POST /chat/messages/upload - replies with
replyToMessageId - reactions through
PATCH /chat/messages/:messageId/reaction - delete-for-me through
PATCH /chat/messages/:messageId/delete-for-me
Reports now require fixed reason values:
spam,harassment,hate_speech,nudity,violence,scam,intellectual_property,self_harm,other
When a post/comment reaches multiple open reports, the backend can automatically flag it for moderation. User reports can include blockTarget: true to block the reported user immediately.
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, andsections
- frontend-friendly grouped payload with
Admin endpoints follow the same split:
POST /marketplace/admin/listingsGET /marketplace/admin/listings/mePATCH /marketplace/admin/listings/:idDELETE /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
conditionnew,like_new,used,refurbished
instrumentType- free-text type such as
oud,piano, orviolin
- free-text type such as
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
roleBucketscounts 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.followersCountstats.followingCountstats.postsCountstats.collaborationsCountcontentCounts.reelscontentCounts.audiocontentCounts.otherviewerState.following
Smoke / e2e
Run:
npm run test:e2e
The smoke suite covers health, auth verification, profile setup, image upload, post creation, feed retrieval, notifications, and comment mentions.