Args
Hi, could someone please guide me on this ?
I need to check if the arg I have received is a Id< > or text. if it's text, i create a new one. if it's Id, I do nothing.
Is there a way to check if arg is a Id ?
thank you
9 Replies
Are you trying to check the type of the arg? Better if you could post an example
Hi, sure.
Thanks for reaching out!
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
topic: "",
question:"",
surah:"",
answers:[{text:"",type:""}],
type:""
}, }) so this is my form schema. All of the keys (topic, question, surah,answers,type) are tables in my database. I'm quering them and showing them as a dropdown list to user. So user can either select existing options or create new one. I'm sending the values as it is to the backend. function onSubmit(values: z.infer<typeof formSchema>) {
onCreate(values) console.log(values) } in my admin.ts (convex folder), export const create = mutation({ args: { topic:v.union(v.string(),v.id("Topics")), question:v.union(v.string(),v.id("Questions")), surah:v.id("Surahs"), answer:v.union(v.string(),v.id("Answers")), type:v.union(v.string(),v.id("Types")) }, handler: async (ctx,{topic,question,surah,answer,type}) => {
const topicId = await checkAndCreate(ctx,topic,"Topics" ); const quesId = await checkAndCreate(ctx, question,"Questions"); const surahQues = await ctx.db.insert("Surah_Ques",{s_id:surah, q_id:quesId as Id<"Questions"> }); const topicQues = await ctx.db.insert("Topic_Ques",{t_id:topicId as Id<"Topics">, q_id:quesId as Id<"Questions"> }); const ansId = await ctx.db.insert("Answers",{title:answer}); const typeId = await checkAndCreate(ctx, type, "Types"); const ansType = await ctx.db.insert("Ans_Types", {a_id:ansId as Id<"Answers">, type_id:typeId as Id<"Types">, content:answer}) const ansQues = await ctx.db.insert("Q_A",{q_id:quesId as Id<"Questions">,a_id:ansId as Id<"Answers">}) // const question = await ctx.db.get(qId) return
// const answers = await getManyFrom(db,"Answers","by_qId",) }, }); checkAndCreate is a function where I send the id(or text if new) if it's Id, it returns it as is it is. else create a new and returns its Id So, my question was how to build my checkAndCreate function ? A solution that just popped to me is to check on client side if it's new option or existing one. then change the types of the values object I'm sending to backend and check type on backend...
}, }) so this is my form schema. All of the keys (topic, question, surah,answers,type) are tables in my database. I'm quering them and showing them as a dropdown list to user. So user can either select existing options or create new one. I'm sending the values as it is to the backend. function onSubmit(values: z.infer<typeof formSchema>) {
onCreate(values) console.log(values) } in my admin.ts (convex folder), export const create = mutation({ args: { topic:v.union(v.string(),v.id("Topics")), question:v.union(v.string(),v.id("Questions")), surah:v.id("Surahs"), answer:v.union(v.string(),v.id("Answers")), type:v.union(v.string(),v.id("Types")) }, handler: async (ctx,{topic,question,surah,answer,type}) => {
const topicId = await checkAndCreate(ctx,topic,"Topics" ); const quesId = await checkAndCreate(ctx, question,"Questions"); const surahQues = await ctx.db.insert("Surah_Ques",{s_id:surah, q_id:quesId as Id<"Questions"> }); const topicQues = await ctx.db.insert("Topic_Ques",{t_id:topicId as Id<"Topics">, q_id:quesId as Id<"Questions"> }); const ansId = await ctx.db.insert("Answers",{title:answer}); const typeId = await checkAndCreate(ctx, type, "Types"); const ansType = await ctx.db.insert("Ans_Types", {a_id:ansId as Id<"Answers">, type_id:typeId as Id<"Types">, content:answer}) const ansQues = await ctx.db.insert("Q_A",{q_id:quesId as Id<"Questions">,a_id:ansId as Id<"Answers">}) // const question = await ctx.db.get(qId) return
// const answers = await getManyFrom(db,"Answers","by_qId",) }, }); checkAndCreate is a function where I send the id(or text if new) if it's Id, it returns it as is it is. else create a new and returns its Id So, my question was how to build my checkAndCreate function ? A solution that just popped to me is to check on client side if it's new option or existing one. then change the types of the values object I'm sending to backend and check type on backend...
ok here's what you can do
you can check if record is existing by doing
const existing = ctx.db.get(IDOFYOURRECORD)
and you can check if !existing
then go ahead and create new one, that'll be your checkAndcreate function.I do that but if it's just a text string, it returns a convex error instead of just null
example:
if (label === "Topics") {
const checkTopic = await ctx.db.get(id as Id<"Topics">)
if(checkTopic===null){
ID = await ctx.db.insert("Topics", { topic: id });
}
it doesn't matter if its a text string, did you confirm on the dashboard if the
id
actually exists ?
i assume you are using typescript .ts right?yes
sorry I didn't understand. If it's a existing option, that works fine. but if i enter a new option, convex throws error.
do you already have a checkAndcreate function ?
yes.
async function checkAndCreate(
ctx: MutationCtx,
id: Id<"Questions"> | Id<"Types"> | Id<"Topics"> | Id<"Answers"> | string,
label: "Questions" | "Types" | "Topics" | "Answers" // Ensure correct collection name
) {
let ID = id; // Keep track of the final ID
// Create a new entry based on the collection type if (label === "Questions") { // const lastQues = await ctx.db.query("Questions").order("desc").take(1)
// const checkQues = await ctx.db.get(id as Id<"Questions">); // if(checkQues===null){ // ID = await ctx.db.insert("Questions", { title: id, q_no: lastQues[0].q_no+1 }); // Default q_no if needed // }
} else if (label === "Topics") { const checkTopic = await ctx.db.get(id as Id<"Topics">) if(checkTopic===null){ ID = await ctx.db.insert("Topics", { topic: id }); } } else if (label === "Types") { const checkType = await ctx.db.get(id as Id<"Types">) if(checkType === null){ const allTypes = await ctx.db.query("Types").collect() const greatestSortOrder = allTypes?.reduce((max, s) => s.sort_order > max ? s.sort_order : max, 0);
ID = await ctx.db.insert("Types", { name: id, sort_order: greatestSortOrder + 1 }); // Default sort_order if needed } } else if (label === "Answers") { ID = await ctx.db.insert("Answers", { title: id }); }
return ID; // Return the existing or newly created ID }
commented out code just for checks I could just use a try catch. Gosh. Sorry bro, brain lagged. Thank you @Hmza
// Create a new entry based on the collection type if (label === "Questions") { // const lastQues = await ctx.db.query("Questions").order("desc").take(1)
// const checkQues = await ctx.db.get(id as Id<"Questions">); // if(checkQues===null){ // ID = await ctx.db.insert("Questions", { title: id, q_no: lastQues[0].q_no+1 }); // Default q_no if needed // }
} else if (label === "Topics") { const checkTopic = await ctx.db.get(id as Id<"Topics">) if(checkTopic===null){ ID = await ctx.db.insert("Topics", { topic: id }); } } else if (label === "Types") { const checkType = await ctx.db.get(id as Id<"Types">) if(checkType === null){ const allTypes = await ctx.db.query("Types").collect() const greatestSortOrder = allTypes?.reduce((max, s) => s.sort_order > max ? s.sort_order : max, 0);
ID = await ctx.db.insert("Types", { name: id, sort_order: greatestSortOrder + 1 }); // Default sort_order if needed } } else if (label === "Answers") { ID = await ctx.db.insert("Answers", { title: id }); }
return ID; // Return the existing or newly created ID }
commented out code just for checks I could just use a try catch. Gosh. Sorry bro, brain lagged. Thank you @Hmza
Try-catch should work. If you want more type-safety i would use a discriminated union so you can always tell whether it's supposed to be an id or a non-id string