Skip to content

fix(client-js,react): support HeadersInit for custom client headers#15366

Open
aryanbarde80 wants to merge 2 commits intomastra-ai:mainfrom
aryanbarde80:codex/fix-client-header-init
Open

fix(client-js,react): support HeadersInit for custom client headers#15366
aryanbarde80 wants to merge 2 commits intomastra-ai:mainfrom
aryanbarde80:codex/fix-client-header-init

Conversation

@aryanbarde80
Copy link
Copy Markdown

@aryanbarde80 aryanbarde80 commented Apr 14, 2026

Description

Allow client SDK custom headers to use the full fetch HeadersInit shapes instead of only plain string maps.

This updates both typing and runtime behavior so callers can pass:

  • plain objects
  • Headers instances
  • header tuple arrays

It also fixes the React provider merge path so local dev header injection still works with non-object header inputs.

Related Issue(s)

Fixes the client SDK custom headers typing/runtime issue.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update

Checklist

  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works
  • I have addressed all Coderabbit comments on this PR

ELI5

The PR lets developers pass custom HTTP headers to the Mastra SDK in more flexible ways—not just as a simple list of pairs, but also as JavaScript objects or arrays. It also fixes a bug where the React version couldn't properly add its own dev headers when developers used these flexible formats.

Changes

Core Type Updates

  • Introduced ClientHeaders type alias equal to HeadersInit in client-sdks/client-js/src/types.ts
  • Updated ClientOptions.headers and RequestOptions.headers from Record<string, string> to ClientHeaders, enabling support for:
    • Plain objects
    • Headers instances
    • Header tuple arrays [key, value][]

Header Normalization

  • Added normalizeHeaders() helper function in client-sdks/client-js/src/resources/base.ts to convert any HeadersInit form into a Record<string, string>
  • Updated BaseResource.request() to use this helper when merging client-level and request-level headers
  • Preserved conditional content-type: application/json header setting for POST/PUT/PATCH/DELETE requests without FormData

React Provider Fix

  • Updated MastraClientProvider props type to accept ClientHeaders instead of Record<string, string>
  • Fixed the local dev header injection (x-mastra-dev-playground) to work with non-object header inputs by converting to Headers instance, then normalizing back to object form

Testing & Documentation

  • Added two new test cases validating:
    • BaseResource with Headers instance in client options
    • Tuple-array headers merged at request level
  • Updated README to document the expanded HeadersInit support for the headers configuration option
  • Added Changeset entry for patch-level releases

Impact

  • Backward compatible: Existing code using plain object headers continues to work unchanged
  • Bug fix: Resolves the React provider's inability to merge dev-injected headers with non-object header inputs
  • Enhanced flexibility: SDK users can now use standard Fetch API header patterns across their codebase

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 14, 2026

🦋 Changeset detected

Latest commit: ff0c3e1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@mastra/client-js Patch
@mastra/react Patch
@mastra/playground-ui Patch
@internal/playground Patch
mastra Patch
create-mastra Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dane-ai-mastra
Copy link
Copy Markdown
Contributor

Thank you for your contribution!

