T
TanStackโ€ข5w ago
evident-indigo

how to upload file in start

when i try uploading file to server i am getting the following error
Seroval caught an error during the parsing process. Error The value [object File] of type "object" cannot be parsed/serialized. There are few workarounds for this problem: - Transform the value in a way that it can be serialized. - If the reference is present on multiple runtimes (isomorphic), you can use the Reference API to map the references. - For more information, please check the "cause" property of this error. - If you believe this is an error in Seroval, please submit an issue at https://github.com/lxsmnsyc/seroval/issues/new
am i doing this correct, i tried searching in docs about how i am suppose to upload file but could not find anything here is the component code, thank you https://github.com/omkargarde/code-explainer/blob/dev/src/routes/upload.tsx
GitHub
Build software better, together
GitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over 420 million projects.
From An unknown user
From An unknown user
From An unknown user
GitHub
code-explainer/src/routes/upload.tsx at dev ยท omkargarde/code-expl...
Contribute to omkargarde/code-explainer development by creating an account on GitHub.
5 Replies
evident-indigo
evident-indigoOPโ€ข4w ago
managed to resolve the problem, if anyone is curious in component just pass the formData directly
const { mutate, isPending, isSuccess, isError, error } = useMutation({
mutationFn: (postData: FormData) => uploadFiles({ data: postData }),
mutationKey: ["uploadFiles"],
});

function handlerSubmit(formData: FormData) {
mutate(formData);
}
const { mutate, isPending, isSuccess, isError, error } = useMutation({
mutationFn: (postData: FormData) => uploadFiles({ data: postData }),
mutationKey: ["uploadFiles"],
});

function handlerSubmit(formData: FormData) {
mutate(formData);
}
and in server function validate if input is of type of FormData
export const uploadFilesFn = createServerFn({ method: "POST" })
.inputValidator(isFormDataSchema)
.handler(async ({ data }) => {
const file = data.get("file");
// rest of the function
export const uploadFilesFn = createServerFn({ method: "POST" })
.inputValidator(isFormDataSchema)
.handler(async ({ data }) => {
const file = data.get("file");
// rest of the function
national-gold
national-goldโ€ข4w ago
right now the serializer cannot handle File we could add support for this, or you could add a custom serializer adapter
harsh-harlequin
harsh-harlequinโ€ข4w ago
It would be really nice to have File support in server functions using Zod schema in inputValidator, I don't know if it's technically possible @omkar garde I do something like this, it allows you to have the { data } in the handler with the corresponding type, and to keep all the input validation logic in the inputValidator instead of putting it in the handler.
const myZodSchema = z.object({
firstname: z.string(),
lastname: z.string(),
avatar: z.object({
blob: z.instanceof(File),
name: z.string(),
}),
});

export const myFormDataValidator = (data: unknown) => {
if (!(data instanceof FormData)) {
throw new Error("Expected FormData");
}

const result = formDataToObject(data);

return myZodSchema.parse(result);
};
const myZodSchema = z.object({
firstname: z.string(),
lastname: z.string(),
avatar: z.object({
blob: z.instanceof(File),
name: z.string(),
}),
});

export const myFormDataValidator = (data: unknown) => {
if (!(data instanceof FormData)) {
throw new Error("Expected FormData");
}

const result = formDataToObject(data);

return myZodSchema.parse(result);
};
formDataToObject is a custom serializer that using "qs"
xenial-black
xenial-blackโ€ข7d ago
I'm facing the same issue and I realized that passing form data within an object doesn't seem to work but only when being passed as a "top level param value":
// Server fns:
export const serverFnWithObjectParam = createServerFn({ method: 'POST' })
.inputValidator(z.object({ formData: z.instanceof(FormData) }))
.handler(async ({ context, data }) => { /* ... */ });


export const serverFnWithTopLevelParam = createServerFn({ method: 'POST' })
.inputValidator(z.instanceof(FormData))
.handler(async ({ context, data }) => { /* ... */ });

// Client cmp:
<form
onSubmit={event => {
event.preventDefault();
const formData = new FormData(event.currentTarget);

// โœ… Works as expected
void serverFnWithTopLevelParam({ data: formData });

// ๐Ÿ”ฅ Throws with SerovalParserError "The value [object File] of type "object" cannot be parsed/serialized." on client
void serverFnWithObjectParam({ data: { formData } });
}}
>
<input name="profileImage" type="file" accept="image/*" />
<button type="submit">Submit</button>
</form>
// Server fns:
export const serverFnWithObjectParam = createServerFn({ method: 'POST' })
.inputValidator(z.object({ formData: z.instanceof(FormData) }))
.handler(async ({ context, data }) => { /* ... */ });


export const serverFnWithTopLevelParam = createServerFn({ method: 'POST' })
.inputValidator(z.instanceof(FormData))
.handler(async ({ context, data }) => { /* ... */ });

// Client cmp:
<form
onSubmit={event => {
event.preventDefault();
const formData = new FormData(event.currentTarget);

// โœ… Works as expected
void serverFnWithTopLevelParam({ data: formData });

// ๐Ÿ”ฅ Throws with SerovalParserError "The value [object File] of type "object" cannot be parsed/serialized." on client
void serverFnWithObjectParam({ data: { formData } });
}}
>
<input name="profileImage" type="file" accept="image/*" />
<button type="submit">Submit</button>
</form>
Is this expected behavior or a bug or am I doing something wrong here? I'd need to pass it inside an object b/c I'm passing other values alongside the form data in my real life scenario... @Yanis From your code above it seems like you mentioned to got it working w/ passing objects that hold the form data ๐Ÿค”. Could you show a bit more in detail, how your solution looks like by showing the implementation of formDataToObject + your server fns using the myFormDataValidator and how you're then calling it from the client? FYI, I could obviously pack all the data into the "top level" FormData but I'd like to avoid that b/c it is rather cumbersome on client and eliminates type safety:
const filesFormData = createFilesFormData(files) : new FormData();

// โœ… Nice API but throws ATM (see above comment)
await serverFn({ data: { userId: 123, more: 'data', files: filesFormData } });

// ๐ŸŸง Clumsy DX & no type safety when building the form data...
formData.set('userId', '123');
formData.set('more', 'data');
await serverFn({ data: formData });
const filesFormData = createFilesFormData(files) : new FormData();

// โœ… Nice API but throws ATM (see above comment)
await serverFn({ data: { userId: 123, more: 'data', files: filesFormData } });

// ๐ŸŸง Clumsy DX & no type safety when building the form data...
formData.set('userId', '123');
formData.set('more', 'data');
await serverFn({ data: formData });
Update @Yanis I just realized from looking at your code again, that you're also passing the whole params as 1 FormData, so your solution is probably the same as the one mentioned in my previous post with the "clumsy DX" if I'm not missing anything.
harsh-harlequin
harsh-harlequinโ€ข7d ago
Hello @skaface , as soon as I'm available I'll send you my complete code, because I'm using a custom serializer for sending and retrieving it

Did you find this page helpful?