Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to parse file uploads handled with unstable_createFileUploadHandler #14

Closed
mdoury opened this issue Nov 7, 2022 · 2 comments · Fixed by #15
Closed

Unable to parse file uploads handled with unstable_createFileUploadHandler #14

mdoury opened this issue Nov 7, 2022 · 2 comments · Fixed by #15

Comments

@mdoury
Copy link
Contributor

mdoury commented Nov 7, 2022

I'm trying to make file uploads type safe using zx.parseFormSafe but I'm not able to parse the NodeOnDiskFile class.

import type { ActionArgs } from "@remix-run/node";
import {
  json,
  NodeOnDiskFile,
  unstable_composeUploadHandlers,
  unstable_createFileUploadHandler,
  unstable_createMemoryUploadHandler,
  unstable_parseMultipartFormData,
} from "@remix-run/node";
import type { ZodError } from "zod";
import { z } from "zod";
import { zx } from "zodix";

function errorAtPath(error: ZodError, path: string) {
  return error.issues.find((issue) => issue.path[0] === path)?.message;
}

export async function action({ request }: ActionArgs) {
  const uploadHandler = unstable_composeUploadHandlers(
    unstable_createFileUploadHandler({
      maxPartSize: 5_000_000,
      file: ({ filename }) => filename,
    }),
    // parse everything else into memory
    unstable_createMemoryUploadHandler()
  );
  const formData = await unstable_parseMultipartFormData(
    request,
    uploadHandler
  );

  const results = await zx.parseFormSafe(formData, {
    file: z.instanceof(NodeOnDiskFile),
    // And some more fields that work just fine 👌
  });

  if (results.success) {
    return await doSomethingWithImage(results.data);
  } else {
    return json({
      success: results.success,
      error: errorAtPath(results.error, "image"),
    });
  }
}

formData.get("image") instanceof NodeOnDiskFile actually evaluates to true.

I found a rather similar issue on the zod repo where running z.instanceof(File) on the server resulted in a Reference Error because the File class is not defined in Node, which is not the case of NodeOnDiskFile.

I tried another method to parse the data which would result in an any type inference but would at least throw for missing data :

const results = await zx.parseFormSafe(formData, {
    image: z.any().refine((file: unknown) => {
      console.log({ file }); 
      return Boolean(file);
    }, "Image is required."),
  });

Which logged { file: '[object File]' } suggesting the file is casted to a string before it reaches the refinement function.

I don't know if this issue comes from zod or from zodix but maybe someone encountered the issue before and found a workaround.

Thanks!

@rileytomasek
Copy link
Owner

I'm not 100% sure, but I don't think Zodix will work with files because it uses the URLSearchParams trick to parse the FormData. The code is here: https://github.com/rileytomasek/zodix/blob/master/src/parsers.ts#L172

If you can think of a simple way to add support, I'd be open to a PR, otherwise you may have to get the file from the FormData separately and handle validation directly with Zod.

@mdoury
Copy link
Contributor Author

mdoury commented Nov 7, 2022

Ok, thanks! I was wondering whether I was doing something wrong 😅

The URLSearchParams trick indeed explains the "[object File]" string in the console output as explained on the UrlSearchParams MDN page :

A literal sequence of name-value string pairs, or any object — such as a FormData object — with an iterator that produces a sequence of string pairs. Note that File entries will be serialized as [object File] rather than as their filename (as they would in an application/x-www-form-urlencoded form).

I will look into it and let you know if I find a way to make it work without adding too much overhead.

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 a pull request may close this issue.

2 participants