Skip to content

[ENGG-5091] fix: content-type sent when body is empty#4055

Merged
wrongsahil merged 5 commits intomasterfrom
no-content-type-header-when-body-empty
Dec 22, 2025
Merged

[ENGG-5091] fix: content-type sent when body is empty#4055
wrongsahil merged 5 commits intomasterfrom
no-content-type-header-when-body-empty

Conversation

@wrongsahil
Copy link
Member

@wrongsahil wrongsahil commented Dec 18, 2025

  • Fix don't send content-type header when body is empty
    • GET: Don't set autogenerated header
    • POST: Don't send body="" as fetch sends text/plain if "" body is sent
  • Cleanup: Remove bodyContainer usage

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed handling of empty POST request bodies to prevent automatic unwanted content-type headers
  • Refactor

    • Improved internal request body state management architecture for better code maintainability and consistency across form, multipart, and raw body inputs
    • Simplified component prop structures to reduce complexity

✏️ Tip: You can customize this high-level summary in your review settings.

@linear
Copy link

linear bot commented Dec 18, 2025

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

This PR refactors the request body state management architecture by removing the context-based RequestBodyStateManager and replacing it with direct prop-passing to component handlers. The RequestBody component is now memoized and introduces internal helpers (getBodyFromBodyContainer, GetBodyReturnType) to map content types to body shapes. The three renderer components (FormBody, MultipartFormBodyRenderer, RawBody) are updated to receive a new handleContentChange callback instead of managing state through context. The setContentType prop is removed from the component hierarchy, and body changes now flow through the new callback. Additionally, parseHttpRequestEntry is modified to assign content-type only when a body is present, and httpRequestPreparationService adds normalization for empty POST bodies.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • New helper type mapping logic: The getBodyFromBodyContainer<T> function and GetBodyReturnType<T> type mapping for different content types needs verification to ensure correct body shape derivation across FORM, MULTIPART_FORM, and text-based types.
  • State flow verification: With removal of centralized RequestBodyStateManager, verify that handleContentChange callback propagates correctly through all three renderer components and that body updates consistently flow back to parent state.
  • RequestBody component refactoring: Examine the memoization change and internal handler logic (handleContentChange, handleContentTypeChange) to ensure proper dependency management and that content type switches apply corresponding bodies correctly.
  • Conditional logic changes: Review modifications to parseHttpRequestEntry (content-type assignment now conditional on body presence) and empty body normalization in httpRequestPreparationService.prepareRequest.
  • Component signature consistency: Verify that all renderer props match the new contract and that body data is correctly sourced from the new props rather than context.

Possibly related PRs

Suggested reviewers

  • nafees87n
  • Aarushsr12

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description provided is sparse and does not follow the repository template. It lacks structured sections like 'Summary of changes', 'Demo Video', 'Test instructions', and the required checklist items are not addressed. Expand the description to include: a detailed summary of changes, video/demo evidence, explicit test instructions, and a completed checklist confirming linting, tests, UI verification, and documentation updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title '[ENGG-5091] fix: content-type sent when body is empty' directly summarizes the main fix: preventing content-type from being sent when the request body is empty, which aligns with the primary objective and changeset.
✨ 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 no-content-type-header-when-body-empty

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a1a652 and 80aa2d7.

📒 Files selected for processing (4)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/http/HttpClientView.tsx
  • app/src/features/apiClient/screens/apiClient/utils.ts
💤 Files with no reviewable changes (1)
  • app/src/features/apiClient/screens/apiClient/components/views/http/HttpClientView.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/features/apiClient/screens/apiClient/utils.ts
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-25T09:03:46.345Z
Learnt from: CR
Repo: requestly/requestly PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T09:03:46.345Z
Learning: Use TypeScript where applicable in the Requestly project

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx
📚 Learning: 2025-09-09T09:07:32.092Z
Learnt from: rohanmathur91
Repo: requestly/requestly PR: 3514
File: app/src/backend/types.ts:1-3
Timestamp: 2025-09-09T09:07:32.092Z
Learning: In the requestly codebase, when defining response types in app/src/backend/types.ts, the preference is to keep internal types like Response<T> unexported and only export the Promise wrapper types like ResponsePromise<T> to maintain a minimal public API surface.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
📚 Learning: 2025-12-11T06:43:01.328Z
Learnt from: rohanmathur91
Repo: requestly/requestly PR: 4000
File: app/src/features/apiClient/slices/multiWorkspaceView/helpers/ApiClientContextRegistry/hooks.ts:6-14
Timestamp: 2025-12-11T06:43:01.328Z
Learning: In app/src/features/apiClient/slices/multiWorkspaceView/helpers/ApiClientContextRegistry/hooks.ts, the useApiClientFeatureContext hook uses useMemo without reactivity intentionally. Contexts are expected to be registered before component mount, and reactivity to registry updates is not needed.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
📚 Learning: 2025-10-08T15:27:08.928Z
Learnt from: Aarushsr12
Repo: requestly/requestly PR: 3650
File: app/src/features/apiClient/screens/apiClient/utils.ts:280-306
Timestamp: 2025-10-08T15:27:08.928Z
Learning: In `app/src/features/apiClient/screens/apiClient/utils.ts`, when parsing cURL commands with multipart form files (values starting with "@"), the `generateMultipartFormKeyValuePairs` function intentionally creates `FormDataKeyValuePair` entries with an empty `value` array (type: FILE). The file path from cURL is not extracted or stored. Instead, the key is preserved to show in the UI table, and users select the actual file later using a file picker.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx
🔇 Additional comments (9)
app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx (2)

