'useInfiniteQuery' hook disappeared after moving to Turborepo

Ffroge4/23/2023
I am using Turborepo with Next.js with the following layout, originally a T3 app
- /apps/app
- /packages/api

The useInfiniteQuery hook seems to have dissapeared after migrating, and I can't get it back.
I am using @trpc/next on the clientside, @trpc/react-query is installed, and everything seems fine?

I'd love some guidance, I'm a little stuck...

/apps/app/src/utils/api.ts
import { httpBatchLink, loggerLink } from "@trpc/client";
import { createTRPCNext } from "@trpc/next";
import superjson from "superjson";

import { type AppRouter } from "@myapp/api";

const getBaseUrl = () => {
    if (typeof window !== "undefined") return ""; // browser should use relative url
    if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
    return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
};

    config() {
        return {
            transformer: superjson,
            links: [
                loggerLink({
                    enabled: (opts) =>
                        process.env.NODE_ENV === "development" ||
                        (opts.direction === "down" &&
                            opts.result instanceof Error),
                }),
                httpBatchLink({
                    url: `${getBaseUrl()}/api/trpc`,
                }),
            ],
        };
    },
    ssr: false,
});


/packages/api/src/routers/job.ts
    findMany: publicProcedure
        .input(
            z.object({
                where: JobPostWhereInputSchema.optional(),
                take: z.number().min(1).max(100).optional(),
                cursor: z.string().nullish(),
                order: JobPostOrderByWithRelationInputSchema.optional(),
            })
        )
        .query(...)

`/packages/api/src/root.ts
import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
import superjson from "superjson";
import { job } from "./routers/job";
import { profile } from "./routers/profile";
import { createTRPCRouter } from "./trpc";

export const appRouter = createTRPCRouter({
    profile,
    job,
});

export type AppRouter = typeof appRouter;

export const client = createTRPCProxyClient<AppRouter>({
    transformer: superjson,
    links: [
        httpBatchLink({
            url: "http://localhost:3000/api/trpc",
        }),
    ],
});
Ffroge4/23/2023
Ffroge4/23/2023
Interesting - it might be something to do with the tsconfig I added strict: true through in the child tsconfigs even though it's enabled at a top level.
And the job. disappeared from the router...

Still investigating
Ffroge4/23/2023
Okay, so turns out in my hate of setting up turborepo, strict mode wasn't enforced, that's enabled now. But what's odd, is the api.job is now never type 0_o
Ffroge4/23/2023
And now finally at what appears to the be root of the issue

export const api = createTRPCNext<AppRouter>({
    config() {
        return {
            /**
             * Transformer used for data de-serialization from the server.
             *
             * @see https://trpc.io/docs/data-transformers
             **/
            transformer: superjson,

            /**
             * Links used to determine request flow from client to server.
             *
             * @see https://trpc.io/docs/links
             * */
            links: [
                loggerLink({
                    enabled: (opts) =>
                        process.env.NODE_ENV === "development" ||
                        (opts.direction === "down" &&
                            opts.result instanceof Error),
                }),
                httpBatchLink({
                    url: `${getBaseUrl()}/api/trpc`,
                }),
            ],
        };
    },
    /**
     * Whether tRPC should await queries when server rendering pages.
     *
     * @see https://trpc.io/docs/nextjs#ssr-boolean-default-false
     */
    ssr: false,
});


Throws:
Type 'CreateRouterInner<RootConfig<{ ctx: { req: NextApiRequest; res: NextApiResponse; session: Session | null; prisma: PrismaClient<PrismaClientOptions, never, RejectOnNotFound | ... 1 more ... | undefined>; }; meta: object; errorShape: { ...; }; transformer: typeof SuperJSON; }>, { ...; }>' does not satisfy the constraint 'Router<AnyRouterDef<AnyRootConfig, any>>'.
  The types returned by 'createCaller(...)' are incompatible between these types.
    Type '{ query: inferHandlerFn<{}>; mutation: inferHandlerFn<{}>; subscription: inferHandlerFn<{}>; } & DecoratedProcedureRecord<{ profile: CreateRouterInner<RootConfig<{ ctx: { req: NextApiRequest; res: NextApiResponse; session: Session | null; prisma: PrismaClient<...>; }; meta: object; errorShape: { ...; }; transformer:...' is not assignable to type '{ query: inferHandlerFn<any>; mutation: inferHandlerFn<any>; subscription: inferHandlerFn<any>; } & DecoratedProcedureRecord<any>'.
      Type '{ query: inferHandlerFn<{}>; mutation: inferHandlerFn<{}>; subscription: inferHandlerFn<{}>; } & DecoratedProcedureRecord<{ profile: CreateRouterInner<RootConfig<{ ctx: { req: NextApiRequest; res: NextApiResponse; session: Session | null; prisma: PrismaClient<...>; }; meta: object; errorShape: { ...; }; transformer:...' is not assignable to type '{ query: inferHandlerFn<any>; mutation: inferHandlerFn<any>; subscription: inferHandlerFn<any>; }'.
        Types of property 'query' are incompatible.
          Type 'inferHandlerFn<{}>' is not assignable to type 'inferHandlerFn<any>'.
            Types of parameters 'path' and 'path' are incompatible.
              Type 'TPath' is not assignable to type 'never'.
                Type 'string' is not assignable to type 'never'.ts(2344)
Nnlucas4/23/2023
This tracks. The cursor needs to be there and if the parent object is nullable it doesn’t get detected. Strict null checks needs to be on at minimum and a new repo might not have that
Ffroge4/23/2023
Yeah strict mode enabled through and through now.
Was a red herring.

Now having issues with the appRouter...
Nnlucas4/23/2023
An you share your backend setup?
Ffroge4/23/2023
It's a next app, the appRouter and server are in their own package in a turborepo'd monorepo
Ffroge4/23/2023
tRPC runs on the next app, not on an independent service
Ffroge4/23/2023
Nnlucas4/23/2023
Right so the error actually starts in your AppRouter I think
Nnlucas4/23/2023
Ignore the frontend errors until that’s fixed
Ffroge4/23/2023
100%
Nnlucas4/23/2023
What does the AppRouter look like? Might need to see quite a bit of the setup. Try commenting up sub routes until the error goes away
Ffroge4/23/2023
That's the app router.

Let me comment out the sub routes and see what happens.
import { company } from "./routers/company";
import { job } from "./routers/job";
import { profile } from "./routers/profile";
import { createTRPCRouter } from "./trpc";

/**
 * This is the primary router for your server.
 *
 * All routers added in /api/routers should be manually added here.
 */
export const appRouter = createTRPCRouter({
    profile,
    job,
    company,
});

// export type definition of API
export type AppRouter = typeof appRouter;
Ffroge4/23/2023
So I've just changed it to this, removing my procedures and sub-routes.

Same errors.
import { createTRPCRouter } from "./trpc";

import { z } from "zod";

import { protectedProcedure, publicProcedure } from "./trpc";

export const exampleRouter = createTRPCRouter({
    hello: publicProcedure
        .input(z.object({ text: z.string() }))
        .query(({ input }) => {
            return {
                greeting: `Hello ${input.text}`,
            };
        }),
    getSecretMessage: protectedProcedure.query(() => {
        return "you can now see this secret message!";
    }),
});

/**
 * This is the primary router for your server.
 *
 * All routers added in /api/routers should be manually added here.
 */
export const appRouter = createTRPCRouter({
    exampleRouter,
});

// export type definition of API
export type AppRouter = typeof appRouter;
Ffroge4/23/2023
I'm happy to share the repo
Nnlucas4/23/2023
Can you show trpc.ts?
Nnlucas4/23/2023
Might be to do with t setup
Ffroge4/23/2023
Ffroge4/23/2023
Is pretty out of the box
Ffroge4/23/2023
I think I've only added the admin middleware
Nnlucas4/23/2023
Oh discord doesn’t like text files on mobile 🫠
Nnlucas4/23/2023
Best to paste a code block
Ffroge4/23/2023
Sec
Ffroge4/23/2023
I'll remove comments
Ffroge4/23/2023
import { prisma } from "@app/db";

import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type Session } from "next-auth";

import { getServerAuthSession } from "@app/auth";

export type CreateContextOptions = {
    session: Session | null;
};

const createInnerTRPCContext = (opts: CreateContextOptions) => {
    return {
        session: opts.session,
        prisma,
    };
};

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
    const { req, res } = opts;

    // Get the session from the server using the getServerSession wrapper function
    const session = await getServerAuthSession({ req, res });

    return {
        ...createInnerTRPCContext({
            session,
        }),
        req,
        res,
    };
};

import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

const t = initTRPC.context<typeof createTRPCContext>().create({
    transformer: superjson,
    errorFormatter({ shape, error }) {
        return {
            ...shape,
            data: {
                ...shape.data,
                zodError:
                    error.cause instanceof ZodError
                        ? error.cause.flatten()
                        : null,
            },
        };
    },
});

export const createTRPCRouter = t.router;

export const publicProcedure = t.procedure;

/** Reusable middleware that enforces users are logged in before running the procedure. */
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
    if (!ctx.session || !ctx.session.user) {
        throw new TRPCError({ code: "UNAUTHORIZED" });
    }
    return next({
        ctx: {
            // infers the `session` as non-nullable
            session: { ...ctx.session, user: ctx.session.user },
        },
    });
});


export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);

const enfroceUserIsAdmin = t.middleware(({ ctx, next }) => {
    if (!ctx.session?.user.isAdmin) {
        throw new TRPCError({ code: "UNAUTHORIZED" });
    }
    return next({
        ctx: {
            // infers the `session` as non-nullable
            session: { ...ctx.session, user: ctx.session.user },
        },
    });
});

export const adminProcedure = t.procedure.use(enfroceUserIsAdmin);
Nnlucas4/23/2023
It’s definitely a weird one
Nnlucas4/23/2023
I don’t see anything obviously wrong but it’s hard to tell on mobile
Ffroge4/23/2023
It's super weird... Is it a typescript or linting issue? As that's all I've been playing around with
Nnlucas4/23/2023
The images you shared with the errors are probably quite useful
Nnlucas4/23/2023
Do both projects have strict enabled?
Ffroge4/23/2023
Everything has strict enabled
Nnlucas4/23/2023
Cool
Nnlucas4/23/2023
I’ll need to have another look a bit later when I’m on desktop
Ffroge4/23/2023
I'll go through and even do it on a package level tsconfig, not just the base
Ffroge4/23/2023
Thank you! Much appreciated!

If you want the source code, I'm happy to share the repo with you
Nnlucas4/23/2023
Definitely make sure it’s not being switched off in any way. Base should be fine though
Nnlucas4/23/2023
The repo would probably help. If you have time try and put together a minimal reproduction
Ffroge4/23/2023
You saw my antics before of chasing down tsconfig issues, so it could be, but I'm doubtful
Ffroge4/23/2023
I'm based in AUS so it'll likely be tomorrow.

I can share you the repo that it is now, if you shoot through your GH
Ffroge4/23/2023
It's fairly minimal anyways, if I take it back any further I'll be stripping out the monorepo
Ffroge4/23/2023
Hol' up - might have some version mismatching going on
Ffroge4/23/2023
Wow, okay... so two packages had different version of @trpc/next and @trpc/server
Must've happened when splitting out, I've done some more consolidating, brining the next client into the api package, wowweee


Edit: And strict mode, obviously... shit breaks without it (but we know this, this is just for the others experiencing weirdness, if it's weird it's usually versions or strict mode)
Nnlucas4/23/2023
Brilliant, that was one thing I was going to check 😃
Ffroge4/23/2023
Appreciate it @nlucas !