rachpradhan
rachpradhan8mo ago

Hey guys, what is the best way to set default values for Convex Schemas

I was not able to find any documentation on this haha, also it would be great to have an easy way to create schemas from type script interfaces as I am trying to port a large part of my database from postgres to convex to see how it performs on dev!
18 Replies
rachpradhan
rachpradhanOP8mo ago
Also is there any data structure that works with postGIS data too(to query nearest distance) Also, is it not possible to run multiple queries in a single function?
export const init_user = query({
// Validators for arguments. // Query implementation.
handler: async (ctx:any, args:any) => {

const user:UserIdentity = await ctx.auth.getUserIdentity() ?? null

//// Read the database as many times as you need here.
//// See https://docs.convex.dev/database/reading-data.
const user_data = await ctx.db
.query("users")
// Ordered by _creationTime, return most recent
.filter((q:any) => q.eq(q.field("id"), user.tokenIdentifier)).collect()

if(user_data.length == 0){
await ctx.db.insert("users", {
id: user.tokenIdentifier,

});
const user_data = await ctx.db
.query("users")
// Ordered by _creationTime, return most recent
.filter((q:any) => q.eq(q.field("id"), user.tokenIdentifier))
return {initalised: true,

}
}
else{
return {initalised: true,

}
}

},
});
export const init_user = query({
// Validators for arguments. // Query implementation.
handler: async (ctx:any, args:any) => {

const user:UserIdentity = await ctx.auth.getUserIdentity() ?? null

//// Read the database as many times as you need here.
//// See https://docs.convex.dev/database/reading-data.
const user_data = await ctx.db
.query("users")
// Ordered by _creationTime, return most recent
.filter((q:any) => q.eq(q.field("id"), user.tokenIdentifier)).collect()

if(user_data.length == 0){
await ctx.db.insert("users", {
id: user.tokenIdentifier,

});
const user_data = await ctx.db
.query("users")
// Ordered by _creationTime, return most recent
.filter((q:any) => q.eq(q.field("id"), user.tokenIdentifier))
return {initalised: true,

}
}
else{
return {initalised: true,

}
}

},
});
rachpradhan
rachpradhanOP8mo ago
This error gets thrown
No description
Michal Srb
Michal Srb8mo ago
On the last point: You cannot write into the database from a query. https://docs.convex.dev/database/writing-data You can read from the database many times from a query function. (I recommend skimming the tutorial for a good overview of Convex: https://docs.convex.dev/get-started )
Writing Data | Convex Developer Hub
Mutations can insert, update, and
Welcome to Convex | Convex Developer Hub
Convex is a novel, fun, and extremely productive way to make backends for your
rachpradhan
rachpradhanOP8mo ago
i see, i fixed that issue with a customQuery/mutationHook! Thanks though, maybe it would be nice to have the custom functions as part of the docs too! For the first part is there any method to create your own types from an existing say prisma database? Also for mutations, what is the best practice to use them(suppose i want to initialise a user(would just calling the function in a useEffect() be best practice or are there other ways of going about it). Also can you call mutations without explicitly asking for them(like a button)
Michal Srb
Michal Srb8mo ago
Yes, you can use a useEffect, just watch out for handling the state before the mutation succeeds. This is what we suggest here: https://docs.convex.dev/auth/database-auth#call-a-mutation-from-the-client
Storing Users in the Convex Database | Convex Developer Hub
You might want to store user information directly in your Convex database, for
Michal Srb
Michal Srb8mo ago
For default values, you can add them after reading data from the database. So if your document has a field x: v.optional(v.string()) you can read the document and then set x to doc.x ?? "default value".
rachpradhan
rachpradhanOP8mo ago
where would you do that, would that be in the client component? as for my initial question i wanted to ask if we can call convex functions outside of react components?
Michal Srb
Michal Srb8mo ago
Yes, useEffect must be called from a client component. Yes, you can call Convex functions from many clients, see https://docs.convex.dev/quickstarts
Quickstarts | Convex Developer Hub
Quickly get up and running with your favorite frontend tooling or language:
rachpradhan
rachpradhanOP8mo ago
oh what i meant to ask was that can it be used in a non-client component(something like zustand)
Michal Srb
Michal Srb8mo ago
If you want to call a mutation from the client outside of a component you can use the async mutation method on the ConvexReactClient instance. In a component you can get the client instance via the useConvex() hook.
rachpradhan
rachpradhanOP8mo ago
in state stores wouldnt that lead to recursive hook calls though?
Michal Srb
Michal Srb8mo ago
That really depends what you do. It might be more of a Zustand question than Convex question.
rachpradhan
rachpradhanOP8mo ago
trueee hmmm if i figure it out ill do a short writeup here; it might be helpful for others too
David Alonso
David Alonso8mo ago
I had the same question as the title of this post, but it seems like it hasn't been answered. For strings, one can use v.literal to set a default value, but I'm not sure how to do the same for other types...
Matt Luo
Matt Luo8mo ago
Thanks @Michal Srb, i think that solution works, i.e. to insert to the db upon getting no result. But that seems like a workaround. You wouldn’t be able to distinguish whether the user actually manually set that value, or whether the system did it. I could see this approach working fully if you add a column about whether the system added that default value I took the useEffect approach for my Next.js client side rendered settings page. I don’t insert a default value to the DB. I used application code to conditionally set a default value. I’m somewhat happy with it. It functionally works, but there is a re-rendering that appears as a flicker in the drop-down to the end user crystaltxt.com/settings to see the flickering I’m considering a different approach to avoid the re-rendering: calling a Convex mutation function instead of a query function. The mutation would return a default value. The tradeoff is that I would need to include default value logic inside the mutation function. Also, the return logic could potentially get complicated if I wanted to return information about whether the mutation had to use the default value or whether the return is indeed the user’s chosen value
Michal Srb
Michal Srb8mo ago
@Matt Luo you are misinterpreting what I wrote. I did not suggest to write to the DB if the value is missing (although that is sometimes what's needed). Most of the time you can just read data and combine it with default values in the query (and this way you can also distinguish default values from real values):
export const myQuery = query({
args: {...}
handler: async (ctx, args) => {
const doc = await ctx.db.get(args.someId);
return {
x: doc.x ?? "some default value"
y: doc.y ?? {value: "something, defaulted: true}
};
});
export const myQuery = query({
args: {...}
handler: async (ctx, args) => {
const doc = await ctx.db.get(args.someId);
return {
x: doc.x ?? "some default value"
y: doc.y ?? {value: "something, defaulted: true}
};
});
The disadvantage of using a mutation for reading data, besides the ergonomics of our hooks, is that it can never be cached.
Matt Luo
Matt Luo8mo ago
Ah I see, that makes sense Thanks Michal

Did you find this page helpful?