behaved
behaved3d ago

operation input is formdata but trpc makes it an application/json POST call

operation:
{
"type": "mutation",
"path": "vectleProfiles.updateProfileLogo",
"input": {},
"context": {},
"id": 4
}
{
"type": "mutation",
"path": "vectleProfiles.updateProfileLogo",
"input": {},
"context": {},
"id": 4
}
request headers:
accept:
*/*
accept-encoding:
gzip, deflate, br, zstd
accept-language:
en-US,en;q=0.9
connection:
keep-alive
content-length:
11
content-type:
application/json
accept:
*/*
accept-encoding:
gzip, deflate, br, zstd
accept-language:
en-US,en;q=0.9
connection:
keep-alive
content-length:
11
content-type:
application/json
sample router
publicProcedure
.input(z.instanceof(FormData))
.mutation(async ({ input, ctx }) => {
...
}),
publicProcedure
.input(z.instanceof(FormData))
.mutation(async ({ input, ctx }) => {
...
}),
3 Replies
behaved
behavedOP3d ago
I found a fix to my issue: superjson transformer somewhat did not work in nextjs v15 app router, though it worked on v14 pages router working code:
splitLink({
condition: op => isNonJsonSerializable(op.input),
true: httpLink({
url: getUrl(),
transformer: new FormDataTransformer(),
}),
false: httpBatchLink({
url: getUrl(),
transformer: superjson,
}),
}),

interface DataTransformer {
serialize: (object: any) => any
deserialize: (object: any) => any
}

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
}
}
splitLink({
condition: op => isNonJsonSerializable(op.input),
true: httpLink({
url: getUrl(),
transformer: new FormDataTransformer(),
}),
false: httpBatchLink({
url: getUrl(),
transformer: superjson,
}),
}),

interface DataTransformer {
serialize: (object: any) => any
deserialize: (object: any) => any
}

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
}
}
Shoutout to this repo, that I found my solution https://github.com/juliangra/trpc-next-formdata-app-router
GitHub
GitHub - juliangra/trpc-next-formdata-app-router: Submit FormData w...
Submit FormData with tRPC 11 using Next.js App router - juliangra/trpc-next-formdata-app-router
Nick
Nick3d ago
Are you set up with that content type as documented here? https://trpc.io/docs/server/validators#non-json-content-types
Input & Output Validators | tRPC
tRPC procedures may define validation logic for their input and/or output, and validators are also used to infer the types of inputs and outputs. We have first class support for many popular validators, and you can integrate validators which we don't directly support.
Nick
Nick3d ago
Should work natively with most links (though tell us what link setup you're using as some may not support it)

Did you find this page helpful?