T
tRPC

'useInfiniteQuery' hook disappeared after moving to Turborepo

'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,
});
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(...)
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",
}),
],
});
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 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 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,
});
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)
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 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 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;
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;
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;
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;
I'm happy to share the repo
Nnlucas4/23/2023
Can you show trpc.ts? Might be to do with t setup
FFroge4/23/2023
FFroge4/23/2023
Is pretty out of the box I think I've only added the admin middleware
Nnlucas4/23/2023
Oh discord doesn’t like text files on mobile 🫠 Best to paste a code block
FFroge4/23/2023
Sec I'll remove comments
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);
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 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 Do both projects have strict enabled?
FFroge4/23/2023
Everything has strict enabled
Nnlucas4/23/2023
Cool 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 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 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 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 It's fairly minimal anyways, if I take it back any further I'll be stripping out the monorepo Hol' up - might have some version mismatching going on 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 !

Looking for more? Join the community!

T
tRPC

'useInfiniteQuery' hook disappeared after moving to Turborepo

Join Server
Recommended Posts
convert the result to date objectsI am not sure if this is even trpcs responsibility but I would like to get my date objects as date oECONNREFUSED with TRPC call on VercelAnyone run into this before? I just deployed my app to Vercel and I run into this error when I triggInvalid ValDoes TRPC string input have a limit? https://prnt.sc/KlXlyoGrzP8P Edit: It was actually from stripis possible to combine next-minimal-starter with react-router-dom ?Hi, I'm trying to combine https://github.com/trpc/trpc/tree/main/examples/next-minimal-starter and rusing same query for entries appHow to use same query for many components? I don't want to request api for many times ;-; I can't pHow can I make a direct fetch on a router endpoint from TRPC in NextJS on client?In the documentation you can use the vanilla TRPC client like this: ``` const bilbo = await clientHow to get unwrapped errors out of proxy clientI'm using sveltekit and in order to redirect from SSR you need to throw an error: https://kit.sveltCan you get the queryClient without using a hook?Can you get the queryClient without using a hook?How can I reset the cursor when using useInfiniteQuery?I have various filters that I can set for the query, but when setting those filters I need to reset How does routing work in tRPC especially when using merged routers?I am having trouble understanding how tRPC lays out routes. Let's say I have the below ```typescripwhere is useQuery [key]?Learning trpc, i use to tanstack with queryKey, can not find how that works with trpc, if i want tuseInfinieQuery with initialData, Refetch only after second change of inputsHi everyone, I have the problem as stated in the title. My code looks like the following: ```ts Adding tRPC HOC breaks zustandIt seems that adding the trpc hoc breaks my zustand stores initial values and generates bad data. AfNext.js + tRPC, multitenancy, access Next.js Router when setting tRPC headersHey all! I'm writing a multi-tenant solution. Most Next.js pages lie under the `/pages/[tenantKey]/`getInfiniteData returns undefinedI am using the t3-stack. Whenever I am calling getInfiniteData it returns undefined. ``` console.loEnable both `useQuery` and a raw `query` from the frontend (for use in async validation)Hello everyone, some context: ``` . I'm building a project with a variant of the t3 stack (key pointNextjs http endpoint (no prisma)I have simple pages/api/todoEndpint how do i call this endpoint with trcp? I don't want to use prismAdding clerk auth object to createServerSideHelpersSo I followed clerk's docs for TRPC (https://clerk.com/docs/nextjs/trpc) and I added `auth` to the `Call retries were exceeded with ssgI'm running a `create-t3-app` on Node 18. Has anyone seen errors trying to use SSG with TRPC and PrHow does batching work in SSR & nextjs app directory?I'm currently playing with the app directory for nextjs. If I have a SSR client set up like this: `