yoniwisin
yoniwisin•5mo ago

Convex + Tanstack Start + Clerk

Hi! I'm trying to add authentication using Clerk to the Trellaux example from the Tanstack-Start site (https://tanstack.com/router/latest/docs/framework/react/examples/start-convex-trellaux). However, the guides from the Convex/Clerk site do not work. ConvexProviderWithClerk seems not supported on Start, so in Convex functions I am not able to get the user identity with ctx.auth.getUserIdentity(). Is there any way to make it work? Thanks ps: I love Convex, nice work 🫶
React TanStack Router Start Convex Trellaux). Example | TanStack Ro...
An example showing how to implement Start Convex Trellaux). in React using TanStack Router.
16 Replies
Convex Bot
Convex Bot•5mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
Facundo
Facundo•5mo ago
I’m running into the same issue. Were you able to find a way around it?
yoniwisin
yoniwisinOP•5mo ago
No solutions right now 😩
1. Move to regular tanstack-router (not start) with convex + clerk 2. Do not use Convex with Clerk: I moved to drizzle + turso + clerk, as clerk is a requirement for me right now
Facundo
Facundo•5mo ago
Thank for getting back to me! Yeah I'll likely have to change stacks as well. I was able to get the userId from clerk in server function and then changed my query to use a userId argument. But you loose the reactivity as it uses the convexhttpclient like the following: const getUserUploadsFn = createServerFn({ method: 'GET' }).handler(async () => { const { userId } = await getAuth(getWebRequest()) if (!userId) { throw redirect({ to: '/' }) } const convex = new ConvexHttpClient(process.env.VITE_CONVEX_URL!) const uploads = await convex.query(api.uploads.listUserUploadsWithId, { userId }) return { uploads, userId } }) export const Route = createFileRoute('/library')({ component: Library, beforeLoad: () => getUserUploadsFn(), loader: ({ context }) => { return { uploads: context.uploads, userId: context.userId } }, }) export function Library() { const { uploads } = Route.useLoaderData()
ballingt
ballingt•5mo ago
Aim is to have support for Convex + Clerk out soon, we've been working with the Clerk team on the "update before hydration finished" errors.
Alan Rubin
Alan Rubin•4mo ago
@ballingt any news on that? Looking forward for a sample project that works using Tanstack start + Clerk + Convex... Thanks
erquhart
erquhart•4mo ago
Latest: https://discord.com/channels/1019350475847499849/1280662608143450112/1316454186627366992 nope actually this is latest: https://discord.com/channels/1019350475847499849/1280662505420623993/1322380531232673792 npm create convex@latest now puts you next 15 w/ clerk 6 (if you choose next and clerk)
ballingt
ballingt•4mo ago
@Alan Rubin are you using Clerk with TanStack? I have a few versions of this prototype of adding Convex, haven't landed on the suggested way to use it yet.
Alan Rubin
Alan Rubin•4mo ago
I tried to use it but got errors when configuring Clerk + Convex + TanStack start. After a couple of hours effort, I ended up using Next.js + Clerk + Convex (based on the generated code by Convex create). It is a shame, I don't like the complexity of Next.js I was hoping to use TanStack Start instead. Surprisingly there seems to be no working sample app using it with Convex and Clerk. Especially since you guys and Clerk are sponsoring TanStack start.
ballingt
ballingt•4mo ago
There is one now! Try npm create convex@latest -- -t tanstack-start-clerk, docs for this coming today.
ballingt
ballingt•4mo ago
Let me know how it goes if you get a chance to try it, I'm writing notes about it now. The are a few steps when you're adding it to an existing project instead of starting with the template.
ballingt
ballingt•4mo ago
npm create convex@latest now offers TanStack as an option with Clerk as the only auth option
No description
ballingt
ballingt•4mo ago
It does require following the normal Clerk + Convex steps described at https://docs.convex.dev/auth/clerk for creating an app and a JWT template, then setting environment variables
too_easy
too_easy•4mo ago
Convex + Clerk + Start is working neatly for us - although setup was tedious at first. I've found this setup at __root works consistently, the other way in the docs was causing issues tbh
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
const [{ convex, queryClient }] = useState(() => {
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
const convexQueryClient = new ConvexQueryClient(convex);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});

convexQueryClient.connect(queryClient);

return {
convex,
queryClient,
};
});

return (
<ClerkProvider>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<QueryClientProvider client={queryClient}>
<html>
<head>
<Meta />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
</QueryClientProvider>
</ConvexProviderWithClerk>
</ClerkProvider>
);
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
const [{ convex, queryClient }] = useState(() => {
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
const convexQueryClient = new ConvexQueryClient(convex);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});

convexQueryClient.connect(queryClient);

return {
convex,
queryClient,
};
});

return (
<ClerkProvider>
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<QueryClientProvider client={queryClient}>
<html>
<head>
<Meta />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
</QueryClientProvider>
</ConvexProviderWithClerk>
</ClerkProvider>
);
}
Then we create a http client to use in server functions like this:
export const createConvexClient = async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const CONVEX_URL = import.meta.env.VITE_CONVEX_URL!;

if (!CONVEX_URL) {
console.error("missing env CONVEX_URL");
}

const { getToken } = await getAuth(getWebRequest());
const token = await getToken({ template: "convex" });

const client = new ConvexHttpClient(CONVEX_URL);

if (token) {
client.setAuth(token);
}

return client;
};
export const createConvexClient = async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const CONVEX_URL = import.meta.env.VITE_CONVEX_URL!;

if (!CONVEX_URL) {
console.error("missing env CONVEX_URL");
}

const { getToken } = await getAuth(getWebRequest());
const token = await getToken({ template: "convex" });

const client = new ConvexHttpClient(CONVEX_URL);

if (token) {
client.setAuth(token);
}

return client;
};
Calling client.mutation(api.doSomething) will now have access to the auth context properly, so no need to pass around userId
ballingt
ballingt•4mo ago
@too_easy do you remember what kind of issues you had with the documented way? Which docs were you following, the template above?

Did you find this page helpful?