next-fetch icon indicating copy to clipboard operation
next-fetch copied to clipboard

RFC: multipart/form-data

Open HaNdTriX opened this issue 2 years ago • 0 comments

Summary

Currently the Form.encType has no effect when using @next-fetch/swr/form. Enabling encTypes like multipart/form-data would allow all kinds of common features like File Uploads.

Basic example

File: pages/file-upload.tsx

export default function MyImageUpload() {
  const mutation = useImageUpload();
  return (
    <Form encType="multipart/form-data" mutation={mutation}>
      <input type="file" name="someFile" accept="image/*" />
      <button type="submit">Submit</button>
    </Form>
  );
}

File: pages/api/image.swr.ts

import { zfd } from "zod-form-data";

export const useImageUpload = mutation(
  zfd.formData({ someFile: zfd.file() }),
  async (formData) => {
    const file: File = formData.get('someFile')
    // ...
    return { message: "Success" };
  }
);

Motivation

Today next-fetch only supports application/x-www-form-urlencoded forms. These forms are quite limited, since they only allow structured string data.

Adding multipart/form-data support, would allow sending all kind of hybrid data (strings, blobs, etc) to the server while still being able to support native html forms (hookResponse).

Challenges

  • FormData inside Edge Runtime and Node.js still lacks in some features.
  • FormData blobs need to be lazy on the server.
  • zod currently doesn't support ES Maps afaik. Nevertheless zod-form-data can help here.
  • ...to be continued

Adoption strategy

The type definition of @next-fetch/swr/form element should be updated for the time multipart/form-data is not supported, otherwise a breaking change will be needed when the feature has been implemented.

export function Form<Data, Error>({
  mutation,
  mutationConfig,
  ...props
}: React.HTMLProps<HTMLFormElement> &
  React.PropsWithChildren<{
    mutation: HookWithFormSubmission<Data, Error>;
    mutationConfig?: SWRMutationConfiguration<Data, Error>;
+    encType?: 'application/x-www-form-urlencoded'
  }>) {
  const { formProps } = useForm(mutation, mutationConfig);
  return createElement("form", { ...formProps, ...props }, props.children);
}

Later this feature can be allowed by adding multipart/form-data to the encType union.

export function Form<Data, Error>({
  mutation,
  mutationConfig,
  ...props
}: React.HTMLProps<HTMLFormElement> &
  React.PropsWithChildren<{
    mutation: HookWithFormSubmission<Data, Error>;
    mutationConfig?: SWRMutationConfiguration<Data, Error>;
-    encType?: 'application/x-www-form-urlencoded' 
+    encType?: 'application/x-www-form-urlencoded' | 'multipart/form-data'
  }>) {
  const { formProps } = useForm(mutation, mutationConfig);
  return createElement("form", { ...formProps, ...props }, props.children);
}

Links

  • https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData?retiredLocale=de
  • https://www.npmjs.com/package/zod-form-data
  • https://xhr.spec.whatwg.org/#dom-formdata

HaNdTriX avatar Oct 02 '22 11:10 HaNdTriX