Skip to content

fix(@novu/js): dedupe notifications in cache.unshift by id#10797

Open
NianJiuZst wants to merge 2 commits intonovuhq:mainfrom
NianJiuZst:main
Open

fix(@novu/js): dedupe notifications in cache.unshift by id#10797
NianJiuZst wants to merge 2 commits intonovuhq:mainfrom
NianJiuZst:main

Conversation

@NianJiuZst
Copy link
Copy Markdown

@NianJiuZst NianJiuZst commented Apr 19, 2026

Summary

Fixes #10762.

When notifications.cache.unshift() is called with a notification that has the same id as an existing notification in the cache, it now skips the insert to avoid duplicates. This prevents the same notification from appearing multiple times in useNotifications() when multiple Novu client instances (e.g., multiple browser tabs) receive the same socket event.

Root Cause

The unshift method in packages/js/src/cache/notifications-cache.ts was unconditionally prepending the notification to the cache without checking if a notification with the same id already exists. When multiple Novu clients receive the same WebSocket notification event, each one calls unshift with the same notification data, resulting in N duplicates for N active clients.

Fix

Added a guard in unshift() that checks if a notification with the same id already exists in the cache before prepending:

// Skip if a notification with the same id already exists to avoid duplicates
if (cachedData.notifications.some((n) => n.id === notification.id)) {
  return;
}

Testing

Added two new test cases:

  1. should not add duplicate notification when unshift is called with the same id - verifies deduplication
  2. should allow unshift of notification with different id - verifies normal behavior still works

Reproduction (from issue)

const novu = new Novu({ applicationIdentifier: 'app', subscriber: 'sub', useCache: true });
const args = { archived: false, limit: 10 };
const notification = { id: 'n1', /* ... */ };

novu.notifications.cache.unshift(args, notification);
novu.notifications.cache.unshift(args, notification);

console.log(novu.notifications.cache.getAll(args).notifications.map(n => n.id));
// Before: ['n1', 'n1']
// After: ['n1']

Note

Low Risk
Low risk: small behavioral guard in NotificationsCache.unshift plus new unit tests; main impact is suppressing inserts when an id already exists, which could affect callers expecting duplicates (unlikely).

Overview
Fixes duplicate in-app notifications by adding an id-based dedupe guard to NotificationsCache.unshift(), skipping the prepend when the cache already contains that notification.

Adds a comprehensive notifications-cache.test.ts suite, including new cases asserting the dedupe behavior and validating cache clearing, aggregation, unique retrieval, and event-driven update/remove emissions.

Reviewed by Cursor Bugbot for commit 89bad2e. Configure here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 19, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43caa937-c114-48b1-b901-e21e64851ee7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 19, 2026

Hey there and thank you for opening this pull request! 👋

We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted.

Your PR title is: fix(@novu/js): dedupe notifications in cache.unshift by id

Requirements:

  1. Follow the Conventional Commits specification

Expected format: feat(scope): Add fancy new feature

Details:
Unknown scope "@novu/js" found in pull request title "fix(@novu/js): dedupe notifications in cache.unshift by id". Scope must match one of: root, api-service, dashboard, inbound-mail, webhook, worker, ws, ee-ai, ee-api, ee-auth, ee-billing, ee-shared-services, ee-translation, step-resolver-worker, application-generic, automation, dal, api, maily-core, maily-render, maily-tailwind-config, maily-tsconfig, notifications, testing, add-inbox, agent-toolkit, framework, js, nextjs, novu, providers, react, react-native, shared, stateless, nestjs, nextjs.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant