Michael Schaufelberger
Michael Schaufelberger
TtRPC
Created by Michael Schaufelberger on 1/17/2025 in #❓-help
How to make prefetching/RSC suspense work with auth?
Hi 👋 I'm using the rsc-rq-prefetch pattern in my project. It worked quite well - up until I added auth to my tRPC procedures. During SSR a server error gets thrown because the useSuspenseQuery request fails (there's no auth because SSR does not use the request's context/headers https://github.com/vercel/next.js/discussions/60640). I'm wondering if there's a workaround that allows prefetching on the server for auth-protected procedures. Am I missing something obvious? I've seen the rsc-links example... Maybe I could make use of this? There it seems the server can use the request headers in a procedure. But since it should be a prefetch (and if pending, should be streamed in later) I'm not sure how I can pass such a rsc-linked call to the client in a possible pending state.
2 replies
TtRPC
Created by Michael Schaufelberger on 1/15/2025 in #❓-help
How do *you* structure the tRPC router when dealing with isolated components?
Hi 👋 I'm wondering how other people handle their tRPC router for cases where a component deep in the Next.js file tree (e.g. /[locale]/(dashboard)/categories/posts/comments/list-component/my-procedure.ts ) has a procedure. Everything inside the folder /[locale]/(dashboard)/categories/posts/comments/list-component is a single react component (tree). I.e. I don't share this component anywhere else. But it still has a query procedure to fetch the data we need for it. Now, since the component's usage is very local, it makes little sense to put it into some global list like the tRPC router, but we have to for tRPC to work. How do you structure the tRPC router to make sure such local components are well maintainable?
5 replies
TtRPC
Created by Michael Schaufelberger on 11/27/2024 in #❓-help
Hydration error when using useQuery instead of useSuspenseQuery when prefetching
I have a page
export const PostsPage = async ({ params }: PostsPageProps) => {
trpc.posts.getAll.prefetch({ userId: user.id });

return (
<HydrateClient>
<PostsTable userId={user.id} />
</HydrateClient>
);
};
export const PostsPage = async ({ params }: PostsPageProps) => {
trpc.posts.getAll.prefetch({ userId: user.id });

return (
<HydrateClient>
<PostsTable userId={user.id} />
</HydrateClient>
);
};
and a component
export const PostsTable = ({ userId }: PostsTableProps) => {
const { data: posts, isPending } = trpc.posts.getAll.useQuery({
userId,
});

const { table } = useDataTable({
data: posts ?? [],
});

return (
<DataTable table={table} isLoading={isPending}>
{ /* ... */ }
</DataTable>
);
};
export const PostsTable = ({ userId }: PostsTableProps) => {
const { data: posts, isPending } = trpc.posts.getAll.useQuery({
userId,
});

const { table } = useDataTable({
data: posts ?? [],
});

return (
<DataTable table={table} isLoading={isPending}>
{ /* ... */ }
</DataTable>
);
};
Where the DataTable has some logic like
return isLoading ? (
<TableRow className="hover:bg-transparent">
{table.getLeafHeaders().map((_, i) => (
<TableCell key={i}>
<Skeleton className="h-6 w-full" />
</TableCell>
))}
</TableRow>
) : (
<TableRow>
<TableCell
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
return isLoading ? (
<TableRow className="hover:bg-transparent">
{table.getLeafHeaders().map((_, i) => (
<TableCell key={i}>
<Skeleton className="h-6 w-full" />
</TableCell>
))}
</TableRow>
) : (
<TableRow>
<TableCell
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
Using a useQuery in the PostsTable component results in a Hydration error. Can I prevent this without having to use Suspense? I found using the isPending flag to be a way simpler solution to rendering the skeleton in a deeply nested component.
2 replies
TtRPC
Created by Michael Schaufelberger on 7/25/2024 in #❓-help
Is there a way to call procedures more directly without a router?
I read the blog post about server actions and wondered if there's a way to use a query or mutation more directly. Does it even make sense to use a procedure more directly? I was just wondering how to more closely colocate a procedures usage near a component. About 80% of the time only a single component will need a mutation (e.g. specific form or a certain data table with component specific data).
2 replies
TtRPC
Created by Michael Schaufelberger on 6/21/2024 in #❓-help
How do I use the rsc-rq-prefetch example with a protected procedure?
Thank you for this implementation! It looks sooo promising 😀 However, I ran into an issue when trying it out: https://github.com/trpc/trpc/blob/next/examples/.experimental/next-app-dir/src/app/rsc-rq-prefetch/page.tsx If we have a protected procedure instead of a public one, the server is not authenticated during SSR and an error is thrown. How can we solve this? Can we just ignore the error and will it still work?
24 replies
TtRPC
Created by Michael Schaufelberger on 6/12/2024 in #❓-help
Can I use the "Streaming with Server Components" strategy with tRPC?
https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#streaming-with-server-components has a neat example on how to prefetch queries without having to await them. Is this something we can use with tRPC?
68 replies
TtRPC
Created by Michael Schaufelberger on 5/28/2024 in #❓-help
How to access the request body in the onError callback?
Hi 👋 I'm having trouble getting the request's body (probably plain text) when the following error occurs in the fetchRequestHandler:
TRPCError: "input" needs to be an object when doing a batch call
TRPCError: "input" needs to be an object when doing a batch call
I've tried to get it with req.text() but as it is already consumed, I cannot access it. The code in question:
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => ({}),
onError: async ({ error, req, input }) => {
console.log('TRPC route error', error);

let body = null;

try {
if (req.method === 'POST') {
const isJsonContentType =
!!req.headers.get('Content-Type')?.includes('application/json') ??
false;

body = isJsonContentType ? await req.json() : await req.text();
}
} catch () {
// here it would throw that the body is unusable
}

console.log(
req.method,
req.headers.get('Content-Type'),
{ body, input },
// here: both, body and input are empty
);

// ...
}
}
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => ({}),
onError: async ({ error, req, input }) => {
console.log('TRPC route error', error);

let body = null;

try {
if (req.method === 'POST') {
const isJsonContentType =
!!req.headers.get('Content-Type')?.includes('application/json') ??
false;

body = isJsonContentType ? await req.json() : await req.text();
}
} catch () {
// here it would throw that the body is unusable
}

console.log(
req.method,
req.headers.get('Content-Type'),
{ body, input },
// here: both, body and input are empty
);

// ...
}
}
I would very much like to know what was sent in order to debug - in any case. The body has to be forwarded to the callback by tRPC, correct? Or is there another way?
2 replies
TtRPC
Created by Michael Schaufelberger on 7/21/2023 in #❓-help
Example for tRPC, stable version, with RSC
Hi This may be a stupid question... The readme of the experimental example mentions
Note You can already use tRPC with app directory, by: using @trpc/client directly in components (both RSC and non-RSC) use @trpc/next for client components
What do you mean by this? Do you have an example for that using the current stable version of tRPC? Especially the part for the RSC components. I currently wouldn't mind to use tRPC in a non-optimal RSC way. But I would like to use Next's app directory to colocate files (and not having to have code in a completely different pages directory).
6 replies
TtRPC
Created by Michael Schaufelberger on 6/13/2023 in #❓-help
What's the benefit of using the context instead of a direct import for the database connection?
I've wondered why I should use the tRPC context instead of just importing my database singleton from a module. Is there any benefit to it? Because of TS performance issues, I'm creating multiple functions for bigger procedures. And passing the db connection around may be an unnecessary overhead.
12 replies
TtRPC
Created by Michael Schaufelberger on 5/25/2023 in #❓-help
Best Practice to Fetch a Query OnDemand
What's the best practice to fetch a query on demand? I don't have the context for the query's input in the scope of my component. (Using react-hook-form with a zodResolver that has a dynamic refinement based on the fields submitted and a components prop)
4 replies
TtRPC
Created by Michael Schaufelberger on 5/7/2023 in #❓-help
How can I access the trpc context in zod's refine function?
To do something like
create: myProcedure
.input(myInputSchema.refine(async ({ slug }, ctx) =>
slugDoesNotAlreadyExist(ctx.db, slug),
{
message: 'Slug already exists',
path: ['slug'],
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.insert(...)
}),
create: myProcedure
.input(myInputSchema.refine(async ({ slug }, ctx) =>
slugDoesNotAlreadyExist(ctx.db, slug),
{
message: 'Slug already exists',
path: ['slug'],
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.insert(...)
}),
Edit: It would allow for easier UX because I can use the existing validation code in the frontend to show the user the errors at the correct field.
5 replies