clerk auth default metadata

hello, to start this of some basic info: i am currently working on a learning project which is a sass using react, next.js and clerk auth with the t3 stack i currently am implementing a sort of token system (and basic role system but thats not very important) utilizing clerks user metadata feature for that as i currently am not using a stand alone db. i was wondering is there was a simple way to add default metadata when a user is created for example when a user is created the start with 100 tokens? currently i am utilizing private metadata for this and the structure looks like this: { "role": "admin", "tokens": 99999 } and utilize these functions for basic manipulation of that metadata:
export async function GetTokenAmount(userId: string) {
const user = await clerkClient().users.getUser(userId);
const data = user.privateMetadata;
const tokenAmount = data.tokens as number;

return tokenAmount;
}
export async function GetTokenAmount(userId: string) {
const user = await clerkClient().users.getUser(userId);
const data = user.privateMetadata;
const tokenAmount = data.tokens as number;

return tokenAmount;
}
export async function AddTokens(userId: string, AddedAmount: number) {
// TODO: Add validation! (Most Likely Stripe)
// const user = await clerkClient().users.getUser(userId);
const currentTokenAmount = GetTokenAmount(userId);
const newTokenAmount = (await currentTokenAmount) + AddedAmount;

if (AddedAmount <= 0) {
throw new Error("Invalid amount");
} else {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: newTokenAmount,
},
});
}
}
export async function AddTokens(userId: string, AddedAmount: number) {
// TODO: Add validation! (Most Likely Stripe)
// const user = await clerkClient().users.getUser(userId);
const currentTokenAmount = GetTokenAmount(userId);
const newTokenAmount = (await currentTokenAmount) + AddedAmount;

if (AddedAmount <= 0) {
throw new Error("Invalid amount");
} else {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: newTokenAmount,
},
});
}
}
export async function RemoveTokens(userId: string, RemovedAmount: number) {
const currentTokenAmount = GetTokenAmount(userId);
const newTokenAmount = (await currentTokenAmount) - RemovedAmount;

if (RemovedAmount <= 0) {
throw new Error("Invalid amount");
} else {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: newTokenAmount,
},
});
}
}
export async function RemoveTokens(userId: string, RemovedAmount: number) {
const currentTokenAmount = GetTokenAmount(userId);
const newTokenAmount = (await currentTokenAmount) - RemovedAmount;

if (RemovedAmount <= 0) {
throw new Error("Invalid amount");
} else {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: newTokenAmount,
},
});
}
}
Users: User metadata
Metadata allows for custom data to be saved on the User object.
Solution:
after some messing around came up with a much simpler solution to use at least for the time being basically when retrieving the tokens in the navbar if it returns as undefined i run a nonblocking function to create the metadata and temporarily show the default amount ```export async function GetTokenAmount(userId: string) { const user = await clerkClient().users.getUser(userId); const data = user.privateMetadata; const tokenAmount = data.tokens as number; ...
Jump to solution
4 Replies
Juraj98
Juraj984mo ago
As far as I'm aware, there is no way to configure Clerk to add metadata by default for new users. There are few possible solutions that I can think of: 1. You can setup webhooks. The problem with this approach is that when user first signs up, you'll probably want to redirect them somewhere immediately. When you do that, the metadata will not be present, as it takes a while (few milliseconds, but still) for the webhook to execute. So this is probably not suitable, but I'm mentioning it, just in case you figure out how to use this method. 2. Instead of adding the metadata by default, you could add some button or even popup with "Claim free tokens" button. When users press this button, you can execute BE function to add those metadata. 3. You can leverage custom onboarding flow. This way, you can redirect users to some page (for example "/welcome") where there will be loading spinner with some text "setting up your profile" and run BE function when they visit the page. After metadata are set, you can redirect users to actual page. Let me know if you go this route, there is an issue with the code in the guide, but I have this setup in my own up, so I can provide you with workaround if needed. 4. You could reverse your counting logic. Instead of setting up certain amount of tokens and subtracting, keep track of "used free tokens". Lets say users start with 100 free tokens, if there is no metadata present, you know they have 100 free tokens left. Once they use some tokens, you just add the number to metadata and calculate remaining tokens - const freeTokens = 100 - (metadata?.usedFreeTokens ?? 0)
TwelthDd
TwelthDdOP4mo ago
hi, thanks for the response i think i will try to go with the first solution you provided and in the little tokens ui default it to 100 tokens if no value is found with that user id whilst the webhook takes its time and if that doesn't work for whatever reason i will probably the third option
Solution
TwelthDd
TwelthDd4mo ago
after some messing around came up with a much simpler solution to use at least for the time being basically when retrieving the tokens in the navbar if it returns as undefined i run a nonblocking function to create the metadata and temporarily show the default amount
export async function GetTokenAmount(userId: string) {
const user = await clerkClient().users.getUser(userId);
const data = user.privateMetadata;
const tokenAmount = data.tokens as number;

if (tokenAmount === undefined) {
// Call CreateTokenMetadata but don't await it to make it non-blocking
CreateTokenMetadata(userId).catch((err) => {
console.error("Error creating token metadata:", err);
});
return 100; // Immediately return the default value without waiting
}

return tokenAmount;
}
export async function GetTokenAmount(userId: string) {
const user = await clerkClient().users.getUser(userId);
const data = user.privateMetadata;
const tokenAmount = data.tokens as number;

if (tokenAmount === undefined) {
// Call CreateTokenMetadata but don't await it to make it non-blocking
CreateTokenMetadata(userId).catch((err) => {
console.error("Error creating token metadata:", err);
});
return 100; // Immediately return the default value without waiting
}

return tokenAmount;
}
export async function CreateTokenMetadata(userId: string) {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: 100,
},
});
}
export async function CreateTokenMetadata(userId: string) {
await clerkClient.users.updateUserMetadata(userId, {
privateMetadata: {
tokens: 100,
},
});
}
TwelthDd
TwelthDdOP4mo ago
wich seems to work and actually create the metadata and not just display the default amount
No description
Want results from more Discord servers?
Add your server