#What is the idiomatic way to model many-to-many relationships in a convex schema
1 messages · Page 1 of 1 (latest)
Hi @grand lodge. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
The idiomatic way to model many-to-many relationships in Convex is to use a join table (also called a relationship or association table). In your case, where many users can join many groups, you would create a third table (e.g., groupUsers) that stores a reference to both a user and a group.
Here’s how you might define this in your Convex schema:
defineSchema({
users: defineTable({
// user fields
}),
groups: defineTable({
// group fields
}),
groupUsers: defineTable({
userId: v.id("users"),
groupId: v.id("groups"),
// Optionally, other fields like join date, role, etc.
})
.index("userId", ["userId"]) // To get groups for a user
.index("groupId", ["groupId"]), // To get users in a group
});
How it works:
- Each document in
groupUsersrepresents a single membership (a user in a group). - To find all groups a user is in, query
groupUsersbyuserId. - To find all users in a group, query
groupUsersbygroupId. - You can add additional indexes (e.g., on both
userIdandgroupId) for efficient lookups if needed.
Example: Adding a user to a group
await ctx.db.insert('groupUsers', { userId, groupId });
Example: Querying all users in a group
const groupUsers = await ctx.db.query("groupUsers")
.withIndex("groupId", q => q.eq("groupId", group._id))
.collect();
const userIds = groupUsers.map(gu => gu.userId);
const users = await Promise.all(userIds.map(db.get));