#useConvexMutation giving data as unknown

28 messages · Page 1 of 1 (latest)

gentle prairie
#

This is my mutation

  const signUp = useMutation({
    mutationFn: useConvexMutation(api.users.create),
    onError: (error) => {
      if (error instanceof ConvexError) {
        toast.error(error.data as string);
      } else {
        toast.error("Something went wrong");
      }
    },
    onSuccess(data) {
      toast.success(data.message); // Data is unknown
      setStep("sign-in");
    },
  });

but if I do this

const mutation = useConvexMutation(api.users.create);
  const signUp = useMutation({
    mutationFn: mutation,
    onError: (error) => {
      if (error instanceof ConvexError) {
        toast.error(error.data as string);
      } else {
        toast.error("Something went wrong");
      }
    },
    onSuccess(data) {
      toast.success(data.message); // Data is correctly type
      setStep("sign-in");
    },
  });
hazy scaffoldBOT
#

Hi @gentle prairie. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

I see you're having an issue with TypeScript typing when using useConvexMutation with TanStack Query's useMutation. This is a known issue where the return type information isn't properly preserved in the first approach.

The issue you're experiencing is related to how type information flows through the wrapper function. In your first example, the type information is lost when you directly pass useConvexMutation(api.users.create) as the mutationFn, resulting in data being typed as unknown.

Your second approach correctly preserves the type information by first assigning the mutation to a variable and then using that variable as the mutationFn. This is the recommended way to maintain proper typing.

This issue is similar to what was reported in a Discord thread where a user found that return types weren't being properly typed when using wrapper functions with Convex mutations Returning data from Tanstack Query useMutation wrapper.

For proper typing with TanStack Query and Convex, you should continue using the second approach:

const mutation = useConvexMutation(api.users.create);
const signUp = useMutation({
  mutationFn: mutation,
  // rest of your code...
});

This ensures that TypeScript correctly infers the return type from your Convex mutation function, giving you proper type safety and autocompletion in your onSuccess handler.

If you need to handle errors from your Convex mutations, you're already correctly using the ConvexError class as shown in the Application Errors documentation.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gentle prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gentle prairie
#

@hazy scaffold how can I prefetch query in nextjs with tanstack-query

import { convexQuery } from "@convex-dev/react-query";
import { api } from "~/convex/_generated/api";
import { HydrateClient } from "~/lib/query/server";
import { getQueryClient } from "~/lib/query/shared";
import { Test } from "./test";

const queryClient = getQueryClient();

const HomePage = () => {
  void queryClient.prefetchQuery(convexQuery(api.users.get, {}));

  return (
    <HydrateClient>
      <Test />
    </HydrateClient>
  );
};

export default HomePage;
"use client";

import { useAuthActions } from "@convex-dev/auth/react";
import { convexQuery } from "@convex-dev/react-query";
import { useSuspenseQuery } from "@tanstack/react-query";

import { Button } from "~/components/ui/button";
import { api } from "~/convex/_generated/api";

export const Test = () => {
  const { signOut } = useAuthActions();
  const { data: user } = useSuspenseQuery(convexQuery(api.users.get, {}));
  console.log(user);
  return (
    <div>
      Hello, {user?.name} ({user?.username})
      <Button onClick={signOut}>Sign Out</Button>
    </div>
  );
};

This is giving me error in convexQuery

hazy scaffoldBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I can see you're trying to prefetch a Convex query in Next.js using TanStack Query, but you're running into an error. Let me help you with this issue.

The problem you're encountering is related to how pagination and server-side rendering work with Convex queries. When using prefetchQuery with Convex in Next.js, there are some limitations to be aware of.

According to the Discord discussions, you can't directly preload a paginated query from Convex on the server side. This is because Convex cursors contain session-specific information that doesn't transfer properly between server and client environments Discord support thread.

For your specific code, there are a few approaches you can take:

  1. Use Convex's built-in preloading mechanism instead of TanStack Query's prefetching:
// Server component
import { preloadQuery } from "convex/nextjs";
import { api } from "~/convex/_generated/api";
import { Test } from "./test";

const HomePage = async () => {
  const preloadedUsers = await preloadQuery(api.users.get, {});
  
  return <Test preloadedUsers={preloadedUsers} />;
};

export default HomePage;

// Client component (Test.tsx)
"use client";
import { usePreloadedQuery } from "convex/react";

export const Test = ({ preloadedUsers }) => {
  const user = usePreloadedQuery(preloadedUsers);
  // rest of your component
};

This approach is documented in the Next.js Server Rendering guide.

  1. For TanStack Query specifically, you might need to use the TanStack Start approach which has better integration between Convex and TanStack Query:
#
// In your router setup
const convexQueryClient = new ConvexQueryClient(CONVEX_URL);
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryKeyHashFn: convexQueryClient.hashFn(),
      queryFn: convexQueryClient.queryFn(),
    },
  },
});
convexQueryClient.connect(queryClient);

This is shown in the TanStack Start Quickstart.

The error you're seeing might also be related to type issues with convexQuery and useSuspenseQuery. There was a recent fix in version @convex-dev/[email protected] that addressed compatibility issues between these functions Discord support thread.

Make sure you're using the latest version of @convex-dev/react-query and check if your error persists. If it does, please share the specific error message so I can provide more targeted help.

Remember that the TanStack Query adapter for Convex is still in beta, so some features might not work perfectly yet.

Would you be able to share the specific error message you're getting with convexQuery?

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gentle prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gentle prairie
#

In const preloadedUsers = await preloadQuery(api.users.get, {}); how can I pass token?

#

@hazy scaffold In const preloadedUsers = await preloadQuery(api.users.get, {}); how can I pass token?

hazy scaffoldBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To pass an authentication token with preloadQuery in Next.js, you need to include it in the third parameter of the function. Here's how you can do it:

const token = await getAuthToken(); // Get your auth token
const preloadedUsers = await preloadQuery(
  api.users.get, 
  {}, 
  { token }
);

The third parameter of preloadQuery accepts a NextjsOptions object where you can specify the authentication token. This is documented in the Next.js Server Rendering documentation.

The implementation of getAuthToken() depends on your authentication provider. For example, if you're using Clerk:

// app/auth.ts
import { auth } from "@clerk/nextjs/server";

export async function getAuthToken() {
  return (await (await auth()).getToken({ template: "convex" })) ?? undefined;
}

Or if you're using Auth0:

// app/auth.ts
import { getSession } from '@auth0/nextjs-auth0';

export async function getAuthToken() {
  const { accessToken } = await getSession();
  return accessToken;
}

If you're using Convex Auth, you would use the convexAuthNextjsToken function:

import { convexAuthNextjsToken } from "@convex-dev/auth/nextjs/server";

// In your Server Component
const preloadedUsers = await preloadQuery(
  api.users.get,
  {},
  { token: await convexAuthNextjsToken() }
);

This approach ensures that your server-side queries are properly authenticated when fetching data from Convex.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gentle prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gentle prairie
#

@hazy scaffold can I use preloaded query with tanstack query?

hazy scaffoldBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Based on the knowledge sources, there are two different approaches for preloading data in Next.js with Convex - one using Convex's native preloading mechanism and another using TanStack Query. However, there isn't a direct integration between Convex's preloadQuery and TanStack Query.

For TanStack Query with Convex, you would use the @convex-dev/react-query adapter which provides its own integration pattern:

import { ConvexQueryClient } from "@convex-dev/react-query";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ConvexProvider } from "convex/react";

// Setup
const convexQueryClient = new ConvexQueryClient(convex);
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryKeyHashFn: convexQueryClient.hashFn(),
      queryFn: convexQueryClient.queryFn(),
    },
  },
});
convexQueryClient.connect(queryClient);

Then you would use TanStack Query's own mechanisms for data fetching:

import { useQuery } from "@tanstack/react-query";
import { convexQuery } from "@convex-dev/react-query";
import { api } from "../convex/_generated/api";

// In your component
const { data, isPending, error } = useQuery(
  convexQuery(api.functions.myQuery, { id: 123 })
);

According to a Discord thread, one Convex team member mentioned they're working on implementing a pattern that would allow server-side rendering with TanStack Query, but it's not yet available as a standard feature:

"Community Member 669 it's helpful to hear this specifically requested! This is a pattern that makes sense. I'll probably implement it in TanStack Start first where we have more reliable data injection SSR primitives"

If you need server-side rendering with data preloading today, the recommended approach is to either:

#
  1. Use Convex's native preloadQuery and usePreloadedQuery without TanStack Query
  2. Use TanStack Query's useSuspenseQuery with Convex as mentioned in one of the Discord threads

The TanStack Query adapter for Convex is still in beta, so some advanced features like combining preloaded queries with TanStack Query are still being developed.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gentle prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gentle prairie
#

@hazy scaffold can you give me threads with useSuspenseQuery usage

hazy scaffoldBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I'll share some examples of useSuspenseQuery usage with Convex from the knowledge sources provided:

Basic Usage with TanStack Start

From the TanStack Start Quickstart documentation, here's a basic example of using useSuspenseQuery with Convex:

import { convexQuery } from "@convex-dev/react-query";  
import { useSuspenseQuery } from "@tanstack/react-query";  
import { createFileRoute } from "@tanstack/react-router";  
import { api } from "../../convex/_generated/api";  
  
export const Route = createFileRoute("/")({  
  component: Home,  
});  
  
function Home() {  
  const { data } = useSuspenseQuery(convexQuery(api.tasks.get, {}));  
  
  return (  
    <div>  
      {data.map(({ _id, text }) => (  
        <div key={_id}>{text}</div>  
      ))}  
    </div>  
  );  
}

TanStack Start Quickstart

Using with Route Loaders

Here's an example showing how to use useSuspenseQuery with TanStack Router's loader for preloading data:

export const Route = createFileRoute('/posts')({  
  loader: async (opts) => {  
    await opts.context.queryClient.ensureQueryData(  
      convexQuery(api.messages.list, {}),  
    );  
  };  
  component: () => {  
    const { data } = useSuspenseQuery(convexQuery(api.messages.list, {}));  
    return (  
      <div>  
        {data.map((message) => (  
          <Message key={message.id} post={message} />  
        ))}  
      </div>  
    );  
  },  
})

TanStack Start documentation

Multiple Queries with useSuspenseQueries

From a Discord thread, here's an example of using multiple queries with useSuspenseQueries:

#
const [
    { data: sales },
    { data: orders },
    { data: activeArtworks },
    { data: revenue },
] = useSuspenseQueries({
    queries: [
        {
            ...convexQuery(api.overview.getArtistMonthlySales, {}),
        },
        {
            ...convexQuery(api.overview.getArtistMonthlyOrders, {}),
            staleTime: Number.POSITIVE_INFINITY,
        },
        {
            ...convexQuery(api.overview.getArtistActiveArtworks, {}),
            staleTime: Number.POSITIVE_INFINITY,
        },
        {
            ...convexQuery(api.overview.getArtistMonthsRevenue, {}),
            staleTime: Number.POSITIVE_INFINITY,
        },
    ],
});

Discord support thread

Using with ID Parameters

Here's an example showing how to use useSuspenseQuery with ID parameters:

function Home() {
  const { data } = useSuspenseQuery(
    convexQuery(api.users.getUserById, {
      id: "some_id_here" as Id<"users">,
    }),
  );

  console.log(data);

  return <div>test</div>;
}

Discord support thread

Handling Loading States

From another Discord thread discussing loading states:

const { data } = useSuspenseQuery(convexQuery(api.network.getDeviceTypes, {}));

The thread notes that unlike regular useQuery, useSuspenseQuery will suspend rendering until data is available, which is why you don't need to handle the undefined state.
Discord support thread

These examples should give you a good starting point for using useSuspenseQuery with Convex in your application.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gentle prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gentle prairie
#

@hazy scaffold can't I use useSuspenseQuery with nextjs

hazy scaffoldBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You can definitely use useSuspenseQuery with Next.js and Convex, but there are some important considerations to keep in mind.

When using Next.js with Convex, you have two main approaches for data fetching with suspense support:

  1. Next.js-specific approach: Using Convex's built-in Next.js integration with preloadQuery and usePreloadedQuery
  2. TanStack Query approach: Using useSuspenseQuery from TanStack Query with Convex

For the TanStack Query approach in Next.js, you need to:

  1. Make sure your component is a Client Component by adding "use client" at the top
  2. Set up the TanStack Query provider in your layout
  3. Use useSuspenseQuery with convexQuery

Here's a basic example:

"use client";

import { convexQuery } from "@convex-dev/react-query";
import { useSuspenseQuery } from "@tanstack/react-query";
import { api } from "~/convex/_generated/api";
import { Suspense } from "react";

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserData />
    </Suspense>
  );
}

function UserData() {
  const { data } = useSuspenseQuery(convexQuery(api.users.get, {}));
  
  return <div>Hello, {data.name}</div>;
}

The key difference between this approach and using Convex's native Next.js integration is that with useSuspenseQuery, the component will suspend on the client side during initial render, while preloadQuery/usePreloadedQuery allows you to fetch the data during server-side rendering.

If you need server-side rendering with suspense, the Convex team is working on better integration between TanStack Query and server components, but it's not fully implemented yet. As mentioned in one of the Discord threads, this is a pattern they're planning to implement in TanStack Start first.

#

For now, if you need server-side rendering with Convex data, the recommended approach is to use Convex's native Next.js integration with preloadQuery and usePreloadedQuery as described in the Next.js Server Rendering documentation.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: