Help using convex with clerk
Note: am new to convex.
I follow the document on how to use clerk with convex I successfully see the user info in clerk but not in convex and I even try to debug using clerk debug method still not working despite the fact that I connect my front with convex using npx convex dev.
46 Replies
Code:
this is for user.ts in convex folder:
import { mutation } from "./_generated/server";
export const store = mutation({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
console.log("server identity", await ctx.auth.getUserIdentity());
if (!identity) {
throw new Error("Called storeUser without authentication present");
}
// Check if we've already stored this identity before.
const user = await ctx.db
.query("users")
.filter((q) => q.eq(q.field('tokenIdentifer'), identity.tokenIdentifier))
.unique();
if (user !== null) {
// If we've seen this identity before but the name has changed, patch the value.
if (user.name !== identity.name) {
await ctx.db.patch(user._id, { name: identity.name });
}
return user._id;
}
// If it's a new identity, create a new
User
.
return await ctx.db.insert("users", {
name: identity.name!,
tokenIdentifier: identity.tokenIdentifier,
});
},
});
This is the useStoreUser hook:
import { useUser } from "@clerk/clerk-react";
import { useConvexAuth } from "convex/react";
import { useEffect, useState } from "react";
import { useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
import { Id } from "../convex/_generated/dataModel";
export default function useStoreUserEffect() {
const { isAuthenticated } = useConvexAuth();
const { user } = useUser();
// When this state is set we know the server
// has stored the user.
const [userId, setUserId] = useState<Id<"users"> | null>(null);
const storeUser = useMutation(api.user.store);
// Call the storeUser
mutation function to store
// the current user in the users
table and return the Id
value.
useEffect(() => {
// If the user is not logged in don't do anything
if (!isAuthenticated) {
return;
}
// Store the user in the database.
// Recall that storeUser
gets the user information via the auth
// object on the server. You don't need to pass anything manually here.
async function createUser() {
const id = await storeUser();
setUserId(id);
}
createUser();
return () => setUserId(null);
// Make sure the effect reruns if the user logs in with
// a different identity
}, [isAuthenticated, storeUser, user?.id]);
return userId;
}
How I handle the clerk login/signup
import { cn } from "@/lib/utils";
import "./btn.css";
import React from "react";
import { useSignIn } from "@clerk/nextjs";
// function SSOCallback() {
// return <AuthenticateWithRedirectCallback afterSignInUrl={'/verifications'}/>;
// }
export const CustomBtn = ({
children,
className,
}: {
children?: any;
className?: any;
}) => {
const { signIn } = useSignIn();
const handleGitHubSignUp = async () => {
await signIn?.authenticateWithRedirect({
strategy: "oauth_github",
redirectUrl: "/",
redirectUrlComplete: "/",
});
console.log("Added successfully");
};
return (
<main>
<button className={cn("btn", className)} onClick={handleGitHubSignUp}>
{children}
</button>
</main>
);
};
I would be happy to provide any code that may help in resolving thishi @Devben . did you follow all the debugging steps here? https://docs.convex.dev/auth/debug
Debugging Authentication | Convex Developer Hub
You have followed one of our authentication guides but something is not working.
in our experience it's almost always one of those steps
Yes you’re right I it’s the first one. I can’t see my log in the log dashboard
And my code isn’t throwing any error in the front end browser console neither backend console
So am kind of lost
@Devben Antonio uses Convex with Clerk in this video. It might be helpful to you https://www.youtube.com/watch?v=ADJKbuayubE
Code With Antonio
YouTube
Build a Real-Time Miro Clone With Nextjs, React, Tailwind (2024)
⭐️ Source Code & More: https://www.codewithantonio.com/projects/miro-clone
💬 Discord & Help: https://www.codewithantonio.com/discord
Clerk: https://dub.sh/mz7fwfW
Convex: https://convex.dev/c/cwa
Liveblocks: https://liveblocks.io/
Logoipsum: https://logoipsum.com/
Sally 3D illustrations: https://www.figma.com/community/file/890095002328610853/s...
Thanks man, just check it out, but he doesn’t store the user credentials in convex
And connecting convex with clerk isn’t a problem but I can’t see the user credentials
@jamwt @erquhart @ian please guys I need help on this, the hackathon will end soon
Ohh gotcha
But I do saw the usestore hook I created in convex function
GitHub
convex-nextjs-app-router-demo/convex/schema.ts at main · get-convex...
Demo showing a Next.js App Router app powered by Convex backend - get-convex/convex-nextjs-app-router-demo
This one stores it in db
But can’t see the user credentials that’s the issue and I am not seeing any error in the console
Okay thanks man I’ll check
😇😇
@erquhart this is my user.ts in convex folder
import { v } from "convex/values";
import { QueryCtx, mutation, query } from "./_generated/server";
export const store = mutation({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new Error("Called storeUser without authentication present");
}
const user = await getUser(ctx, identity.nickname!);
if (user !== null) {
if (
user.name !== identity.name
user.username !== identity.nickname
user.pictureUrl !== identity.pictureUrl ||
user.tokenIdentifier !== identity.tokenIdentifier
) {
await ctx.db.patch(user._id, {
tokenIdentifier: identity.tokenIdentifier,
name: identity.name,
username: identity.nickname,
pictureUrl: identity.pictureUrl,
});
}
return user._id;
}
// If it's a new identity, create a new
User
.
return await ctx.db.insert("users", {
tokenIdentifier: identity.tokenIdentifier,
name: identity.name!,
username: identity.nickname!,
pictureUrl: identity.pictureUrl!,
numPosts: 0,
});
},
});
export const get = query({
args: {
username: v.string(),
},
handler: async (ctx, args) => {
return await getUser(ctx, args.username);
},
});
export async function getUser(ctx: QueryCtx, username: string) {
return await ctx.db
.query("users")
.withIndex("username", (q) => q.eq("username", username))
.unique();
}
But I can't see any user credential in convex
I use github auth in clerkFirst thing you want to do is use logging to find where your code is not doing what you expect. Can you confirm that the mutation is running at all?
It's definitely running
To make it easier to debug
That shows you're running dev successfully, but is the mutation actually running when you call it from your ui
I saw the function I created in convex function
Your code you originally pasted had a console log with server identity, was that log not printing out at all? Or printing but the value was undefined?
The issue now is I can't see any user credential in convex dashboard so.... there's no way I can call the user without the usere credential being store in database
Yes I followed the convex debug method In convex docs
But I can’t seems to see any log in convex dashboard log
I followed this steps
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
We need to be clear on what you mean by "can't see any log in convex dashboard"
If you put a console log as the first line in the mutation, and then run the mutation from your frontend, does it log?
If not that means you're not actually calling the mutation from your frontend
Still not working
Remeber am new lol 😂
No problem lol
Can you paste the frontend code that you're using to run the mutation
Also, if you paste code, put triple backticks before and after (```), just helps with readability.
hmm nothing is running at all?
This is my convex dashboard logs .. after adding the console at the top
Can you paste the react code where you're running the mutation
import { v } from "convex/values";
import { QueryCtx, mutation, query } from "./_generated/server";
export const store = mutation({
args: {}, handler: async (ctx) => { console.log("server identity", await ctx.auth.getUserIdentity()); const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Called storeUser without authentication present"); } const user = await getUser(ctx, identity.nickname!); if (user !== null) { if ( user.name !== identity.name user.username !== identity.nickname user.pictureUrl !== identity.pictureUrl || user.tokenIdentifier !== identity.tokenIdentifier ) { await ctx.db.patch(user._id, { tokenIdentifier: identity.tokenIdentifier, name: identity.name, username: identity.nickname, pictureUrl: identity.pictureUrl, }); } return user._id; } // If it's a new identity, create a new
args: {}, handler: async (ctx) => { console.log("server identity", await ctx.auth.getUserIdentity()); const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Called storeUser without authentication present"); } const user = await getUser(ctx, identity.nickname!); if (user !== null) { if ( user.name !== identity.name user.username !== identity.nickname user.pictureUrl !== identity.pictureUrl || user.tokenIdentifier !== identity.tokenIdentifier ) { await ctx.db.patch(user._id, { tokenIdentifier: identity.tokenIdentifier, name: identity.name, username: identity.nickname, pictureUrl: identity.pictureUrl, }); } return user._id; } // If it's a new identity, create a new
User
.
return await ctx.db.insert("users", {
tokenIdentifier: identity.tokenIdentifier,
name: identity.name!,
username: identity.nickname!,
pictureUrl: identity.pictureUrl!,
numPosts: 0,
});
},
});
export const get = query({
args: {
username: v.string(),
},
handler: async (ctx, args) => {
return await getUser(ctx, args.username);
},
});
export async function getUser(ctx: QueryCtx, username: string) {
return await ctx.db
.query("users")
.withIndex("username", (q) => q.eq("username", username))
.unique();
}That's your convex code - where in your frontend are you calling the function?
I haven’t use it yet in my front end I want to make sure it store the user credentials before using it in my front end
But with this convex code I should be able to see the user credentials if I login or sign up right ?
Only if you call the mutation, if you're authenticating directly through Clerk nothing will happen in Convex.
The above code is just creating functions - the functions have to be executed somewhere
Have you walked through the basic Convex/Clerk auth docs?
https://docs.convex.dev/auth/clerk
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
Now am seeing bug
Unhandled Runtime Error
Error: [CONVEX Q(user:get)] [Request ID: U8BLF4acYTjrzAV8] Server Error
ArgumentValidationError: Object is missing the required field
username
. Consider wrapping the field validator in v.optional(...)
if this is expected.
Object: {}
Validator: v.object({username: v.string()})
Called by client
I just use it as an example here
'use client'
import { Input } from "@/components/ui/input";
import React from "react";
import { BackgroundBeams } from "@/components/ui/background-beams";
import { Div } from "@/components/ui/moving-borders";
import {UserButton } from "@clerk/nextjs";
import { TextRevealCard } from "@/components/ui/text-reveal";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
const Developers = ({ username }: { username: string }) => {
const user = useQuery(api.user.get, {username});
return (
<React.Fragment>
<main className="h-screen w-full rounded-md bg-neutral-950 relative flex flex-col items-center antialiased">
<div className="h-screen w-full max-w-lg text-white P-4">
<div className="flex justify-between items-center p-4 relative z-10">
<h1 className="text-2xl p-2">LetCollab</h1>
<UserButton afterSignOutUrl="/" />
</div>
<div className="px-6">
<Input
type="text"
placeholder="Search"
className="rounded-lg p-7 border border-neutral-800 focus:ring-2 focus:ring-teal-500 w-full relative z-10 mt-4 bg-neutral-950 placeholder:text-neutral-700 text-white"
/>
<section>
<h1 className="text-2xl text-white">
This is where the user will be
</h1>
<h2>@{user?.username}</h2>
</section>
</div>
</div>
<BackgroundBeams />
</main>
</React.Fragment>
);
};
export default Developers;Ah that's a good sign
I'm sorry for disturbing
not disturbing at all, this is what support forum is for 👍
So this is progress! It's trying to insert the user, but you're missing the
username
field and your schema requires it. So you'll either want to make sure you include a username or make the field optional in your schema. If any of that doesn't make sense I can break it down further.Yes please do
my schema.ts file
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
users: defineTable({
// Unique identifier from the auth provider
tokenIdentifier: v.string(),
name: v.string(),
username: v.string(),
pictureUrl: v.string(),
numPosts: v.number(),
})
.index("tokenIdentifier", ["tokenIdentifier"])
.index("username", ["username"]),
posts: defineTable({
authorId: v.id("users"),
text: v.string(),
}).index("authorId", ["authorId"]),
});
Yesssss brother I see some logs lol 😂
hahahaha
In your convex code, where you're inserting the user, look at the line where you're passing in
username
- you're using the nickname
property of the identity
object
That property can be undefined, which is why you had to use an exclamation point to make the typescript error go away
If you want your users to have unique, required usernames, you'll want to make sure you have usernames enabled in Clerk, and then use that instead of the nicknameI don't want that
I use github auth
You mean here right?
export const get = query({
args: {
username: v.string(),
},
handler: async (ctx, args) => {
return await getUser(ctx, args.username);
},
});
No, in the insert:
That's where you're creating the user
If you don't want them to have a username, drop the username line
You'll also need to remove the username field from your users table in the schema
But if this is all a part of a template, you'll want to search the code and see where the username is being used in the app, as those parts will break once the field is removed
I'm doing something like this where I can be able to get the user username and name
hi. I'll try to catch up and help
in general, make sure you understand your typescript errors (or lints) before you silence them
b/c often we'll find we work on an issue with someone and we end up tracing the root cause back to an error or warning that told them what the problem was earlier
I strongly recommend you always have your projects be 100% clean of lints warning and errors b/c understanding them and fixing them is usually key to mastering the programming model and libraries you're working with. and warnings/errors will re-emerge as true bugs, that are more mysterious and harder to debug a little further into your project
Part of the confusion is error silencing in the template, so it probably feels like a pattern to follow. I believe OP is using this one: https://github.com/get-convex/convex-demos/blob/d4b6b97ce839ade881109653a6ffe8ea5ff4d082/users-and-clerk/convex/users.ts#L39
fair point!
Thank you so much Jamie, I’ll try to always avoid the errors am a baby developer lol 😂
@erquhart Thank you so much for yesterday, so.... the demo you sent show the name of the person that login but I can't seems to find it in the database
This is how I use this in my frontend for example
<span className="text-white text-4xl">Logged in{user!.username ?
as ${user!.fullName}
: ""}</span>