A simple package to bridge the gap between TRPC and TanStack Query for Vue much like how TRPC has their own in-house React Query Integration
If you're using @tanstack/vue-query then you might know that working with query keys and query functions can sometimes become cumbersome. A lead maintainer of Tanstack Query, TkDodo, has said that "Separating QueryKey from QueryFunction was a mistake".
So this package tightly couples your keys and functions leading to brilliant DX :rocket:
const currentUserQuery = queryOptions({
queryKey: ["user", "current"],
queryFn: () => trpc.user.current.query(),
});
const { data } = useQuery(currentUserQuery);
const { mutateAsync } = useMutation({
mutationFn: (input: UnwrapRef<typeof form>) => trpc.user.signUp.mutate(input),
onSuccess: async () => {
await useQueryClient().invalidateQueries({
queryKey: currentUserQuery.queryKey,
});
await navigateTo("/onboarding");
},
});
const { data } = useClient().user.current.useQuery();
const { mutateAsync } = useClient().user.signUp.useMutation({
onSuccess: async () => {
await useClient().user.current.invalidate();
await navigateTo("/onboarding");
},
});
pnpm i @colonel-sandvich/trpc-vue-query
// main.ts
import { TrpcVueQueryPlugin } from "@colonel-sandvich/trpc-vue-query";
import { VueQueryPlugin } from "@tanstack/vue-query";
import { httpBatchLink } from "@trpc/client";
import { createApp } from "vue";
import App from "./src/App.vue";
import { trpc } from "your-path-to-trpc-client";
// ^ See https://trpc.io/docs/client/vanilla/setup#3-initialize-the-trpc-client
export const app = createApp(App);
app
.use(VueQueryPlugin) // Make sure {@tanstack/vue-query}'s plugin goes first
.use(TrpcVueQueryPlugin, {
trpcClient: trpc,
})
.mount("#app");
// src/composables/useClient.ts
import { TrpcVueClient, clientKey } from "@colonel-sandvich/trpc-vue-query";
import { inject } from "vue";
import type { AppRouter } from "your-path-to-trpc-app-router-type";
export function useClient() {
return inject(clientKey) as TrpcVueClient<AppRouter>;
}
@tanstack/vue-query for Nuxt if you haven't already// src/plugins/01.vueQueryPlugin.ts
// Important that this plugin comes before the `02.clientPlugin` since that has this plugin as a dependency
export default defineNuxtPlugin((nuxt) => {
nuxt.vueApp.use(VueQueryPlugin);
// Below is for SSR. Remove if you don't need this
// Provided from TanStack Query docs: https://tanstack.com/query/v5/docs/vue/guides/ssr
const vueQueryState = useState<DehydratedState | null>("vue-query");
if (process.server) {
nuxt.hooks.hook("app:rendered", () => {
vueQueryState.value = dehydrate(queryClient);
});
}
if (process.client) {
nuxt.hooks.hook("app:created", () => {
hydrate(queryClient, vueQueryState.value);
});
}
});
// src/plugins/02.clientPlugin.ts
export default defineNuxtPlugin(() => {
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: "/api/trpc",
headers: useRequestHeaders(),
fetch: customFetchWrapper(), // Crucial for SSR
}),
],
});
const client = createTrpcVueClient(trpc, useQueryClient());
return {
provide: {
client,
},
};
});
// src/composables/useClient.ts
export const useClient = () => {
return useNuxtApp().$client;
};
Go check out /examples to see some basic uses.
examples/vue-minimalpnpm i Anywhere
cd examples/vue-minimal
pnpm dev
If ports 3000 (client) and 3001 (server) are available then you should up and running
Please please please absolutely make an issue or PR for any bugs or feature requests, I highly encourage it.
Big thanks to Robert Soriano for his trpc-nuxt package that inspired this package.
$ claude mcp add trpc-vue-query \
-- python -m otcore.mcp_server <graph>