vpjm
vpjm
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
/solve
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
solution :
// TRPC PROVIDER file
export class FormDataTransformer implements DataTransformer {
serialize(object: any) {
if (!(object instanceof FormData)) {
throw new Error("Expected FormData");
}

return object;
}

deserialize(object: any) {
return object as JSON;
}
}


function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => !isNonJsonSerializable(op.input) && op.type !== "subscription" && !op.context["stream"],
true: httpBatchLink({url: getUrl(),transformer }),
false: splitLink({
condition: (op) => isNonJsonSerializable(op.input) && op.type !== "subscription" && !op.context["stream"],
true: httpLink({
url: getUrl(),transformer: new FormDataTransformer(),
}),
false: splitLink({
condition: (op) => op.type === "subscription" && !op.context["stream"],
true: unstable_httpSubscriptionLink({
url: getUrl(),transformer
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),transformer
}),
}),
}),
}),
],
}),
);
return (
<QueryClientProvider client={getQueryClient()}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</trpc.Provider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
// TRPC PROVIDER file
export class FormDataTransformer implements DataTransformer {
serialize(object: any) {
if (!(object instanceof FormData)) {
throw new Error("Expected FormData");
}

return object;
}

deserialize(object: any) {
return object as JSON;
}
}


function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => !isNonJsonSerializable(op.input) && op.type !== "subscription" && !op.context["stream"],
true: httpBatchLink({url: getUrl(),transformer }),
false: splitLink({
condition: (op) => isNonJsonSerializable(op.input) && op.type !== "subscription" && !op.context["stream"],
true: httpLink({
url: getUrl(),transformer: new FormDataTransformer(),
}),
false: splitLink({
condition: (op) => op.type === "subscription" && !op.context["stream"],
true: unstable_httpSubscriptionLink({
url: getUrl(),transformer
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),transformer
}),
}),
}),
}),
],
}),
);
return (
<QueryClientProvider client={getQueryClient()}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</trpc.Provider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
I try to simplify again but :
Uncaught (in promise) TRPCClientError: [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
},
{
"code": "custom",
"message": "Input not instance of File",
"fatal": true,
"path": [
"image"
]
}
]
Uncaught (in promise) TRPCClientError: [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"name"
],
"message": "Required"
},
{
"code": "custom",
"message": "Input not instance of File",
"fatal": true,
"path": [
"image"
]
}
]
look like the same error
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
My stack is next 15.2.2 , trpc v11 and next auth (idk if this is important)
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
but still dont work
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
As Nick Lucas advised me, I went back to the basics
//QueryClient
export const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 30 * 1000,
},
}})


//PROVIDER
function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => op.type === 'subscription',
true: unstable_httpSubscriptionLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
}),
],
}),
);
return (
<QueryClientProvider client={getQueryClient()}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</trpc.Provider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
//QueryClient
export const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 30 * 1000,
},
}})


//PROVIDER
function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => op.type === 'subscription',
true: unstable_httpSubscriptionLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
}),
],
}),
);
return (
<QueryClientProvider client={getQueryClient()}>
<trpc.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</trpc.Provider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
// ENDPOINT TRPC
const uploadFileSchema = zfd.formData({
name: zfd.text(),
image: zfd.file(),
});
export const appRouter = createTRPCRouter({
//...
myEndpoint: publicProcedure.input(uploadFileSchema).mutation(async (opts) => {
console.log(opts.input);
})
})
const uploadFileSchema = zfd.formData({
name: zfd.text(),
image: zfd.file(),
});
export const appRouter = createTRPCRouter({
//...
myEndpoint: publicProcedure.input(uploadFileSchema).mutation(async (opts) => {
console.log(opts.input);
})
})
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
export function Upload() {
const mutation = trpc.myEndpoint.create.useMutation({
onError(err) {
alert('Error from server: ' + err.message);
},
});
const schema = zfd.formData({
name: zfd.text(),
image: zfd.file(),
});

const form = useZodFormData({
schema,
});

const [noJs, setNoJs] = useState(false);

return (
<>
<FormProvider {...form}>
<form
method="post"
action={`/api/trpc/${mutation.trpc.path}`}
encType="multipart/form-data"
onSubmit={(_event) => {
if (noJs) {
return;
}
void form.handleSubmit(async (values, event) => {
await mutation.mutateAsync(new FormData(event?.target));
})(_event);
}}
style={{ display: 'flex', flexDirection: 'column', gap: 10 }}
ref={form.formRef}
>
<fieldset>
<div style={{}}>
<label htmlFor="name">Enter your name</label>
<input {...form.register('name')} />
{form.formState.errors.name && (
<div>{form.formState.errors.name.message}</div>
)}
</div>

<div>
<label>Required file, only images</label>
<input type="file" {...form.register('image')} />
{form.formState.errors.image && (
<div>{form.formState.errors.image.message}</div>
)}
</div>
<div>
<button type="submit" disabled={mutation.status === 'pending'}>
submit
</button>
</div>
</fieldset>
</form>
</FormProvider>
</>
);
}
export function Upload() {
const mutation = trpc.myEndpoint.create.useMutation({
onError(err) {
alert('Error from server: ' + err.message);
},
});
const schema = zfd.formData({
name: zfd.text(),
image: zfd.file(),
});

const form = useZodFormData({
schema,
});

const [noJs, setNoJs] = useState(false);

return (
<>
<FormProvider {...form}>
<form
method="post"
action={`/api/trpc/${mutation.trpc.path}`}
encType="multipart/form-data"
onSubmit={(_event) => {
if (noJs) {
return;
}
void form.handleSubmit(async (values, event) => {
await mutation.mutateAsync(new FormData(event?.target));
})(_event);
}}
style={{ display: 'flex', flexDirection: 'column', gap: 10 }}
ref={form.formRef}
>
<fieldset>
<div style={{}}>
<label htmlFor="name">Enter your name</label>
<input {...form.register('name')} />
{form.formState.errors.name && (
<div>{form.formState.errors.name.message}</div>
)}
</div>

<div>
<label>Required file, only images</label>
<input type="file" {...form.register('image')} />
{form.formState.errors.image && (
<div>{form.formState.errors.image.message}</div>
)}
</div>
<div>
<button type="submit" disabled={mutation.status === 'pending'}>
submit
</button>
</div>
</fieldset>
</form>
</FormProvider>
</>
);
}
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
Now I have the same endpoint and frontend as https://github.com/trpc/examples-next-formdata, but it still doesn't work.
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
//CLIENT FORM
const url = `/test/upload/${uuidv4()}`
const formData = new FormData();
formData.append('file', data.image);
formData.append('metadata', JSON.stringify({
filename,
type:"IMAGE",
extension,
img_width: width,
img_height: height,
..._.pick(file,["size","name"]),
video_length:null,
mimeType:"IMAGE_JPEG"
}));
formData.append('url', url);
await createPost.mutateAsync(formData)
const url = `/test/upload/${uuidv4()}`
const formData = new FormData();
formData.append('file', data.image);
formData.append('metadata', JSON.stringify({
filename,
type:"IMAGE",
extension,
img_width: width,
img_height: height,
..._.pick(file,["size","name"]),
video_length:null,
mimeType:"IMAGE_JPEG"
}));
formData.append('url', url);
await createPost.mutateAsync(formData)
//TRPC PROVIDER
function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => op.type === 'subscription',
true: unstable_httpSubscriptionLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
})
],
}),
);
function TRPCProviders(props: Readonly<{ children: React.ReactNode }>) {
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
splitLink({
condition: (op) => op.type === 'subscription',
true: unstable_httpSubscriptionLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
false: unstable_httpBatchStreamLink({
url: getUrl(),
/**
* @see https://trpc.io/docs/v11/data-transformers
*/
transformer,
}),
})
],
}),
);
//ENDPOINT TRPC
const uploadFileSchema = zfd.formData({
url: zfd.text(),
file: zfd.file(z.instanceof(File)),
metadata: zfd.json(z.any()),
});

export const appRouter = createTRPCRouter({
uploadFile: publicProcedure.input(uploadFileSchema).mutation(async (opts) => {
console.log(opts.input);
})})
const uploadFileSchema = zfd.formData({
url: zfd.text(),
file: zfd.file(z.instanceof(File)),
metadata: zfd.json(z.any()),
});

export const appRouter = createTRPCRouter({
uploadFile: publicProcedure.input(uploadFileSchema).mutation(async (opts) => {
console.log(opts.input);
})})
14 replies
TtRPC
Created by vpjm on 3/11/2025 in #❓-help
FormData TRPCClientError
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export const createTRPCContext = async (opts: FetchCreateContextFnOptions) => {
const session = await auth();

return {
session,
};
};

export type Context = Awaited<ReturnType<typeof createTRPCContext>>;
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export const createTRPCContext = async (opts: FetchCreateContextFnOptions) => {
const session = await auth();

return {
session,
};
};

export type Context = Awaited<ReturnType<typeof createTRPCContext>>;
//NEXT-API : api/trpc/[trpc]/route.ts
const handler = (req: NextRequest) =>
fetchRequestHandler({
router: appRouter,
req,
endpoint: '/api/trpc',
/**
* @see https://trpc.io/docs/v11/context
*/
createContext: createTRPCContext,
/**
* @see https://trpc.io/docs/v11/error-handling
*/
onError: process.env.NODE_ENV === "development" ? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
);
}
: undefined,
});

export { handler as GET, handler as POST };
const handler = (req: NextRequest) =>
fetchRequestHandler({
router: appRouter,
req,
endpoint: '/api/trpc',
/**
* @see https://trpc.io/docs/v11/context
*/
createContext: createTRPCContext,
/**
* @see https://trpc.io/docs/v11/error-handling
*/
onError: process.env.NODE_ENV === "development" ? ({ path, error }) => {
console.error(
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`
);
}
: undefined,
});

export { handler as GET, handler as POST };
` //PS : createQueryClient for TRPC PROVIDER
export const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 30 * 1000,
},
dehydrate: {
serializeData: transformer.serialize,
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === "pending",
},
hydrate: {
deserializeData: transformer.deserialize,
},
},
});
export const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 30 * 1000,
},
dehydrate: {
serializeData: transformer.serialize,
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === "pending",
},
hydrate: {
deserializeData: transformer.deserialize,
},
},
});
14 replies
TtRPC
Created by vpjm on 2/10/2025 in #❓-help
Passing generic inside tRPC query
Thank you very much, I am now sure that it's not doable right now 🤍 Have an excellent day! (PS: I will close the post at the end of the day if anyone wants to add something in the meantime)
6 replies
TtRPC
Created by vpjm on 2/10/2025 in #❓-help
Passing generic inside tRPC query
:trpc:
6 replies
TtRPC
Created by vpjm on 2/10/2025 in #❓-help
Passing generic inside tRPC query
I saw that there are several Git issues related to this topic, so feel free to reply to this post !
6 replies