1-5: Imports look clean and appropriate.

The transition from context-based imports to direct type imports (RequestContentType, RQAPI) aligns well with the prop-driven refactor.


7-24: Clean prop-driven implementation.

The component correctly:

  • Destructures props with a sensible default for body
  • Wraps the change handler to maintain content type context
  • Passes data directly to KeyValueTable

The useCallback dependencies are correct.

app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx (7)

1-16: Imports are well-organized.

The added imports for autogenerated header management (useAutogenerateStore, AutogeneratedFieldsNamespace, createHeaderEntry, CONTENT_TYPE_HEADER) support the PR's goal of conditionally setting the Content-Type header based on body presence.


18-57: Helper function logic is correct.

The parseSingleModeBody function correctly maps each RequestContentType to its corresponding body container shape, with a sensible default fallback to text.


59-87: Well-typed helper with appropriate defaults.

The GetBodyReturnType conditional type and getBodyFromBodyContainer function provide type-safe body extraction with sensible defaults ([] for form types, "" for text types).


89-94: Props interface is clean and focused.

The removal of setContentType in favor of internal handleContentTypeChange simplifies the external API while maintaining full functionality.


113-136: Body-presence check works correctly for both arrays and strings.

The condition body && body?.length > 0 at line 115 correctly handles:

  • Array bodies (form, multipart): checks for non-empty arrays
  • String bodies (JSON, raw, etc.): checks for non-empty strings

The Content-Type header is purged when the body is empty or missing, which aligns with the PR objective.


165-207: Body editor rendering is well-structured.

The useMemo for bodyEditor correctly:

  • Maps content types to appropriate renderer components
  • Passes handleContentChange and computed body via getBodyFromBodyContainer
  • Includes all necessary dependencies in the dependency array

213-238: Radio group and overall component structure look good.

The "text" value abstraction for RAW/JSON content types is handled consistently in both the value computation and onChange handler. The component is properly memoized with React.memo.


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
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: 9

🧹 Nitpick comments (1)
app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/multipart-form-body-renderer.tsx (1)

1-1: Use PascalCase for React import.

The import uses lowercase react which is inconsistent with the rest of the codebase. While it works, convention is to use React with PascalCase.

🔎 Apply this diff:
-import react, { useCallback } from "react";
+import React, { useCallback } from "react";

And update the component type:

