Sam Whitmore
Sam Whitmore11mo ago

Convex Auth: Implementation with Svelte(Kit)

I've just started learning SvelteKit and Convex, and recently (yesterday) began implementing an auth solution with Clerk. Today, I've woken up to see that Convex Auth is in beta and I feel like the perfect case study – but all the docs are written for React! I'm wondering if Convex Auth is available to use in SvelteKit and, if so, if someone would kindly walk me through the process! Cheers!
19 Replies
Michal Srb
Michal Srb11mo ago
Not supported atm, and it might be a while before we get to Svelte. I personally recommend React over Svelte, unless you're already very invested into Svelte.
Sam Whitmore
Sam WhitmoreOP11mo ago
Fascinating! You’re the first I’ve heard of that sentiment; why do you suggest avoiding Svelte in favour of React? I recently completed a React bootcamp, so I’m rather familiar with it… but pivoted away immediately because of how ‘tangled’ the framework seemed to be. Svelte seemed to be more straightforward, imo; but I’d love to hear yours!
Michal Srb
Michal Srb11mo ago
Svelte is great, and eventually we want it to have the same support as React. Right now Convex works best with React. On the general Svelte vs React, I'd say Svelte simplifies some simpler scenarios, and can give you out-of-the-box better end-user UX. React is more versatile, and much, much more popular (which comes with wider library and component library support).
LeakZ
LeakZ11mo ago
Same here ... love svelte and I'll wait for svelte support , hard to go back after trying svelte, especially version 5 🙂 ❤️
Sam Whitmore
Sam WhitmoreOP11mo ago
I cant wait for Svelte5 to formally release along with a fresh tutorial to introduce Svelte with! I feel like I'm caught between two world atm; but, regardless, I'm not turning my back on the language – just steadily slogging my way through the confusion Whenever Convex Auth supports Svelte(Kit), I'll be in there right away
LeakZ
LeakZ11mo ago
Same here ... for now I'm sticking with supabase until we got more convex & svelte support, can't wait for it since I love convex but the auth part restricted to react is a bit of a no go for me rn . ❤️
Sam Whitmore
Sam WhitmoreOP11mo ago
Convex and Svelte do work really well together! It's just the Auth aspect where they're prioritising React usability (which makes sense due to the ecosystem's size) Until Convex Auth is available to Svelte users, you can use Clerk as a substitute for authentication purposes – that's worked great for me, anyway 🙂
siingers
siingers10mo ago
the docs state you should use a hook from their react library to check for logged-in/out status on the backend over checking with clerk. Did you have any issues handling this? https://docs.convex.dev/auth/clerk#logged-in-and-logged-out-views
Convex & Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
syarif
syarif10mo ago
Hi, I also recently try convex and it works amazing with svelte until I reach the auth. I'll be right there when convex auth svelte is available
ballingt
ballingt10mo ago
Helpful to hear! @syarif you can add Clerk or Auth0 today by getting an identity token and calling convexClient.setAuth(token) with it, but that's more steps that we hope this will take in the future.
Wesley7104
Wesley710410mo ago
I want to say I have fallen in love with Convex and will not be using any other databases from here on out. However, I am a Svelte/SvelteKit only dev after discovering it. I am eagerly awaiting more svelte-friendly documentation within Convex. It would help so much!
syarif
syarif10mo ago
thanks tom, indeed there could be a way, I'm using firebase auth now and I think I can integrate it. but I like the opiniated database of convex, I want opiniated auth as well 😛
Kevin07
Kevin0710mo ago
Svelte + 1:1 react convex support is the dream ✨️✨️
syarif
syarif10mo ago
anyone have tried to use firebase auth with convex?
NazCodeland
NazCodeland2mo ago
hey, is there there an example of this in code by any chance? - I've tried it and const identity = await ctx.auth.getUserIdentity() is returning null
erquhart
erquhart2mo ago
There isn't yet. Can you share some relevant code here?
NazCodeland
NazCodeland2mo ago
Sure, I'll try to make a separate repo later today I'll share this for now, maybe making a repo is unncessary since Im just doing this: src/route/+layout.svelte
<script>
import { setupConvex } from 'convex-svelte';
import { PUBLIC_CONVEX_URL, PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public';
setupConvex(PUBLIC_CONVEX_URL);

let { data } = $props();

</script>


<ClerkProvider {...data} publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
...
</ClerkProvider>
<script>
import { setupConvex } from 'convex-svelte';
import { PUBLIC_CONVEX_URL, PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public';
setupConvex(PUBLIC_CONVEX_URL);

let { data } = $props();

</script>


<ClerkProvider {...data} publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
...
</ClerkProvider>
src/route/+page.svelte
<script>
import { useConvexClient } from 'convex-svelte';
import { useClerkContext } from 'svelte-clerk/client';


const client = useConvexClient();
const ctx = useClerkContext();



const fetchToken = async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
try {
const token = await ctx.session?.getToken({ template: 'convex' });
return token || null;
} catch (error) {
console.error('Error fetching token:', error);
return null;
}
};


$effect(() => {
client?.setAuth(fetchToken, (isAuthenticated: boolean) => {
if (isAuthenticated) {
console.log('isAuthenticated:', isAuthenticated);
}
});
});
</script>
<script>
import { useConvexClient } from 'convex-svelte';
import { useClerkContext } from 'svelte-clerk/client';


const client = useConvexClient();
const ctx = useClerkContext();



const fetchToken = async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
try {
const token = await ctx.session?.getToken({ template: 'convex' });
return token || null;
} catch (error) {
console.error('Error fetching token:', error);
return null;
}
};


$effect(() => {
client?.setAuth(fetchToken, (isAuthenticated: boolean) => {
if (isAuthenticated) {
console.log('isAuthenticated:', isAuthenticated);
}
});
});
</script>
it logs isAuthenticated: true in devtools when I sign into clerk within all my funcitons, such as
export const updateShareVisibility = mutation({
args: {
userId: v.id("users"),
shareVisibility: schema.tables.users.validator.fields.shareVisibility
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
console.log(identity) // logs null
...
export const updateShareVisibility = mutation({
args: {
userId: v.id("users"),
shareVisibility: schema.tables.users.validator.fields.shareVisibility
},
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
console.log(identity) // logs null
...
I've refreshed the page after signing into clerk just incase, it still doesn't recognize that I set clent.setAuth() Followed all the react agnostic steps from https://docs.convex.dev/auth/clerk
erquhart
erquhart2mo ago
Where's the client code where you're calling updateSharedVisibiity, and how do you ensure isAuthenticated is true in the client before calling it
NazCodeland
NazCodeland2mo ago
I have this wrapper
export async function updateConvexShareVisibility(
userId: Id<"users">,
payload: Partial<WithoutSystemFields<Doc<"users">>>
) {
if (payload.shareVisibility) {
await client.mutation(api.mutations.updateShareVisibility, {
userId,
shareVisibility: payload.shareVisibility
});
}
};
export async function updateConvexShareVisibility(
userId: Id<"users">,
payload: Partial<WithoutSystemFields<Doc<"users">>>
) {
if (payload.shareVisibility) {
await client.mutation(api.mutations.updateShareVisibility, {
userId,
shareVisibility: payload.shareVisibility
});
}
};
which I calling from a setter within a class
set shareVisibility(value) {
// avoid double clicks
if (this.shareVisibility === value) return;

this.#shareVisibility = value;
updateLocalStorage('appState', { shareVisibility: value });

const sharingLocation = this.shareVisibility !== 'offline';


// [✅] convex user
// [✅] clerk user
if (this.convexId && this.clerkId) {
console.log("1");
updateConvexShareVisibility(this.convexId, this.buildConvexUser());
}

// [✅] convex user
// [] clerk user
else if (this.convexId) {
console.log("2");
updateConvexShareVisibility(this.convexId, this.buildConvexUser());
}

// [] convex user
// [] clerk user
else if (sharingLocation && !this.convexId) {
console.log("3");
createConvexUser(this.buildConvexUser());
}
}
set shareVisibility(value) {
// avoid double clicks
if (this.shareVisibility === value) return;

this.#shareVisibility = value;
updateLocalStorage('appState', { shareVisibility: value });

const sharingLocation = this.shareVisibility !== 'offline';


// [✅] convex user
// [✅] clerk user
if (this.convexId && this.clerkId) {
console.log("1");
updateConvexShareVisibility(this.convexId, this.buildConvexUser());
}

// [✅] convex user
// [] clerk user
else if (this.convexId) {
console.log("2");
updateConvexShareVisibility(this.convexId, this.buildConvexUser());
}

// [] convex user
// [] clerk user
else if (sharingLocation && !this.convexId) {
console.log("3");
createConvexUser(this.buildConvexUser());
}
}
the logic might be broken within this setter but I just wanted to show you how its being called by the time shareVisibility is called the user is logged in and I have a clerkId, but to answer your other question and how do you ensure isAuthenticated is true in the client before calling it, I don't think I am, I assumed that would be set to true because I am logged in to clerk oops I noticed that within +page.svelte the client was from useConvexClient whereas, the wrapper that are getting called from the class setters, are using client that is from ConvexHttpClient so, I removed the code within src/route/+page.svelte in favour of this within +page.svelte.ts
import type { PageServerLoad } from './$types';
import { PUBLIC_CONVEX_URL } from '$env/static/public';
import { ConvexHttpClient } from 'convex/browser';
import { api } from '../convex/_generated/api';

export const load: PageServerLoad = async ({ locals }) => {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL);

const token = await locals.auth.getToken({ template: 'convex' });
console.log("token", token);
if (token) client.setAuth(token);

const initial = await client.query(api.queries.getUsers, {});
return { initial };
};
import type { PageServerLoad } from './$types';
import { PUBLIC_CONVEX_URL } from '$env/static/public';
import { ConvexHttpClient } from 'convex/browser';
import { api } from '../convex/_generated/api';

export const load: PageServerLoad = async ({ locals }) => {
const client = new ConvexHttpClient(PUBLIC_CONVEX_URL);

const token = await locals.auth.getToken({ template: 'convex' });
console.log("token", token);
if (token) client.setAuth(token);

const initial = await client.query(api.queries.getUsers, {});
return { initial };
};
so now I have if (token) client.setAuth(token); set on client from ConvexHTTPClient which is the same one being used by the wrapper functions that call the convex functions. Still logs null for const identity = await ctx.auth.getUserIdentity();`

Did you find this page helpful?