Please ensure that your PR fixes an existing issue and that you have linked it in the description (e.g. with Fixes #1234).

We use CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.

Addressing CodeRabbit's feedback will greatly increase the chances of your PR being merged. We appreciate your understanding and cooperation in helping us maintain high code quality standards.

Comment @coderabbitai review in case you want to trigger a review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

Walkthrough

Expands @mastra/client-js and @mastra/react to support HeadersInit (including Headers instances and tuples) for custom request headers instead of plain objects. Adds type definitions, header normalization logic, unit tests, and documentation updates.

Changes

Cohort / File(s) Summary
Changeset & Documentation
.changeset/slow-snails-breathe.md, client-sdks/client-js/README.md
Introduces changeset entry for patch releases and updates README to document expanded headers option now accepting HeadersInit forms.
Type System Updates
client-sdks/client-js/src/types.ts
Adds ClientHeaders type alias for HeadersInit and updates ClientOptions.headers and RequestOptions.headers from Record<string, string> to ClientHeaders.
Core Implementation
client-sdks/client-js/src/resources/base.ts
Introduces normalizeHeaders() helper to convert HeadersInit forms into Record<string, string>. Updates BaseResource.request() to conditionally set content-type: application/json and merge client-level and request-level headers using normalized values.
Unit Tests
client-sdks/client-js/src/resources/base.test.ts
Adds two test cases validating header handling with Headers instances and tuple-array headers, ensuring proper merging and request success.
React Provider Integration
client-sdks/react/src/mastra-client-context.tsx
Updates MastraClientProviderProps.headers type to ClientHeaders. Replaces direct object construction with normalized Headers instance for conditional x-mastra-dev-playground header injection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: expanding header support in client-js and react SDKs to accept HeadersInit instead of just Record<string, string>.

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

✨ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client-sdks/client-js/src/resources/base.ts`:
- Around line 18-20: The current Array.isArray(headers) branch uses
Object.fromEntries(headers) which drops duplicate header keys; instead construct
a Headers instance from the tuple array (new Headers(headers)) and use its
entries() to build the resulting plain object so duplicate names are combined
per the Fetch spec (comma+space). Update the branch that handles
Array.isArray(headers) to create a Headers from the tuples and then produce the
output object from Headers.entries() rather than calling Object.fromEntries on
the raw tuples; reference the Array.isArray(headers) check, Headers, and
Object.fromEntries when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4a2e8ad-1244-46c3-a256-e73e844c0a1e

📥 Commits

Reviewing files that changed from the base of the PR and between 274504c and ff0c3e1.

📒 Files selected for processing (6)
  • .changeset/slow-snails-breathe.md
  • client-sdks/client-js/README.md
  • client-sdks/client-js/src/resources/base.test.ts
  • client-sdks/client-js/src/resources/base.ts
  • client-sdks/client-js/src/types.ts
  • client-sdks/react/src/mastra-client-context.tsx

Comment on lines +18 to +20
if (Array.isArray(headers)) {
return Object.fromEntries(headers);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In the Fetch standard (or MDN), when constructing new Headers([['accept','application/json'], ['accept','text/plain']]), what does headers.get('accept') return, and how are duplicate tuple header names handled?

💡 Result:

When constructing new Headers([['accept','application/json'], ['accept','text/plain']]), headers.get('accept') returns "application/json, text/plain". Duplicate tuple header names are handled by treating the Headers object as a multimap that allows multiple values per name. The constructor processes the sequence of tuples by appending each (name, value) pair to the internal header list using the append algorithm, which adds each new value as a separate header entry. The get(name) method then returns all values for that name (case-insensitive match), joined by ", " (comma followed by space), in the order they were added. This behavior aligns with HTTP semantics where multiple headers with the same name are combined into a single comma-separated list for most headers (except Set-Cookie, which has special handling via getSetCookie). MDN confirms get returns all values as a single string when multiple are present, with an example for Accept-Encoding showing "deflate, gzip". The Fetch spec's header list supports duplicates internally, and get combines them.

Citations:


🏁 Script executed:

# Look at the full context of the normalizeHeaders function
cat -n client-sdks/client-js/src/resources/base.ts | head -30

Repository: mastra-ai/mastra

Length of output: 1008


🏁 Script executed:

# Search for how normalizeHeaders is used downstream
rg -A 5 -B 2 "normalizeHeaders" client-sdks/client-js/src/resources/base.ts

Repository: mastra-ai/mastra

Length of output: 573


Normalize duplicate tuple headers per HTTP specification.

At line 19, Object.fromEntries(headers) drops repeated header names (last-write-wins), misaligning with how the Headers instanceof branch handles them. The Fetch standard combines duplicate header names with ", " (comma-space). Use new Headers(headers).entries() to maintain consistency.

Proposed fix
  if (Array.isArray(headers)) {
-   return Object.fromEntries(headers);
+   return Object.fromEntries(new Headers(headers).entries());
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Array.isArray(headers)) {
return Object.fromEntries(headers);
}
if (Array.isArray(headers)) {
return Object.fromEntries(new Headers(headers).entries());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client-sdks/client-js/src/resources/base.ts` around lines 18 - 20, The
current Array.isArray(headers) branch uses Object.fromEntries(headers) which
drops duplicate header keys; instead construct a Headers instance from the tuple
array (new Headers(headers)) and use its entries() to build the resulting plain
object so duplicate names are combined per the Fetch spec (comma+space). Update
the branch that handles Array.isArray(headers) to create a Headers from the
tuples and then produce the output object from Headers.entries() rather than
calling Object.fromEntries on the raw tuples; reference the
Array.isArray(headers) check, Headers, and Object.fromEntries when making the
change.

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