\n\n### Link to reproduction\n\n---\n\n### To reproduce\n\n---\n\n### Additional information\n\n_No response_\n\n### 👨👧👦 Contributing\n\n- [ ] 🙋♂️ Yes, I'd be down to file a PR fixing this bug!",[2920],{"name":2891,"color":2892},6737,"bug: cannot narrow input types of `useMutation(trpc...).mutate(INPUT)` - cannot overwrite `mutationoptions.mutationFn` (`@trpc/tanstack-react-query`)","2025-05-01T18:59:50Z","https://github.com/trpc/trpc/issues/6737",0.6554515,{"description":2927,"labels":2928,"number":1560,"owner":2869,"repository":2901,"state":2912,"title":2929,"updated_at":2930,"url":2931,"score":2932},"Defining a query/mutation with no `input` results in\r\n```\r\nTRPCError: [query.\u003CqueryName>] - Input parser expects a Zod validator\r\n```\r\nIt's possible to workaround this by defining an empty input:\r\n```tsx\r\ninput: z.object({}),\r\n```\r\nHowever, that way an empty object is expected when calling the query via TRPC client.\r\nIt's a legit use-case to have queries/mutations with no `input`, right? Can we allow omitting `input` in such cases?",[],"Defining procedures with no `input`","2022-06-08T14:55:56Z","https://github.com/trpc/trpc-openapi/issues/5",0.6899995,{"description":2934,"labels":2935,"number":2936,"owner":2869,"repository":2869,"state":2912,"title":2937,"updated_at":2938,"url":2939,"score":2940},"Apologies if I have missed something simple, but I cannot figure out why input is `undefined` in my mutation handler.\r\n\r\npages/api/trpc/[trpc].ts\r\n```\r\nimport * as trpc from '@trpc/server'\r\nimport * as trpcNext from '@trpc/server/adapters/next'\r\nimport jwt from 'jsonwebtoken'\r\nimport superjson from 'superjson'\r\nimport { usersRouter } from '../../../routers/users'\r\nimport prisma from '../../../utils/prisma'\r\n\r\nconst createContext = async ({\r\n\treq,\r\n\tres,\r\n}: trpcNext.CreateNextContextOptions) => {\r\n\tasync function getUserFromHeader() {\r\n\t\tconst authHeader = req.headers['authorization']\r\n\t\tconst token = authHeader && authHeader.split(' ')[1]\r\n\t\tif (token == null) return null\r\n\t\ttry {\r\n\t\t\tconst payload = jwt.verify(token, process.env.JWT_SECRET)\r\n\t\t\tconst user = await prisma.user.findFirst({\r\n\t\t\t\twhere: { id: payload.id },\r\n\t\t\t})\r\n\t\t\treturn user\r\n\t\t} catch (error) {\r\n\t\t\treturn null\r\n\t\t}\r\n\t}\r\n\r\n\tconst user = await getUserFromHeader()\r\n\r\n\treturn {\r\n\t\tuser,\r\n\t}\r\n}\r\n\r\ntype Context = trpc.inferAsyncReturnType\u003Ctypeof createContext>\r\n\r\nexport function createRouter() {\r\n\treturn trpc.router\u003CContext>()\r\n}\r\n\r\nconst appRouter = createRouter()\r\n\t.transformer(superjson)\r\n\t.merge('users.', usersRouter)\r\n\r\nexport type AppRouter = typeof appRouter\r\n\r\nexport default trpcNext.createNextApiHandler({\r\n\trouter: appRouter,\r\n\tcreateContext,\r\n\tonError({ error }) {\r\n\t\tconsole.log(error.message)\r\n\t\tif (error.code === 'INTERNAL_SERVER_ERROR') {\r\n\t\t\tconsole.error('Something went wrong', error)\r\n\t\t}\r\n\t},\r\n\tbatching: {\r\n\t\tenabled: true,\r\n\t},\r\n})\r\n```\r\n\r\nrouters/users.ts\r\n```\r\nimport * as trpc from '@trpc/server'\r\nimport argon2 from 'argon2'\r\nimport jwt from 'jsonwebtoken'\r\nimport { createRouter } from '../pages/api/trpc/[trpc]'\r\nimport { registerSchema } from '../types'\r\nimport prisma from '../utils/prisma'\r\n\r\nexport const usersRouter = createRouter().mutation('create', {\r\n\tinput: registerSchema,\r\n\tasync resolve({ input }) {\r\n\t\tconsole.log({ input })\r\n\t\tif (await prisma.user.findFirst({ where: { email: input.email } })) {\r\n\t\t\tthrow trpc.httpError.badRequest('Email is taken')\r\n\t\t}\r\n\t\tconst user = await prisma.user.create({\r\n\t\t\tdata: {\r\n\t\t\t\temail: input.email,\r\n\t\t\t\thashedPassword: await argon2.hash(input.password),\r\n\t\t\t},\r\n\t\t})\r\n\t\tconst token = jwt.sign(\r\n\t\t\t{\r\n\t\t\t\tid: user.id,\r\n\t\t\t},\r\n\t\t\tprocess.env.JWT_SECRET\r\n\t\t)\r\n\t\treturn {\r\n\t\t\ttoken,\r\n\t\t}\r\n\t},\r\n})\r\n```\r\n\r\ntypes/index.ts\r\n```\r\nimport { z } from 'zod'\r\n\r\nexport const registerSchema = z.object({\r\n\temail: z.string().email().min(8).max(160),\r\n\tpassword: z.string().min(12).max(200),\r\n})\r\n\r\nexport type RegisterForm = z.infer\u003Ctypeof registerSchema>\r\n```\r\n\r\nutils/trpc.ts\r\n```\r\nimport { createReactQueryHooks } from '@trpc/react'\r\nimport type { AppRouter } from '../pages/api/trpc/[trpc]'\r\n\r\nexport const trpc = createReactQueryHooks\u003CAppRouter>()\r\n```\r\n\r\npages/_app.tsx\r\n```\r\nimport { withTRPC } from '@trpc/next'\r\nimport { AppProps } from 'next/app'\r\nimport '../styles/globals.css'\r\n\r\nconst MyApp = ({ Component, pageProps }: AppProps) => {\r\n\treturn \u003CComponent {...pageProps} />\r\n}\r\n\r\nexport default withTRPC({\r\n\tconfig(_ctx) {\r\n\t\tconst url = `${process.env.NEXT_PUBLIC_SERVER_URL}/api/trpc`\r\n\r\n\t\treturn {\r\n\t\t\turl,\r\n\t\t\tqueryClientConfig: {\r\n\t\t\t\tdefaultOptions: {\r\n\t\t\t\t\tqueries: {\r\n\t\t\t\t\t\tstaleTime: 600,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t}\r\n\t},\r\n\tssr: true,\r\n})(MyApp)\r\n```\r\n\r\npages/register.tsx\r\n```\r\nimport { zodResolver } from '@hookform/resolvers/zod'\r\nimport { useForm } from 'react-hook-form'\r\nimport Layout from '../components/Layout'\r\nimport { RegisterForm, registerSchema } from '../types'\r\nimport { trpc } from '../utils/trpc'\r\n\r\nconst RegisterPage = () => {\r\n\tconst createUser = trpc.useMutation('users.create')\r\n\r\n\tconst {\r\n\t\tregister,\r\n\t\thandleSubmit,\r\n\t\tformState: { errors },\r\n\t} = useForm\u003CRegisterForm>({\r\n\t\tresolver: zodResolver(registerSchema),\r\n\t})\r\n\r\n\tconst onSubmit = async (data: RegisterForm) => {\r\n\t\tawait createUser.mutateAsync(data, {\r\n\t\t\tasync onSuccess(res) {\r\n\t\t\t\tlocalStorage.setItem('token', res.token)\r\n\t\t\t},\r\n\t\t})\r\n\t}\r\n\r\n\treturn (\r\n\t\t\u003CLayout title=\"Register\">\r\n\t\t\t\u003Cform onSubmit={handleSubmit(onSubmit)}>\r\n\t\t\t\t\u003Cdiv>\r\n\t\t\t\t\t\u003Clabel>Email\u003C/label>\r\n\t\t\t\t\t\u003Cinput {...register('email')} type=\"email\" />\r\n\t\t\t\t\t\u003Cspan>{errors.email?.message}\u003C/span>\r\n\t\t\t\t\u003C/div>\r\n\r\n\t\t\t\t\u003Cdiv>\r\n\t\t\t\t\t\u003Clabel>Password\u003C/label>\r\n\t\t\t\t\t\u003Cinput {...register('password')} type=\"password\" />\r\n\t\t\t\t\t\u003Cspan>{errors.password?.message}\u003C/span>\r\n\t\t\t\t\u003C/div>\r\n\r\n\t\t\t\t\u003Cdiv>\r\n\t\t\t\t\t\u003Cinput type=\"submit\" />\r\n\t\t\t\t\u003C/div>\r\n\t\t\t\u003C/form>\r\n\t\t\u003C/Layout>\r\n\t)\r\n}\r\n\r\nexport default RegisterPage\r\n```\r\n\r\nError\r\n```\r\n{\r\n \"id\": null,\r\n \"error\": {\r\n \"json\": {\r\n \"message\": \"[\\n {\\n \\\"code\\\": \\\"invalid_type\\\",\\n \\\"expected\\\": \\\"object\\\",\\n \\\"received\\\": \\\"undefined\\\",\\n \\\"path\\\": [],\\n \\\"message\\\": \\\"Required\\\"\\n }\\n]\",\r\n \"code\": -32600,\r\n \"data\": {\r\n \"code\": \"BAD_REQUEST\",\r\n \"stack\": \"TRPCError: [\\n {\\n \\\"code\\\": \\\"invalid_type\\\",\\n \\\"expected\\\": \\\"object\\\",\\n \\\"received\\\": \\\"undefined\\\",\\n \\\"path\\\": [],\\n \\\"message\\\": \\\"Required\\\"\\n }\\n]\\n at ProcedureWithInput.parseInput (/Users/bytemagician/code/web/node_modules/@trpc/server/dist/router-175c3ce6.cjs.dev.js:37:13)\\n at ProcedureWithInput.call (/Users/bytemagician/code/web/node_modules/@trpc/server/dist/router-175c3ce6.cjs.dev.js:62:24)\\n at Router.invoke (/Users/bytemagician/code/web/node_modules/@trpc/server/dist/router-175c3ce6.cjs.dev.js:286:22)\\n at Object.mutation (/Users/bytemagician/code/web/node_modules/@trpc/server/dist/router-175c3ce6.cjs.dev.js:305:21)\\n at Object.callProcedure (webpack-internal:///./node_modules/@trpc/server/dist/callProcedure-66851817.cjs.dev.js:56:19)\\n at eval (webpack-internal:///./node_modules/@trpc/server/dist/index-5abbb896.cjs.dev.js:227:44)\\n at Array.map (\u003Canonymous>)\\n at Object.requestHandler (webpack-internal:///./node_modules/@trpc/server/dist/index-5abbb896.cjs.dev.js:223:45)\\n at processTicksAndRejections (node:internal/process/task_queues:96:5)\\n at async eval (webpack-internal:///./node_modules/@trpc/server/adapters/next/dist/trpc-server-adapters-next.cjs.dev.js:50:5)\",\r\n \"path\": \"users.create\"\r\n }\r\n }\r\n }\r\n}\r\n```",[],607,"Input is not being passed to mutation handler","2022-10-05T00:09:38Z","https://github.com/trpc/trpc/issues/607",0.692486,{"description":2942,"labels":2943,"number":2947,"owner":2869,"repository":2869,"state":2912,"title":2948,"updated_at":2949,"url":2950,"score":2951},"> From @KATT:\r\n>\r\n> We should `Omit` `mutationFn` etc from `UseMutationOptions` (and for queries too)\r\n>\r\n\r\n\r\n### Provide environment information\r\n\r\n```\r\n System:\r\n OS: macOS 13.6\r\n CPU: (10) arm64 Apple M1 Pro\r\n Memory: 123.95 MB / 16.00 GB\r\n Shell: 5.9 - /bin/zsh\r\n Binaries:\r\n Node: 18.16.1 - /usr/local/bin/node\r\n Yarn: 1.22.17 - /usr/local/bin/yarn\r\n npm: 9.5.1 - /usr/local/bin/npm\r\n pnpm: 8.6.10 - /opt/homebrew/bin/pnpm\r\n Watchman: 2022.06.27.00 - /opt/homebrew/bin/watchman\r\n Browsers:\r\n Chrome: 117.0.5938.92\r\n Safari: 16.6\r\n npmPackages:\r\n @tanstack/react-query: ^4.29.5 => 4.29.14 \r\n @trpc/client: ^10.21.1 => 10.23.0 \r\n @trpc/next: ^10.23.0 => 10.23.0 \r\n @trpc/react-query: ^10.21.1 => 10.23.0 \r\n @trpc/server: ^10.21.1 => 10.23.0 \r\n next: ^13.3.4 => 13.3.4 \r\n react: ^18.2.0 => 18.2.0 \r\n typescript: ^5.0.4 => 5.0.4 \r\n ```\r\n\r\n### Describe the bug\r\n\r\n`mutationFn` is not being called on `mutate.` My use-case is I need to add some variables from localStorage to my payload before it gets sent off in the mutation. `mutationFn` is never called, though `onSuccess` is.\r\n\r\n```\r\nexport const trpc = createTRPCNext({...})\r\n```\r\n\r\n```\r\n const mutation = trpc.auth.verifyCode.useMutation({\r\n mutationFn: async (variables) => {\r\n console.log('called'); // NEVER CALLED\r\n const extendedVariables = {\r\n ...variables,\r\n referralCode: maybeReferralCode,\r\n ...validatedUtm,\r\n };\r\n return trcpProxyClient.auth.verifyCode.mutate(extendedVariables);\r\n },\r\n\r\n onSuccess(data) {\r\n console.log('called2'); // CALLED\r\n storage.setAccessToken(data.accessToken);\r\n storage.setRefreshToken(data.refreshToken);\r\n\r\n identify(data);\r\n },\r\n });\r\n\r\n return mutation;\r\n};\r\n```\r\n\r\n### Link to reproduction\r\n\r\nhttps://stackblitz.com/edit/github-ucxmx2?file=src%2Fpages%2Findex.tsx\r\n\r\n### To reproduce\r\n\r\nSee example user mutation `updateUser`\r\n\r\nhttps://stackblitz.com/edit/github-ucxmx2?file=src%2Fpages%2Findex.tsx\r\n\r\n### Additional information\r\n\r\n_No response_\r\n\r\n### 👨👧👦 Contributing\r\n\r\n- [ ] 🙋♂️ Yes, I'd be down to file a PR fixing this bug!",[2944],{"name":2945,"color":2946},"👻 invalid","e4e669",4860,"bug: mutationFn being ignored","2025-03-20T15:41:56Z","https://github.com/trpc/trpc/issues/4860",0.7019693,{"description":2953,"labels":2954,"number":2958,"owner":2869,"repository":2869,"state":2912,"title":2959,"updated_at":2960,"url":2961,"score":2962},"Currently in the docs section for [Inferring Types](https://trpc.io/docs/infer-types), there is not code snippet for getting the proper types in the options object for mutations. I think it will be helpful to add because there may be a lot of people who wants to create a custom hook wrapping their mutations and want to add proper types for options parameter.\r\n\r\nHere is a code snippet from a custom hook that i created with a mutation:\r\n\r\n```ts\r\nimport { InferTRPCMutationOptions, trpc } from \"@/lib/trpc\";\r\n\r\ninterface UseAddProductParams {\r\n options?: InferTRPCMutationOptions\u003C\"product.add\">;\r\n}\r\n\r\nfunction useAddProduct({ options }: UseAddProductParams) {\r\n const productMutation = trpc.useMutation([\"product.add\"], options);\r\n\r\n return productMutation;\r\n}\r\nexport { useAddProduct };\r\n```\r\n\r\nThis is what InferTRPCMutationOptions looks like:\r\n\r\n\r\n```ts\r\ntype TError = TRPCClientErrorLike\u003CAppRouter>;\r\n\r\nexport type InferTRPCMutationOptions\u003C\r\n TRouteKey extends keyof AppRouter[\"_def\"][\"mutations\"]\r\n> = UseTRPCMutationOptions\u003C\r\n inferMutationInput\u003CTRouteKey>,\r\n TError,\r\n inferMutationOutput\u003CTRouteKey>,\r\n unknown\r\n>;\r\n```\r\n\r\ninferMutationInput and inferMutationOutput are exactly the same as the docs suggest\r\n\r\n\r\n",[2955],{"name":2956,"color":2957},"📚 documentation / examples","0075ca",2342,"Improve docs section for \"Inferring Types\"","2022-12-11T18:01:44Z","https://github.com/trpc/trpc/issues/2342",0.7031269,["Reactive",2964],{},["Set"],["ShallowReactive",2967],{"$fTRc1wZytZ_XrK4EfJfei_Sz-An4H4Yy6syhVxH_PVJc":-1,"$f7i07gFuE0tcPdf9Xw4KoPYnQII8a2RNy5ckE41qAQJM":-1},"/trpc/trpc-openapi/390"]