CodingWithJamal
CodingWithJamal17mo ago

Do I need TRPC with Convex?

So i was wondering, since convex is type safe by default and seems to make api request to convex from its hooks, is there a need to use something like trpc while using convex? TRPC + nextjs makes the api routes more type safe, however convex kinda has its own api from the /convex folder when you call api.task.get in your components. So it seems like TRPC would not really be needed.
7 Replies
Michal Srb
Michal Srb17mo ago
Hey @CodingWithJamal , you are right, you don't need TRPC if you can use the ConvexReactClient with the useQuery, useMutation and useAction hooks. These are already both statically type checked with TypeScript and runtime type checked with argument validation.
CodingWithJamal
CodingWithJamalOP17mo ago
OKay great! Also, if I wanted to use convex as an authentication as well how would I manage that on your end? It would seem like I would need to manage the session, cookies, etc myself.
erquhart
erquhart17mo ago
There are guides in the docs for using providers like Clerk and Auth0 (so you don't have to manage everything yourself), or for running your own custom setup if you prefer: https://docs.convex.dev/auth
Authentication | Convex Developer Hub
Add authentication to your Convex app.
Omar Farooq
Omar Farooq17mo ago
NextAuth works great.
CodingWithJamal
CodingWithJamalOP17mo ago
okay thanks So im using clerk but how would I control when a user signs up with clerk, that I could save there auth data into convex as well? Is there a recommended way to sync these to actions?
erquhart
erquhart17mo ago
I have a users table with a field for the user's clerk id, and I run a mutation to update that table whenever authenticated state in the app goes from false to true to keep it synced:
export const updateUser = mutation({
handler: async ({ auth, db, scheduler }) => {
const identity = await auth.getUserIdentity()
if (!identity) {
throw new Error('Not authenticated')
}
if (!identity.email) {
throw new Error('No email address')
}
const user = await db
.query('users')
.withIndex('byClerkId', (q) => q.eq('clerkId', identity.subject))
.unique()
if (!user) {
await db.insert('users', {
clerkId: identity.subject,
email: identity.email,
})
return
}
if (user.email !== identity.email) {
await db.patch(user._id, { email: identity.email })
}
},
})
export const updateUser = mutation({
handler: async ({ auth, db, scheduler }) => {
const identity = await auth.getUserIdentity()
if (!identity) {
throw new Error('Not authenticated')
}
if (!identity.email) {
throw new Error('No email address')
}
const user = await db
.query('users')
.withIndex('byClerkId', (q) => q.eq('clerkId', identity.subject))
.unique()
if (!user) {
await db.insert('users', {
clerkId: identity.subject,
email: identity.email,
})
return
}
if (user.email !== identity.email) {
await db.patch(user._id, { email: identity.email })
}
},
})
I have an Auth provider that I call it from, starts something like this:
const AuthProvider = ({ children }: AuthProviderProps) => {
const { isAuthenticated } = useConvexAuth()
const updateUser = useMutation(api.auth.updateUser)
useEffect(() => {
if (isAuthenticated) {
updateUser()
}
}, [isAuthenticated])
const AuthProvider = ({ children }: AuthProviderProps) => {
const { isAuthenticated } = useConvexAuth()
const updateUser = useMutation(api.auth.updateUser)
useEffect(() => {
if (isAuthenticated) {
updateUser()
}
}, [isAuthenticated])
CodingWithJamal
CodingWithJamalOP17mo ago
Oh okay i see, they you would wrap this in your app so the component tree can revalidate the state if needed

Did you find this page helpful?