Skip to content

Messaging Module

Location: apps/api/src/modules/messaging/

Aggregate roots: Conversation, ConversationMember, Message, MessageReaction

The Messaging context provides DM and small-group conversations. A privacy gate ensures DMs can only be opened between users who share at least one circle. Real-time delivery is planned via Centrifugo; clients currently poll.


Prisma Models

ModelTableNotes
ConversationconversationsDM or group (kind='dm'|'group')
ConversationMemberconversation_membersMember row with role + lastReadAt
MessagemessagesBody text, append-only
MessageReactionmessage_reactionsEmoji reaction per (message, user, emoji)

Conversation Fields

FieldTypeNotes
idUUID PK
kindStringdm | group
titleString?Group title
createdAtDateTime
updatedAtDateTime

ConversationMember Fields

FieldTypeNotes
conversationIdUUID FK
userIdUUID FK
roleStringowner | member
joinedAtDateTime
lastReadAtDateTime?Tracks read receipts
mutedAtDateTime?Per-conversation mute

Message Fields

FieldTypeNotes
idUUID PK
conversationIdUUID FK
authorIdUUID FK
bodyStringMax 4,000 chars
createdAtDateTime

MessageReaction Fields

FieldTypeNotes
messageIdUUID FK
userIdUUID FK
emojiString1–8 graphemes (server-validated)
createdAtDateTime

PK: (messageId, userId, emoji)


Key Methods

MethodDescription
openDm(userId, otherUserId)Open or reuse a DM; privacy-gated to shared circles
createGroup(userId, title, memberIds)Create group conversation (max 50 members)
sendMessage(conversationId, userId, body)Post a message; emit messaging.message.posted
listConversations(userId)All conversations with last message + unread count
listMessages(conversationId, userId, opts)Paginated message history
markRead(conversationId, userId)Update lastReadAt
addMember(conversationId, userId, newMemberId)Add to group; emits messaging.member.added
removeMember(conversationId, userId, targetId)Remove from group
muteConversation(conversationId, userId, mute)Toggle per-conversation mute
addReaction(messageId, userId, emoji)React to a message
removeReaction(messageId, userId, emoji)Remove reaction

HTTP Endpoints

All under /api/v1/messaging/. Require JwtAuthGuard.

MethodPathDescription
GET/messaging/conversationsList my conversations
POST/messaging/conversations/dmOpen DM { otherUserId }
POST/messaging/conversations/groupCreate group { title, memberIds }
GET/messaging/conversations/:id/messagesMessage history
POST/messaging/conversations/:id/messagesSend message { body }
POST/messaging/conversations/:id/readMark read
POST/messaging/conversations/:id/muteMute/unmute { muted: bool }
POST/messaging/conversations/:id/membersAdd member (group)
DELETE/messaging/conversations/:id/members/:uidRemove member
POST/messaging/messages/:id/reactionsAdd reaction { emoji }
DELETE/messaging/messages/:id/reactions/:emojiRemove reaction

Constraints

RuleValue
Max group size50 members (MAX_GROUP_SIZE)
Max message length4,000 chars (MAX_MESSAGE_LENGTH)
DM privacy gateSender and recipient must share at least one circle
Muted conversationsNo push/in-app notifications when mutedAt is set

Events Emitted

EventPayloadConsumed by
messaging.conversation.created{ conversationId, kind, creatorId }(observability)
messaging.message.posted{ messageId, conversationId, authorId }Notifications
messaging.member.added{ conversationId, memberId, addedBy }Notifications

Regulus — invite-only social-knowledge platform