Skip to content

Commit 38ee8b4

Browse files
fix(openapi-fetch): fix headers for FormData (#1192)
* fix(openapi-fetch): fix headers for FormData We need to delete the `Content-Type` header if the serialized body is a `FormData`. This allows the browser to set the `Content-Type` and boundary expression correctly. Resolves #1191 * fix(openapi-fetch): revert unnecessary change `baseHeaders` is still the same object via reference, we can just access it directly.
1 parent f259093 commit 38ee8b4

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

.changeset/orange-comics-sip.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-fetch": patch
3+
---
4+
5+
Fix header handling for FormData

packages/openapi-fetch/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,17 @@ const { data, error } = await put("/submit", {
218218
});
219219
```
220220

221+
If your `body` is already a `FormData`, provide an identity function:
222+
223+
```ts
224+
const { data, error } = await put("/submit", {
225+
body: myFormData,
226+
bodySerializer: (body) => body,
227+
});
228+
```
229+
230+
For `multipart/form-data`, [do not set the `Content-Type` header](https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#sending_files_using_a_formdata_object). The browser will set that for you, along with the boundary expression, which serves as a delimiter for the form fields.
231+
221232
## 🎯 Project Goals
222233

223234
1. Infer types automatically from OpenAPI schemas **without generics** (or, only the absolute minimum needed)

packages/openapi-fetch/src/index.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,6 @@ describe("client", () => {
272272
const client = createClient<paths>();
273273
mockFetchOnce({ status: 200, body: "{}" });
274274
const { data } = await client.put("/contact", {
275-
headers: {
276-
"Content-Type": "multipart/form-data",
277-
},
278275
body: {
279276
name: "John Doe",
280277
@@ -293,6 +290,9 @@ describe("client", () => {
293290
// expect post_id to be encoded properly
294291
const req = fetchMocker.mock.calls[0][1];
295292
expect(req.body).toBeInstanceOf(FormData);
293+
294+
// TODO: `vitest-fetch-mock` does not add the boundary to the Content-Type header like browsers do, so we expect the header to be null instead
295+
expect((req.headers as Headers).get("Content-Type")).toBeNull();
296296
});
297297
});
298298

packages/openapi-fetch/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
125125
headers: baseHeaders,
126126
};
127127
if (requestBody) requestInit.body = bodySerializer(requestBody as any);
128+
// remove `Content-Type` if serialized body is FormData; browser will correctly set Content-Type & boundary expression
129+
if (requestInit.body instanceof FormData) baseHeaders.delete("Content-Type");
128130
const response = await fetch(finalURL, requestInit);
129131

130132
// handle empty content

0 commit comments

Comments
 (0)