Clerk: Wait until Convex also set up the user

Currently, I have this code:
if (!isLoaded) {
setIsLoading(false);
// We probably need a toast showing that the user has to try again or use a better way.
return;
}
const result = await signIn.create({
identifier: values.username + values.usernameId,
password: values.password,
});

if (result.status === "complete") {
await setActive({ session: result.createdSessionId });
void initialConvexSetup();
router.push("/");
}
if (!isLoaded) {
setIsLoading(false);
// We probably need a toast showing that the user has to try again or use a better way.
return;
}
const result = await signIn.create({
identifier: values.username + values.usernameId,
password: values.password,
});

if (result.status === "complete") {
await setActive({ session: result.createdSessionId });
void initialConvexSetup();
router.push("/");
}
If I call the initialConvexSetup function here it's giving me an error that I'm not authenticated. This is because Convex is taking more time for the authentication setup than a clerk. because all other function I run after that initialSetup work. How can I wait until convex also is done with the setup?
54 Replies
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Additionally it seems that I also have to wait for that until I can execute router.push
trace
traceโ€ข9mo ago
What is the initialConvexSetup function doing?
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
It's failing at the unauthenticated step and saying I'm not logged in
export const initialConvexSetup = mutation({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Unauthenticated call to mutation");
}

if (!identity.preferredUsername) {
throw new Error(
"Username is not defined. This is likely an error by us.",
);
}

const newUser = await ctx
.table("users")
.insert({
username: identity.preferredUsername,
clerkId: identity.tokenIdentifier,
})
.get();

const supportChat = await ctx
.table("privateChats")
.insert({ users: [newUser._id], support: true });

const selfChat = await ctx
.table("privateChats")
.insert({ users: [newUser._id, newUser._id], support: false });

await newUser.patch({
chats: { add: [supportChat, selfChat] },
});
},
});
export const initialConvexSetup = mutation({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Unauthenticated call to mutation");
}

if (!identity.preferredUsername) {
throw new Error(
"Username is not defined. This is likely an error by us.",
);
}

const newUser = await ctx
.table("users")
.insert({
username: identity.preferredUsername,
clerkId: identity.tokenIdentifier,
})
.get();

const supportChat = await ctx
.table("privateChats")
.insert({ users: [newUser._id], support: true });

const selfChat = await ctx
.table("privateChats")
.insert({ users: [newUser._id, newUser._id], support: false });

await newUser.patch({
chats: { add: [supportChat, selfChat] },
});
},
});
trace
traceโ€ข9mo ago
Did you set up auth.config.ts correctly? Once I named the file auth.confiig.ts and spend hours try to find the issue lol
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
i guess yes, because it's only for that short time period after that creation (all queries I call in a different page (after sign up) work) (i think convex takes more time than the set active funtion takes.
Gamius
Gamiusโ€ข9mo ago
Hey @erquhart, Ive seen you helped fleetadmiraljakob yesterday on another problem. I work with fleetadmiraljakob on a Chat app. Maybe you can help us with this problem? I hope its okey for you that i pinged you ...
erquhart
erquhartโ€ข9mo ago
taking a look
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
you are such an gigachad
erquhart
erquhartโ€ข9mo ago
i do not know what that means lol where is the initial code running? signIn.create() specifically. Can you provide more of the surrounding code
Gamius
Gamiusโ€ข9mo ago
Can I send you the Github repo?
erquhart
erquhartโ€ข9mo ago
I can help troubleshoot here to an extent, if you want direct assistance you can dm me and we can discuss there, I do contract work. (I'm a community member, not a Convex employee) If you can share a bit more of the code mentioned in my earlier message we can troubleshoot here
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
The docs only show a way to use the signIn Component from clerk. I want a fully customized UI that's the reason why I use the signin.create and setactive function: to only use the logic from clerk/convex and have my own ui
erquhart
erquhartโ€ข9mo ago
I'm using it the way you're describing, let me check it's been so long since I've touched this code - you were totally right, both of those need to be called in the client one sec, comparing what I have to yours is this happening on signup only or signin too?
erquhart
erquhartโ€ข9mo ago
why aren't you using signUp.create()?
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
ohhhh let me check
erquhart
erquhartโ€ข9mo ago
fairly certain that fetch is where things are breaking
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
sure? It was working (without Convex) and only the addition of convex broke things. (But could still be the fetch for sure)
erquhart
erquhartโ€ข9mo ago
convex is a part of your auth flow now, it's hooked into the clerk provider, etc your convex function is where auth is out of sync on signup the fetch is an orphan network call that convex would only learn about after clerk's state updates use signUp.create() to keep convex in the loop
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Now I know why I'm not using signup.create: It's because of some design decision we made. We decided that the username consists of an id and the name. clerk wasn't able to ensure that this is the case so I built the Route handler as a proxy to validate the username schema but maybe that was a stupid design decision
erquhart
erquhartโ€ข9mo ago
Yeah I would have: - each user as a record in a convex users table, with a convex id - a clerkId field on each record to connect each app user to a clerk user - a username field to capture the username not a stupid design decision, just comes with tradeoffs
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
ok, so the tasks that are todo: move the route handler 1:1 to convex? but will that make anything better? because the entry in the users table happens in the initial convex setup
erquhart
erquhartโ€ข9mo ago
Not sure what you mean by make the route handler 1:1 to convex
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
convert it to convex you meant that would make some stuff easier
erquhart
erquhartโ€ข9mo ago
The route handler approach is custom, so doing that means you may encounter unique problems. I'm recommending not doing it custom so you can use established working solutions.
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Okay, I will convert the route handler into a convex action. But I think the original problem remains
erquhart
erquhartโ€ข9mo ago
If you use signUp.create() directly, that should address the problem As far as usernames being a combination of an id+name, i'd personally look for another way to get whatever guarantee you were trying to achieve through that approach. If you can share what you're trying to accomplish there we can discuss what that might look like.
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
you mean why I want username + id? I basically want that people can have the same username (some people want the excact username everywhere). and I thought it makes it harder to just spam random usernames to create a new chat with them
erquhart
erquhartโ€ข9mo ago
So you want to support non-unique usernames You could definitely do that without adding the username to the id
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Yes, but they also need to be identifiable so I added an id that is in combination with the username unique
erquhart
erquhartโ€ข9mo ago
You can represent all of that in-app without modifying the id itself Doing it this way added complexity with Clerk, which is why you had to use a proxy, and now it's adding complexity with Convex. I'm just recommending you consider an alternate approach you can still do everything you're trying to do in your app from a functionality standpoint, but there's more than one way to implement You can definitely continue as planned too, I just don't personally have experience with next.js router to help much there. Definitely doable, I'm sure
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Ok, so there is no identifiable problem with Convex here. I will look how I can do it differently
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
@erquhart Hi, I made a minimal reproduction with signup.create and without an extra validation step on a route handler. https://github.com/FleetAdmiralJakob/minimal-reproduction-convex-clerk/
GitHub
GitHub - FleetAdmiralJakob/minimal-reproduction-convex-clerk
Contribute to FleetAdmiralJakob/minimal-reproduction-convex-clerk development by creating an account on GitHub.
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
In this you can see that the convex mutation setInitialUserUp an error throws because the user is unauthenticated despite waiting for signup.create in the app/page.tsx
trace
traceโ€ข9mo ago
Where is the schema.ts file? Did not see it inside convex folder
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Not needed, it's a minimal reproduction of the issue I did not want to blow anything up, that's why I also removed libaries like react-hook-form so you can focus on the problem
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Should I add a schema? My focus was to prove the point of the error
erquhart
erquhartโ€ข9mo ago
I'll take a look as soon as I'm at my machine Try setting the session id after sign up but before running the mutation although their docs say that's apparently deprecated in favor of setActive(), should work for now
erquhart
erquhartโ€ข9mo ago
I'm also assuming you have verification disabled, but you'd usually have that step in between. Here's their doc on using the isSignUp() hook for custom flows, although I'm sure you've probably already seen it. They use setActive() here in place of setSession(). https://clerk.com/docs/custom-flows/use-sign-up
useSignUp() and useSignIn() | Clerk
Learn how to use the useSignUp() and useSignIn() hooks to build a custom sign-up and sign-in flow.
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
so I should I use setActive after using signup.create?
erquhart
erquhartโ€ข9mo ago
Yep, that should do it
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Seems like this is not possible:
const completedSignUp = await signUp.create({
username: username,
password: password,
});

await setActive({ session: completedSignUp.createdSessionId });
const completedSignUp = await signUp.create({
username: username,
password: password,
});

await setActive({ session: completedSignUp.createdSessionId });
`
No description
erquhart
erquhartโ€ข9mo ago
Can you find out what the actual error is? anything printed in the console? It seems to be pointing to the signUp.create() call
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
Well, it works now. I will update the GitHub Repo and will leave this repo as a showcase for other users who run into this kind of problem. If I update the Repo can you ensure that it is not only me that got it working (luck) but the repo also works on your machine?
erquhart
erquhartโ€ข9mo ago
I can't promise I'll have time to. But to be clear, there isn't anything non-standard in that repo, it's now following the recommended practice from Clerk's own docs.
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
ok, sure for any others scrolling to the end here is the repo: https://github.com/FleetAdmiralJakob/minimal-reproduction-convex-clerk and my other (main) problem (with which everything started) had nothing to do with what I originally thought. the problem was that I just deleted the account to test everything out instead of signing it out properly.
erquhart
erquhartโ€ข9mo ago
no way so it worked fine with the proxy?
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
yes, the proxy works fine and is doing well
erquhart
erquhartโ€ข9mo ago
hahahaha alls well that ends well
FleetAdmiralJakob ๐Ÿ—• ๐Ÿ—— ๐Ÿ—™
yes, just the wrong testing method