KrleDano
KrleDano13mo ago

How to create unique field in Convex

How do I restrict the name to be unique meaning, two document can't have same name?
export default defineSchema({
transactionCategory: defineTable({
name: v.string(), // has to be unique
icon: v.optional(v.string()),
}),
});
export default defineSchema({
transactionCategory: defineTable({
name: v.string(), // has to be unique
icon: v.optional(v.string()),
}),
});
7 Replies
Joseph_Ebuka
Joseph_Ebuka13mo ago
I don’t think this is possible but when creating the mutation u can do something like query the field with an index of the name and of the name already exists throw an error or do something with that but if the name dosent exist continue with the function would this work? @erquhart i used it once
burnstony#1975
burnstony#197513mo ago
this seems like a very important feature. over the years using apis, a pattern I've seen that makes an API a lot easier to use is the idea that every object has an internal unique identifier that the API's own system creates and also another unique identifier that the API user can set this is especially useful for integrating between systems that already exist before you start using the API
lee
lee13mo ago
This is how you do it. It's ~2 extra lines of code wherever you create the document or modify its 'name' field And if you are creating or modifying the name in many places, you can put the constraint in a row-level-security rule or something similar https://www.npmjs.com/package/convex-helpers, and then it will be enforced on every db.insert/db.patch/db.replace
Michal Srb
Michal Srb13mo ago
To recap: - Declare an index using the name field - Whenever creating or modifying documents, check that there isn't an existing document with the same name - If this needs to be done in multiple places, wrap the logic in a JS/TypeScript function - If you need to do this for many different documents and fields, check out convex-helpers for utilities that help
erquhart
erquhart13mo ago
Another approach, which I've taken, is pulling db operations out of my convex functions and creating "db functions" - they're just functions that I pass the context object to. Give them useful names, handle authz, enforce uniqueness, validation, etc in the db function and you don't have to worry about any of that at the call site. I'm still not certain how well this will age compared to middleware, but it's similar to how things are structured with frontend libs like react query, where you have dedicated and often reused functions for fetching. I just finished a pretty big overhaul in this direction, I have more details if it's helpful for anyone.
jamwt
jamwt13mo ago
@ian and @erquhart I'm pretty sure you guys are connecting, right? Ian is thinking through something similar for middleware vs. a kind of macro layer gluing together these functions
erquhart
erquhart13mo ago
Yeah we've definitely connected on middleware, he also shared his in progress work for me to try out. The challenge is I personally prefer keeping things highly declarative (perhaps to a fault), so I'm more likely to pass the context around everywhere like a football than to use middleware. I haven't heard much about the macro layer concept, interested to hear more.

Did you find this page helpful?