Peform
Peform
TtRPC
Created by Peform on 5/3/2025 in #❓-help
trpc caching procedure calls for queries in the same batch
Hello, I am using nextjs and trpc server components, using trpc to prefetch queries. All of the requests on this page are protected by a procedure called protectedGuildPermissionsProcedure which essentially checks that the user has the right permissions to access the data that is returned.
await Promise.all([
api.guild.get.prefetch({ guildId }),
api.guild.channels.prefetch({ guildId }),
api.guild.roles.prefetch({ guildId }),
api.applicationPanel.list.prefetch({ guildId }),
api.application.applicationExists.prefetch({ guildId }),
]);
await Promise.all([
api.guild.get.prefetch({ guildId }),
api.guild.channels.prefetch({ guildId }),
api.guild.roles.prefetch({ guildId }),
api.applicationPanel.list.prefetch({ guildId }),
api.application.applicationExists.prefetch({ guildId }),
]);
procedure:
export const guildProcedure = protectedProcedure
.input(z.object({ guildId: z.string() }))
.use(async ({ ctx, next, input }) => {
const hasPermissions = await someExpensiveOperationToCheckPermissions(input.guildId)
if(!hasPermissions) {
throw new TRPCError({ code: 'FORBIDDEN', message: 'You do not have permission to access this guild' });
}

return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
});
export const guildProcedure = protectedProcedure
.input(z.object({ guildId: z.string() }))
.use(async ({ ctx, next, input }) => {
const hasPermissions = await someExpensiveOperationToCheckPermissions(input.guildId)
if(!hasPermissions) {
throw new TRPCError({ code: 'FORBIDDEN', message: 'You do not have permission to access this guild' });
}

return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
});
Is there some way of telling TRPC to cache the response from the procedure for all batched requests so wont do the same permission check, even though it has already been done? As it currently stands, this expensive procedure will run 5 times, one for each api request that is prefetched. Yes, I could cache this in redis (which I do) but if there is no cache to quickly see if they have permissions each query will have to run the expensive function and it could make the page take up to 30 seconds to load. I'm thinking I could use some sort of redis lock, but it still seems a little inefficient having 5 queries that run at he same time (same trpc batch) all getting the same data from redis?
9 replies
TtRPC
Created by Peform on 4/28/2025 in #❓-help
mutation taking a long time to appear after prefetching query
Hello, I'm am currently having an issue where if my isCaptchaValid endpoint errors (using throw new TRPCError(...)) the error message takes a long time to come through, it appears that it is linked to the endpoints execution time, as if I add some code to wait 2 seconds before executing it dramatically increases the time it takes for the error to come back. I am using nextjs loading.tsx which is how the loading animation works, so as soon as the loading animation is gone it means that TRPC data has finished fetching, so the error message should be appearing instantly? In the below server component you can see I am using prefetch and in the client component you can see I am using useQuery, which should not be refetching on first render since I used prefetch function. Server component:
export default async function Page({ params }: Props) {
const { guildId, uuid } = await params;
const session = await auth();
await api.verification.isCaptchaValid.prefetch({ guildId, uuid });
if (!session?.user.accountProviderId) redirect(`/login?callbackUrl=/verify/${guildId}/${uuid}`);

return (
<HydrateClient>
<div className="container flex flex-1 items-center justify-center">
<Content guildId={guildId} uuid={uuid} />
</div>
</HydrateClient>
);
}
export default async function Page({ params }: Props) {
const { guildId, uuid } = await params;
const session = await auth();
await api.verification.isCaptchaValid.prefetch({ guildId, uuid });
if (!session?.user.accountProviderId) redirect(`/login?callbackUrl=/verify/${guildId}/${uuid}`);

return (
<HydrateClient>
<div className="container flex flex-1 items-center justify-center">
<Content guildId={guildId} uuid={uuid} />
</div>
</HydrateClient>
);
}
client component:
export const Content = ({ guildId, uuid }: Props) => {
const { data: isCaptchaValid, error: invalidCaptchaError } = api.verification.isCaptchaValid.useQuery({
guildId,
uuid,
});

if (!isCaptchaValid)
return (
<div className="flex items-center gap-5">
<UserXIcon className="h-12 w-12 text-destructive" />
<div>
<h1 className="text-2xl font-semibold">Invalid Captcha</h1>
<p>{invalidCaptchaError?.message}</p>
</div>
</div>
);

return <h1>Success</h1>
};
export const Content = ({ guildId, uuid }: Props) => {
const { data: isCaptchaValid, error: invalidCaptchaError } = api.verification.isCaptchaValid.useQuery({
guildId,
uuid,
});

if (!isCaptchaValid)
return (
<div className="flex items-center gap-5">
<UserXIcon className="h-12 w-12 text-destructive" />
<div>
<h1 className="text-2xl font-semibold">Invalid Captcha</h1>
<p>{invalidCaptchaError?.message}</p>
</div>
</div>
);

return <h1>Success</h1>
};
2 replies
TtRPC
Created by Peform on 3/1/2025 in #❓-help
server side prefetch + optional client side refetch
Hello, I'm looking for advice on a pattern for server-side prefetching with optional client-side refreshing in Next.js. Current setup: - Next.js app with tRPC - A getUserGuilds procedure that accepts an optional refetch parameter - When refetch: true, it fetches fresh data from Discord, otherwise uses cached data - Server-side prefetch for initial load, need client sided optional refresh Current solution: I'm using two separate queries: 1. First query with no parameters matches server prefetch for initial load: server prefetch:
await api.discord.getUserGuilds.prefetch();
await api.discord.getUserGuilds.prefetch();
const { data: guilds } = api.discord.getUserGuilds.useQuery(undefined, { enabled: true });
const { data: guilds } = api.discord.getUserGuilds.useQuery(undefined, { enabled: true });
2. Second query with refetch: true for manual refreshing:
const { data: refetchedGuilds, refetch } = api.discord.getUserGuilds.useQuery(
{ refetch: true },
{ enabled: false }
);
const { data: refetchedGuilds, refetch } = api.discord.getUserGuilds.useQuery(
{ refetch: true },
{ enabled: false }
);
3. Using separate useEffects to update UI state when either dataset changes
export const ServerList = () => {
const { data: guilds } = api.discord.getUserGuilds.useQuery(undefined, {
enabled: true,
});
const {
data: refetchedGuilds,
refetch:,
isFetching,
} = api.discord.getUserGuilds.useQuery({ refetch: true }, { enabled: false });
const [filteredGuilds, setFilteredGuilds] = useState(guilds);
const [search, setSearch] = useState('');

useEffect(() => {
setFilteredGuilds(
guilds?.filter(
({ name, permissions }) => permissions & 0x20 && name.toLowerCase().includes(search.toLowerCase().trim()),
),
);
}, [search, guilds]);

useEffect(() => {
if (refetchedGuilds) {
setFilteredGuilds(refetchedGuilds.filter(({ name, permissions }) => permissions & 0x20 && name.toLowerCase().includes(search.toLowerCase().trim())));
}
}, [refetchedGuilds]);
export const ServerList = () => {
const { data: guilds } = api.discord.getUserGuilds.useQuery(undefined, {
enabled: true,
});
const {
data: refetchedGuilds,
refetch:,
isFetching,
} = api.discord.getUserGuilds.useQuery({ refetch: true }, { enabled: false });
const [filteredGuilds, setFilteredGuilds] = useState(guilds);
const [search, setSearch] = useState('');

useEffect(() => {
setFilteredGuilds(
guilds?.filter(
({ name, permissions }) => permissions & 0x20 && name.toLowerCase().includes(search.toLowerCase().trim()),
),
);
}, [search, guilds]);

useEffect(() => {
if (refetchedGuilds) {
setFilteredGuilds(refetchedGuilds.filter(({ name, permissions }) => permissions & 0x20 && name.toLowerCase().includes(search.toLowerCase().trim())));
}
}, [refetchedGuilds]);
2 replies
TtRPC
Created by Peform on 2/22/2025 in #❓-help
super quick question about prefetching
Is it best to use a promise.all when using multiple instances of prefetch to fetch data on the server to ensure that both queries are running at the same time? From the looks of it trpc handles this automatically so it is unneeded?
await Promise.all([api.guild.get.prefetch({ guildId }), api.application.getList.prefetch({ guildId })]);
await Promise.all([api.guild.get.prefetch({ guildId }), api.application.getList.prefetch({ guildId })]);
1 replies
TtRPC
Created by Peform on 1/27/2025 in #❓-help
trpc useError hook?
Hey, I have a hook which i use for fetching some data using trpc and I'm wondering if there is some sort of useError so i can catch all of the errors for each query, in one variable? or is the best way using the error property on each of the useQuery invocations? Currently, if I want to display an error I'd have to make 3 separate variables and parse each of those in the if statement if error at the bottom.
export const GuildProvider = ({ guildId, children, fetchChannels, fetchRoles }: Props) => {
const isFetching = useIsFetching();
const { data: guild } = api.guild.get.useQuery({
guildId,
});
const { data: channels } = api.guild.channels.useQuery(
{
guildId,
},
{ enabled: fetchChannels },
);
const { data: roles } = api.guild.get.useQuery(
{
guildId,
},
{ enabled: fetchRoles },
);

if (isFetching) {
return <CustomLoader />;
}

if (error) {
return <div>Error fetching guild data: {error.message}</div>;
}
export const GuildProvider = ({ guildId, children, fetchChannels, fetchRoles }: Props) => {
const isFetching = useIsFetching();
const { data: guild } = api.guild.get.useQuery({
guildId,
});
const { data: channels } = api.guild.channels.useQuery(
{
guildId,
},
{ enabled: fetchChannels },
);
const { data: roles } = api.guild.get.useQuery(
{
guildId,
},
{ enabled: fetchRoles },
);

if (isFetching) {
return <CustomLoader />;
}

if (error) {
return <div>Error fetching guild data: {error.message}</div>;
}
1 replies
TtRPC
Created by Peform on 10/13/2024 in #❓-help
onSuccess mutation not being called
Hey, I have this mutation which is called when I click a button. Inside the onSuccess callback I show a toast to the user telling them that the request was successful. However, when I do a state change before I call the mutation, eg setRequests the onSuccess callback never runs, I know this because the console log never appears. But, when I remove the setRequests it does, there is no visible error in my console. Why does this happen? Is it by design?
export const GenerationsCard = ({ generation, selectedFlag, setRequests }: Props) => {
const clearMutation = api.generations.clear.useMutation();
const warnMutation = api.punishments.warn.useMutation();
const banMutation = api.punishments.ban.useMutation();

const handleBanGeneration = async (requestId: string, requestType: string) => {
setRequests((prev) => prev?.filter((req) => req.id !== requestId));
await banMutation.mutateAsync(
{ requestId, requestType },
{
onSuccess: (response) => {
console.log(234243243);
toast('Success', {
description: response.message,
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
});
},
onError: (error) => {},
},
);
};
export const GenerationsCard = ({ generation, selectedFlag, setRequests }: Props) => {
const clearMutation = api.generations.clear.useMutation();
const warnMutation = api.punishments.warn.useMutation();
const banMutation = api.punishments.ban.useMutation();

const handleBanGeneration = async (requestId: string, requestType: string) => {
setRequests((prev) => prev?.filter((req) => req.id !== requestId));
await banMutation.mutateAsync(
{ requestId, requestType },
{
onSuccess: (response) => {
console.log(234243243);
toast('Success', {
description: response.message,
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
});
},
onError: (error) => {},
},
);
};
2 replies
TtRPC
Created by Peform on 8/7/2024 in #❓-help
useQuery `onSuccess` callback depreciated
Hello. I've just found out that the onSuccess callback in useQuery has been depreciated. What should I be using instead?
This callback will fire any time the query successfully fetches new data.

@deprecated — This callback will be removed in the next major version.
This callback will fire any time the query successfully fetches new data.

@deprecated — This callback will be removed in the next major version.
const {
data: fetchedUserData,
refetch: refetchUserData,
isFetching: fetchingUserData,
} = api.userLookup.find.useQuery(
{
userId: form.getValues("userId"),
},
{
enabled: !!form.getValues("userId"),
onSuccess: (data) => { // depreciated
setUserData(data);
}
},
);
const {
data: fetchedUserData,
refetch: refetchUserData,
isFetching: fetchingUserData,
} = api.userLookup.find.useQuery(
{
userId: form.getValues("userId"),
},
{
enabled: !!form.getValues("userId"),
onSuccess: (data) => { // depreciated
setUserData(data);
}
},
);
5 replies
TtRPC
Created by Peform on 6/6/2024 in #❓-help
TRPC response data changing to undefined when typing in a form field.
Hello, I'm currently having a very strange issue, I am using a form to input some data and send off to a trpc endpoint to retrieve some data from the server when I click the submit button. However, the respnose userData variable is being changed to undefined whenever I type into the form field. I am not sure why this is happening as I do not mutate userData anywhere, and there are no new TRPC queries running (verified checking network tab) and the TRPC endpoint is set to disabled. So it will only fetch data once I call the refetch function... Anyone have any idea?
4 replies
TtRPC
Created by Peform on 6/5/2024 in #❓-help
conditionally fetching data with a useQuery
Hello, From my understanding a useMutation is primarily for updating/deleting/creating a record. And a useQuery is primarily used for fetching data from the server. However, when it comes to conditionally fetching data from the server I belive the syntax for a useMutation is much more ideal? I'm not sure if I am missing something, or if in this scenario it would be better to use a useMutation. The issue is, I only want to query the API when the user presses a specific button and once they have input a userId to search. I also want to display a toast (notification) when the request has finished. However, the onSuccess and onError callbacks are depreciated, meaning I have to now use a useEffect. This is greatly overcomplicating everything. I'm not sure if there is a better way of doing this? Any advice would be appreciated.
3 replies
TtRPC
Created by Peform on 5/10/2024 in #❓-help
uploading image via trpc endpoint
Hello, is there any example for uploading an image to a trpc endpoint?
5 replies