0

I am new to TRPC and have set up a custom hook in my NextJS app to make queries. This hook is sending out a query to generateRandomWorker but the response always returns a generic 500 error. I am completely stuck until I can figure out this issue.

The hook:

// filepath: src\utilities\hooks\useCreateRandomWorker.ts

type ReturnType = {
    createWorker: () => Promise<Worker>,
    isCreating: boolean,
}

const useCreateRandomWorker = (): ReturnType => {

    const [isCreating, setIsCreating] = useState(false);

    const createWorker = async (): Promise<Worker> => {

        setIsCreating(true);

        const randomWorker: CreateWorker = await client.generateRandomWorker.query(null);

        const createdWorker: Worker = await client.createWorker.mutate(randomWorker);

        setIsCreating(false);

        return createdWorker;
    }

    return { createWorker, isCreating };

Here is the router. I know the WorkerService calls work because they are returning the proper values when passed into getServerSideProps which directly calls them. WorkerService.generateRandomWorker is synchronous, the others are async.

// filepath: src\server\routers\WorkerAPI.ts

export const WorkerRouter = router({
  generateRandomWorker: procedure
        .input(z.null()) // <---- I have tried completely omitting `.input` and with a `null` property
        .output(PrismaWorkerCreateInputSchema)
        .query(() => WorkerService.generateRandomWorker()),
  getAllWorkers: procedure
        .input(z.null())
        .output(z.array(WorkerSchema))
        .query(async () => await WorkerService.getAllWorkers()),
  createWorker: procedure
        .input(PrismaWorkerCreateInputSchema)
        .output(WorkerSchema)
        .mutation(async ({ input }) => await WorkerService.createWorker(input)),
});

The Next API listener is at filepath: src\pages\api\trpc\[trpc].ts

When the .input is omitted the request URL is /api/trpc/generateRandomWorker?batch=1&input={"0":{"json":null,"meta":{"values":["undefined"]}}} and returns a 500.

When the .input is z.null() the request URL is /api/trpc/generateRandomWorker?batch=1&input={"0":{"json":null}} and returns a 500.

Can anyone help on what I might be missing?

Additional Info

The client declaration.

// filepath: src\utilities\trpc.ts

export const client = createTRPCProxyClient<AppRouter>({
    links: [
        httpBatchLink({
            url: `${getBaseUrl() + trpcUrl}`, // "http://localhost:3000/api/trpc"
            fetch: async (input, init?) => {
                const fetch = getFetch();
                return fetch(input, {
                    ...init,
                    credentials: "include",
                })
            }
        }),
    ],
    transformer: SuperJSON,
});

The init:

// filepath: src\server\trpc.ts

import SuperJSON from "superjson";
import { initTRPC } from "@trpc/server";

export const t = initTRPC.create({
    transformer: SuperJSON,
});

export const { router, middleware, procedure, mergeRouters } = t;
Ken
  • 91
  • 4
  • The way you are calling your mutate functions looks weird to me. I usually find them in the shape of `const { mutate } = client.procedure.useMutation()` (this is a hook call, so at the root of your custom hook or of your component), and then you can call `mutate({ /* args */ })` from anywhere. Calling it like that also gives you `isMutating` for your "isCreating" state, and `mutateAsync` if you still need to await it (`const { mutateAsync, isMutating } = client.procedure.useMutation()`) – Sheraff Jan 24 '23 at 22:31
  • I believe the way you are describing is if I was using TRPC as hooks. I tried that originally but React didn't like hooks inside hooks, so I'm using it as the vanilla client. `client.procedure` is not an available option and `useMutation` is not an option off of `client.createWorker` – Ken Jan 25 '23 at 00:57
  • tRPC is made to be used in react. If "React didn't like hooks inside hooks" it's because you weren't using it correctly. Every procedure has ways to be called *as a hook* or in "non-hook places" like inside a function call or inside a useEffect. You might have a particular use-case, but it's probably not "react not liking it". – Sheraff Jan 25 '23 at 20:51
  • I like clean code and the way that I would have had to structure the Next trpc queries would have been messy, from what I was reading in the docs, since I wanted to call them on button press. I haven't seen anything that says using the vanilla client is bad practice. I am more than willing to check out any examples you have to demonstrate your point, but at the moment, I am still stuck with the queries failing. – Ken Jan 26 '23 at 00:16

2 Answers2

0

Sorry I am not familiar with the vanilla client. But since you're in react you might be interested in some ways you can call a trpc procedure from anywhere while using the react client:

By using the context you can pretty much do anything from anywhere:

    const client = trpc.useContext()
    const onClick = async () => {
        const data = await client.playlist.get.fetch({id})
    }

For a known query, you can disable it at declaration and refetch it on demand

    const {refetch} = trpc.playlist.get.useQuery({id}, {enabled: false})
    const onClick = async () => {
        const data = await refetch()
    }

If your procedure is a mutation, it's trivial, so maybe you can turn your GET into a POST

    const {mutateAsync: getMore} = trpc.playlist.more.useMutation()
    const onClick = async () => {
        const data = await getMore({id})
    }
Sheraff
  • 5,730
  • 3
  • 28
  • 53
0

Answered.

Turns out I was missing the export for the API handler in api/trpc/[trpc].ts

Ken
  • 91
  • 4