Skip to content

feat(api-service,js): ensure backwards compatibility for context prefs fixes NV-7072#9890

Merged
ChmaraX merged 2 commits intonextfrom
nv-7072-context-preferences-subscription-identifier-compatibility
Jan 23, 2026
Merged

feat(api-service,js): ensure backwards compatibility for context prefs fixes NV-7072#9890
ChmaraX merged 2 commits intonextfrom
nv-7072-context-preferences-subscription-identifier-compatibility

Conversation

@ChmaraX
Copy link
Contributor

@ChmaraX ChmaraX commented Jan 22, 2026

What changed? Why was the change needed?

Problem

Context preferences require contextKeys in both JWT and subscription identifier, but old clients only include them in JWT.

In more detail - https://linear.app/novu/issue/NV-7072/context-preferences-subscription-identifier-compatibility-issue

Why Context Must Be in the Identifier

  1. Uniqueness: Same subscriber can have multiple subscriptions to the same topic with different contexts
    • tk_updates:si_user123:ctx_project:a
    • tk_updates:si_user123:ctx_project:b
  2. Client cache key: Client uses identifier to cache and manage subscriptions locally
  3. API operations: GET/PATCH/DELETE use identifier as the lookup key

The Mismatch

sequenceDiagram
    participant Old Client
    participant API
    participant Database

    Note over Old Client: @novu/js < 3.13.0
    
    Old Client->>API: POST /subscriptions<br/>identifier: "tk_updates:si_user123"<br/>JWT contains: contextKeys=["project:a"]
    
    API->>API: Sees contextKeys in JWT<br/>Adds :ctx_ to identifier
    
    API->>Database: Store subscription<br/>identifier: "tk_updates:si_user123:ctx_project:a"
    
    Note over Old Client: Later: tries to fetch subscription
    
    Old Client->>API: GET /subscriptions/tk_updates:si_user123<br/>(auto-generated, no :ctx_)
    
    API->>Database: Find by identifier:<br/>"tk_updates:si_user123"
    
    Database-->>API: ❌ Not found
    API-->>Old Client: 404
Loading

Root cause: Old client generates identifier without :ctx_, but server adds it → permanent mismatch.

Solution

Context Compatibility Interceptor detects old clients and strips contextKeys from JWT before processing:

sequenceDiagram
    participant Old Client
    participant Interceptor
    participant API
    participant Database

    Old Client->>Interceptor: POST /subscriptions<br/>No Novu-Client-Version header<br/>JWT: contextKeys=["project:a"]
    
    Interceptor->>Interceptor: Check header<br/>Missing → old client
    
    Interceptor->>Interceptor: Strip contextKeys from session
    
    Interceptor->>API: Continue request<br/>contextKeys=undefined
    
    API->>Database: Store subscription<br/>identifier: "tk_updates:si_user123"<br/>(no :ctx_)
    
    Note over Old Client: Later: fetch works!
    
    Old Client->>API: GET /subscriptions/tk_updates:si_user123
    API->>Database: Find: "tk_updates:si_user123"
    Database-->>API: ✅ Found
    API-->>Old Client: 200 OK
Loading

Changes

  • Client: Added Novu-Client-Version header
  • API: Interceptor strips contextKeys for clients < 3.13.0 or missing header
  • Result: Client-generated identifier matches server-stored identifier

Deployment

⚠️ Deploy API before releasing @novu/js@3.13.0


@linear
Copy link

linear bot commented Jan 22, 2026

@netlify
Copy link

netlify bot commented Jan 22, 2026

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

Name Link
🔨 Latest commit ffc43f7
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/6972407908075a00088dcff5

@github-actions github-actions bot changed the title feat(api-service,js): ensure backwards compatibility for cotnext prefs feat(api-service,js): ensure backwards compatibility for cotnext prefs fixes NV-7072 Jan 22, 2026
'Novu-Client-Version': DEFAULT_CLIENT_VERSION,
'Content-Type': 'application/json',
'User-Agent': userAgent,
'User-Agent': userAgent, // Deprecated: Doesn't work in browsers, kept for backward compatibility
Copy link
Contributor Author

Choose a reason for hiding this comment

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

User-Agent is not a reliable header, its being stripped therefore I couldn't use it to check the @novu/js version, so I introduced new one.

https://linear.app/novu/issue/NV-7073/remove-user-agent-header-override-logic-from-novujs-and-all-other

Copy link
Contributor

Choose a reason for hiding this comment

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

@ChmaraX take a look at: #9894

Let's just remove it altogether. No need to keep it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do that in separate PR.

@ChmaraX ChmaraX changed the title feat(api-service,js): ensure backwards compatibility for cotnext prefs fixes NV-7072 feat(api-service,js): ensure backwards compatibility for context prefs fixes NV-7072 Jan 22, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

Walkthrough

The changes introduce client version tracking through a new HTTP header Novu-Client-Version across the codebase. A new interceptor, ContextCompatibilityInterceptor, is applied to the InboxTopicController to disable context for clients older than version 3.13.0. The HTTP client now sends the Novu-Client-Version header alongside requests and marks the userAgent option as deprecated for browser environments. Supporting constants and type definitions are added, and tests are updated to verify the presence of the new header in requests.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding backwards compatibility for context preferences by detecting old clients and disabling context, with ticket reference NV-7072.
Description check ✅ Passed The description comprehensively explains the problem, reasoning, solution, and changes. It relates directly to the changeset by documenting why context compatibility is needed and how the interceptor solves the identifier mismatch issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 a review from scopsy January 22, 2026 15:10
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 22, 2026

Open in StackBlitz

@novu/js

npm i https://pkg.pr.new/novuhq/novu/@novu/js@9890

@novu/nextjs

npm i https://pkg.pr.new/novuhq/novu/@novu/nextjs@9890

novu

npm i https://pkg.pr.new/novuhq/novu@9890

@novu/react

npm i https://pkg.pr.new/novuhq/novu/@novu/react@9890

@novu/react-native

npm i https://pkg.pr.new/novuhq/novu/@novu/react-native@9890

commit: ffc43f7

@ChmaraX ChmaraX merged commit 2b52bd4 into next Jan 23, 2026
34 of 35 checks passed
@ChmaraX ChmaraX deleted the nv-7072-context-preferences-subscription-identifier-compatibility branch January 23, 2026 11:44
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.

2 participants