T
tRPC
'useInfiniteQuery' hook disappeared after moving to Turborepo
'useInfiniteQuery' hook disappeared after moving to Turborepo
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",
}),
],
});

Interesting - it might be something to do with the
Throws:
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,
});
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)
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
Yeah strict mode enabled through and through now.
Was a red herring.
Now having issues with the appRouter...
An you share your backend setup?
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
Right so the error actually starts in your AppRouter I think
Ignore the frontend errors until that’s fixed
100%
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
That's the app router.
Let me comment out the sub routes and see what happens.
So I've just changed it to this, removing my procedures and sub-routes.
Same errors.
I'm happy to share the repo
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;
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;
Can you show trpc.ts?
Might be to do with t setup
Is pretty out of the box
I think I've only added the admin middleware
Oh discord doesn’t like text files on mobile ðŸ«
Best to paste a code block
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);
It’s definitely a weird one
I don’t see anything obviously wrong but it’s hard to tell on mobile
It's super weird... Is it a typescript or linting issue? As that's all I've been playing around with
The images you shared with the errors are probably quite useful
Do both projects have strict enabled?
Everything has strict enabled
Cool
I’ll need to have another look a bit later when I’m on desktop
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
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
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)Brilliant, that was one thing I was going to check 😃
Appreciate it @nlucas !
Looking for more? Join the community!
T
tRPC
'useInfiniteQuery' hook disappeared after moving to Turborepo
T
tRPC
'useInfiniteQuery' hook disappeared after moving to Turborepo
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:
`