Skip to content

Commit

Permalink
fix(openapi-fetch): fix headers for FormData (#1192)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
psychedelicious authored Jul 3, 2023
1 parent f259093 commit 38ee8b4
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-comics-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-fetch": patch
---

Fix header handling for FormData
11 changes: 11 additions & 0 deletions packages/openapi-fetch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ const { data, error } = await put("/submit", {
});
```

If your `body` is already a `FormData`, provide an identity function:

```ts
const { data, error } = await put("/submit", {
body: myFormData,
bodySerializer: (body) => body,
});
```

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.

## 🎯 Project Goals

1. Infer types automatically from OpenAPI schemas **without generics** (or, only the absolute minimum needed)
Expand Down
6 changes: 3 additions & 3 deletions packages/openapi-fetch/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,6 @@ describe("client", () => {
const client = createClient<paths>();
mockFetchOnce({ status: 200, body: "{}" });
const { data } = await client.put("/contact", {
headers: {
"Content-Type": "multipart/form-data",
},
body: {
name: "John Doe",
email: "test@email.email",
Expand All @@ -293,6 +290,9 @@ describe("client", () => {
// expect post_id to be encoded properly
const req = fetchMocker.mock.calls[0][1];
expect(req.body).toBeInstanceOf(FormData);

// 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
expect((req.headers as Headers).get("Content-Type")).toBeNull();
});
});

Expand Down
2 changes: 2 additions & 0 deletions packages/openapi-fetch/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ export default function createClient<Paths extends {}>(clientOptions: ClientOpti
headers: baseHeaders,
};
if (requestBody) requestInit.body = bodySerializer(requestBody as any);
// remove `Content-Type` if serialized body is FormData; browser will correctly set Content-Type & boundary expression
if (requestInit.body instanceof FormData) baseHeaders.delete("Content-Type");
const response = await fetch(finalURL, requestInit);

// handle empty content
Expand Down

0 comments on commit 38ee8b4

Please sign in to comment.