Skip to content

Conversation

ChmaraX
Copy link
Contributor

@ChmaraX ChmaraX commented Oct 10, 2025

What changed? Why was the change needed?

Screenshots

Expand for optional sections

Related enterprise PR

Special notes for your reviewer

Summary by CodeRabbit

  • New Features

    • Context-aware real-time updates for in-app notifications and unread/unseen counts.
    • Subscribers now join context-specific channels; messages can target specific contexts.
    • Default behavior remains unchanged when no context is provided.
  • Chores

    • Dependency submodule updated with no user-visible impact.

Copy link

linear bot commented Oct 10, 2025

@github-actions github-actions bot changed the title feat(ws): add context-based room routing for ws connections feat(ws): add context-based room routing for ws connections fixes NV-6790 Oct 10, 2025
@ChmaraX
Copy link
Contributor Author

ChmaraX commented Oct 10, 2025

@coderabbitai summary

Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

✅ Actions performed

Summary regeneration triggered.

@ChmaraX ChmaraX changed the title feat(ws): add context-based room routing for ws connections fixes NV-6790 feat(ws,worker,api): add context-based room routing for ws connections fixes NV-6790 Oct 10, 2025
Copy link

pkg-pr-new bot commented Oct 10, 2025

Open in StackBlitz

npm i https://pkg.pr.new/novuhq/novu@9341
npm i https://pkg.pr.new/novuhq/novu/@novu/providers@9341
npm i https://pkg.pr.new/novuhq/novu/@novu/shared@9341

commit: 556defd

Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Walkthrough

Adds optional contextKeys propagation across WebSocket payloads and routing. Updates DTOs, commands, services, and gateways to accept and forward contextKeys, enabling context-based room joins and targeted emits. Refactors socket-worker and queue services to object-based sendMessage params. Includes a submodule pointer update.

Changes

Cohort / File(s) Summary of Changes
Submodule pointer
.../.source
Updated submodule commit reference only.
API Inbox use cases
apps/api/src/app/inbox/usecases/delete-all-notifications/delete-all-notifications.usecase.ts, .../delete-many-notifications/delete-many-notifications.usecase.ts, .../mark-many-notifications-as/mark-many-notifications-as.usecase.ts, .../mark-notifications-as-seen/mark-notifications-as-seen.usecase.ts, .../update-all-notifications/update-all-notifications.usecase.ts
Added contextKeys: command.contextKeys to WebSocket payloads for unread/unseen events.
Worker workflow use cases
apps/worker/src/app/workflow/usecases/process-unsnooze-job/process-unsnooze-job.usecase.ts, .../send-message/send-message-in-app.usecase.ts
Included contextKeys in emitted WebSocket data (from command or entity).
WS external route + worker
apps/ws/src/socket/services/web-socket.worker.ts, apps/ws/src/socket/usecases/external-services-route/external-services-route.command.ts, apps/ws/src/socket/usecases/external-services-route/external-services-route.usecase.ts
Threaded contextKeys through worker payloads, command now has optional contextKeys?: string[]; updated use case to pass contextKeys to wsGateway.sendMessage and repository getCount.
WS gateway routing
apps/ws/src/socket/ws.gateway.ts
Added context-based rooms. Extended sendMessage(userId, event, data, contextKeys?); joins default room when no contextKeys, or per-key rooms when present; emits accordingly.
Generic DTOs and queue/service plumbing
libs/application-generic/src/dtos/web-sockets-job.dto.ts, libs/application-generic/src/services/queues/web-sockets-queue.service.ts, libs/application-generic/src/services/socket-worker/socket-worker.service.ts
Added contextKeys?: string[] to DTO. Refactored queue and socket-worker to object-based sendMessage params; propagated contextKeys through send, unread, and unseen count methods.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant API/Worker as API/Worker Usecases
  participant SocketSvc as SocketWorkerService
  participant WSQueue as WebSocketsQueueService
  participant WSProc as WebSocket Worker
  participant WSGW as WS Gateway
  participant Rooms as Context Rooms

  API/Worker->>SocketSvc: sendMessage({ userId, event, data, contextKeys? })
  SocketSvc->>WSQueue: add({ userId, event, data, contextKeys?, ... })
  WSQueue-->>WSProc: job({ userId, event, data, contextKeys? })
  WSProc->>WSGW: sendMessage(userId, event, data, contextKeys?)
  Note over WSGW: On connect, subscribers join default<br/>or per-context rooms based on contextKeys
  alt contextKeys provided
    loop for each key
      WSGW->>Rooms: emit to room userId:contextKey
    end
  else no contextKeys
    WSGW->>Rooms: emit to room userId:__default__
  end

  rect rgba(240,248,255,0.6)
  Note over SocketSvc,WSGW: Unread/Unseen count paths pass contextKeys to<br/>repository getCount and to WSGateway.sendMessage
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