-export const MultipartFormBodyRenderer: react.FC<{
+export const MultipartFormBodyRenderer: React.FC<{
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d558704 and b20f5d1.

📒 Files selected for processing (8)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx (7 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx (1 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/multipart-form-body-renderer.tsx (1 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/raw-body-renderer.tsx (2 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/request-body-types.ts (0 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/http/HttpClientView.tsx (2 hunks)
  • app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx (1 hunks)
  • app/src/features/apiClient/screens/apiClient/utils.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/request-body-types.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-10-08T15:27:08.928Z
Learnt from: Aarushsr12
Repo: requestly/requestly PR: 3650
File: app/src/features/apiClient/screens/apiClient/utils.ts:280-306
Timestamp: 2025-10-08T15:27:08.928Z
Learning: In `app/src/features/apiClient/screens/apiClient/utils.ts`, when parsing cURL commands with multipart form files (values starting with "@"), the `generateMultipartFormKeyValuePairs` function intentionally creates `FormDataKeyValuePair` entries with an empty `value` array (type: FILE). The file path from cURL is not extracted or stored. Instead, the key is preserved to show in the UI table, and users select the actual file later using a file picker.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/utils.ts
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/multipart-form-body-renderer.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx
📚 Learning: 2025-11-20T14:37:21.459Z
Learnt from: nafees87n
Repo: requestly/requestly PR: 3801
File: app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts:180-194
Timestamp: 2025-11-20T14:37:21.459Z
Learning: In HttpRequestExecutor.execute method in `app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts`, iterationContext parameter must be required (not optional) with no defaults or falsy checks. TypeScript should enforce that callers provide this data, per maintainer iostreamer-X's requirement.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/utils.ts
📚 Learning: 2025-10-22T22:57:45.959Z
Learnt from: mustafa-sayyed
Repo: requestly/requestly PR: 3723
File: app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx:146-151
Timestamp: 2025-10-22T22:57:45.959Z
Learning: In the API Client Scripts tab (app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx), the dot indicator should only appear when scripts are present (count > 0). When no scripts exist, the dot should not be rendered. This is the intended behavior per issue #3731.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/http/HttpClientView.tsx
📚 Learning: 2025-10-10T11:08:23.369Z
Learnt from: nafees87n
Repo: requestly/requestly PR: 3655
File: app/src/features/apiClient/screens/apiClient/components/views/components/Collection/components/CollectionRunnerView/components/RunResultView/RunResultContainer/RunResultContainer.tsx:426-426
Timestamp: 2025-10-10T11:08:23.369Z
Learning: In the file `app/src/features/apiClient/screens/apiClient/components/views/components/Collection/components/CollectionRunnerView/components/RunResultView/RunResultContainer/RunResultContainer.tsx`, the `destroyInactiveTabPane={true}` setting is intentional. The loss of state (expandedKeys, scroll position) when switching tabs is acceptable for now and will be addressed in future work.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx
📚 Learning: 2025-11-25T09:03:46.345Z
Learnt from: CR
Repo: requestly/requestly PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T09:03:46.345Z
Learning: Use TypeScript where applicable in the Requestly project

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx
  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
📚 Learning: 2025-12-11T06:43:01.328Z
Learnt from: rohanmathur91
Repo: requestly/requestly PR: 4000
File: app/src/features/apiClient/slices/multiWorkspaceView/helpers/ApiClientContextRegistry/hooks.ts:6-14
Timestamp: 2025-12-11T06:43:01.328Z
Learning: In app/src/features/apiClient/slices/multiWorkspaceView/helpers/ApiClientContextRegistry/hooks.ts, the useApiClientFeatureContext hook uses useMemo without reactivity intentionally. Contexts are expected to be registered before component mount, and reactivity to registry updates is not needed.

Applied to files:

  • app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx
🧬 Code graph analysis (1)
app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/raw-body-renderer.tsx (2)
app/src/features/apiClient/helpers/variableResolver/variable-resolver.ts (1)
  • useScopedVariables (283-314)
app/src/hooks/useDebounce.ts (1)
  • useDebounce (4-20)
🔇 Additional comments (5)
app/src/features/apiClient/screens/apiClient/components/views/http/components/HttpRequestTabs/HttpRequestTabs.tsx (1)

116-124: LGTM!

The simplified RequestBody usage with direct body and contentType props is cleaner than the previous approach. The component now encapsulates content-type management internally while still allowing parent state updates via setRequestEntry.

app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/raw-body-renderer.tsx (1)

9-29: LGTM!

Clean refactor to the prop-driven callback pattern. The handleContentChange callback properly propagates changes with the content type, and the debounced handler has correct dependencies.

app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/multipart-form-body-renderer.tsx (1)

7-23: LGTM!

The refactor to use handleContentChange callback is clean and consistent with the other body renderers. The implementation correctly propagates content type changes.

app/src/features/apiClient/screens/apiClient/components/views/components/request/renderers/form-body-renderer.tsx (1)

7-26: LGTM on the refactor pattern.

The prop-driven approach with handleContentChange callback is consistent with the other body renderers and properly propagates content type with body updates.

app/src/features/apiClient/screens/apiClient/components/views/components/request/RequestBody.tsx (1)

82-258: Overall approach is sound but needs cleanup.

The refactor successfully moves content-type management into RequestBody and properly integrates with useAutogenerateStore for header management. The core logic for conditionally setting Content-Type headers based on body presence aligns with the PR objective. After removing debug logs, dead code, and fixing the missing dependency, this is a solid implementation.

Copy link
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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9d7f44 and 1a1a652.

📒 Files selected for processing (2)
  • app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts (1 hunks)
  • app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestPreparationService.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-20T14:37:21.459Z
Learnt from: nafees87n
Repo: requestly/requestly PR: 3801
File: app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts:180-194
Timestamp: 2025-11-20T14:37:21.459Z
Learning: In HttpRequestExecutor.execute method in `app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts`, iterationContext parameter must be required (not optional) with no defaults or falsy checks. TypeScript should enforce that callers provide this data, per maintainer iostreamer-X's requirement.

Applied to files:

  • app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestExecutor.ts
  • app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestPreparationService.ts
🔇 Additional comments (1)
app/src/features/apiClient/helpers/httpRequestExecutor/httpRequestPreparationService.ts (1)

200-204: Remove the review comment—the code is correct and type-safe.

The condition correctly handles all valid request body types. According to the type definitions, RequestBody can only be: string, KeyValuePair[], FormDataKeyValuePair[], undefined, or null. The code safely normalizes empty bodies:

  • !renderedEntry?.request?.body catches undefined, null, and empty strings "" (which is falsy)
  • renderedEntry.request?.body?.length <= 0 catches empty arrays [] (which are truthy but have length === 0) and empty strings (length 0)

Both strings and arrays have a .length property, so accessing it is type-safe. Empty objects {} cannot occur due to the type system. The logic is sound and properly normalizes all empty body variants before passing to fetch.

The "Hacky Fix" comment accurately describes the workaround for fetch automatically setting Content-Type to text/plain for string bodies, but the implementation itself is not problematic.

@wrongsahil wrongsahil merged commit 73bb4fe into master Dec 22, 2025
3 checks passed
@wrongsahil wrongsahil deleted the no-content-type-header-when-body-empty branch December 22, 2025 16:44
@coderabbitai coderabbitai bot mentioned this pull request Feb 25, 2026
8 tasks
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.

2 participants