@novu/dal

Suggested reviewers

  • scopsy
  • djabarovgeorge

Poem

A rabbit taps the socket line—hop hop, keys in tow,
Context trails like clover paths where messages now go.
Rooms bloom per tiny key, defaults when none appear,
Unread counts nibble neatly, unseen edges clear.
With packets packed and burrows mapped,
We ship, we squeak—delivered, wrapped. 🐇📬

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nv-6790-ws-context-isolated-event-emitting

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 588683c and c1708b1.

📒 Files selected for processing (15)
  • .source (1 hunks)
  • apps/api/src/app/inbox/usecases/delete-all-notifications/delete-all-notifications.usecase.ts (1 hunks)
  • apps/api/src/app/inbox/usecases/delete-many-notifications/delete-many-notifications.usecase.ts (1 hunks)
  • apps/api/src/app/inbox/usecases/mark-many-notifications-as/mark-many-notifications-as.usecase.ts (1 hunks)
  • apps/api/src/app/inbox/usecases/mark-notifications-as-seen/mark-notifications-as-seen.usecase.ts (1 hunks)
  • apps/api/src/app/inbox/usecases/update-all-notifications/update-all-notifications.usecase.ts (1 hunks)
  • apps/worker/src/app/workflow/usecases/process-unsnooze-job/process-unsnooze-job.usecase.ts (1 hunks)
  • apps/worker/src/app/workflow/usecases/send-message/send-message-in-app.usecase.ts (1 hunks)
  • apps/ws/src/socket/services/web-socket.worker.ts (1 hunks)
  • apps/ws/src/socket/usecases/external-services-route/external-services-route.command.ts (1 hunks)
  • apps/ws/src/socket/usecases/external-services-route/external-services-route.usecase.ts (4 hunks)
  • apps/ws/src/socket/ws.gateway.ts (2 hunks)
  • libs/application-generic/src/dtos/web-sockets-job.dto.ts (1 hunks)
  • libs/application-generic/src/services/queues/web-sockets-queue.service.ts (2 hunks)
  • libs/application-generic/src/services/socket-worker/socket-worker.service.ts (10 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ChmaraX ChmaraX requested review from LetItRock and scopsy October 10, 2025 13:55
Base automatically changed from nv-6659-inbox-filter-by-context to next October 13, 2025 08:36
Copy link

netlify bot commented Oct 13, 2025

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit d5c6eec
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/68ecc1a6edf97b0008062da2

Comment on lines +160 to +175
// Join context-specific rooms for efficient message routing
if (contextKeys.length === 0) {
// Join default room for connections without context
await connection.join(`${subscriber._id}:${DEFAULT_CONTEXT_ROOM}`);
Logger.log(`Connection ${connection.id} joined default room for ${subscriber._id}`, LOG_CONTEXT);
} else {
// Join a room for each context key
for (const contextKey of contextKeys) {
await connection.join(`${subscriber._id}:${contextKey}`);
}
Logger.log(
`Connection ${connection.id} joined context rooms for ${subscriber._id}: ${contextKeys.join(', ')}`,
LOG_CONTEXT
);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To route events only to inbox sessions with matching contexts, there are two possible approaches:

  1. Room-based routing (chosen): Each inbox session joins a context-specific room on connection. Events are emitted directly to the appropriate rooms.

  2. JWT-based filtering: Decode the JWT token for each connected client when sending an event, extract the contextKeys, and decide whether to emit.

I think the #1 approach is better for performance and scalability.

LOG_CONTEXT
);

await connection.join(subscriber._id);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also left the original subscriberId room for backwards compatibility, so after this change is rolled-out, the number of open rooms will double (until we remove this line), but that shouldn't be a problem, its just a server side grouping mechanism not a new WS connection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant