erquhart
erquhart•15mo ago

Getting table name from ID, and convex-helpers musings

Is it possible to parse the table name from an id?
18 Replies
lee
lee•15mo ago
it's possible if you're in a convex function and you have a list of possible table names, like this: https://github.com/get-convex/convex-helpers/blob/cfbeeb0a95e5f530789ce25ffcc073a3e6be8087/convex/lib/rowLevelSecurity.ts#L307 if you're not in a convex function it's (currently) impossible, because for example you could delete a table and create a new one with a different name but the same id format. can you describe the use-case?
GitHub
convex-helpers/convex/lib/rowLevelSecurity.ts at cfbeeb0a95e5f53078...
A collection of useful code to complement the official packages. - get-convex/convex-helpers
erquhart
erquhartOP•15mo ago
Perfect, thank you. I'm in a convex function so this works great.
ian
ian•15mo ago
What is the use case, out of curiosity? @sshader and I were just talking about this the other day
erquhart
erquhartOP•15mo ago
Basically writing my own version of the helpers. Upon realizing this I decided it's time to start using the helpers lol
ian
ian•15mo ago
which part of the helpers?
erquhart
erquhartOP•15mo ago
I've amassed a fair amount of tech debt in my convex functions and it's finally starting to slow me down. I was trying to figure out why my code patterns felt so scattered, and it finally hit me that I'm wiring up things that I simply never would have with a traditional database, where the query just does it all. A big one is "hydration" of dependent documents which, can be three layers deep in some cases - optional value, or else the optional value from the parent, or else a default value from the next parent. Pulling all of those relationships together in the right places got messy. RE: which part of the helpers, at the moment I was starting to write functions like readX, where I'd get a document from a specific table by id, handle ensuring the value existed and throwing if not. So using a helper in place of that. Will likely be implementing a lot of your helpers over the next day or so.
ian
ian•15mo ago
I'm also re-implementing some helpers to publish to convex-helpers over the next week or so, so maybe we should chat 🙂 I have only seen needing the table name for RLS - wrapping db.get when you do readX is it not doing db.get? you can use normalizeId to validate it's for the right table
erquhart
erquhartOP•15mo ago
Yeah it was using db.get(). I was getting the table name to throw a useful error, though for Sentry rather than the application. Happy to chat anytime if the helpers are being revisited
ian
ian•15mo ago
My helpers priorities are roughly: - make middleware easy to write (adding a user, etc. to ctx) - update RLS API, enabling a "Triggers" type middleware via DBWrapper - update Zod implementation to support taking in zod for function args (later: leveraging DBWrapper for read/write validation, and converting zod type to schema validation for tables too) - update Sessions wrappers (related: withAPIKey)
erquhart
erquhartOP•15mo ago
That all feels on point to me as a user Let me know if this should be a separate post and I'll move it: no matter what table name I pass to getOneOrNullFrom(), I get: Argument of type 'string' is not assignable to parameter of type 'never'. I know you mentioned having new in-progress helpers - if something is available to try I'm willing to copy/paste to stay closer to the edge. Just let me know.
ian
ian•15mo ago
is the index named "by_XXX" or just "XXX"? i.e. does it differ from getOneFrom?
Michal Srb
Michal Srb•15mo ago
@erquhart can you provide code examples for the "hydration"? I'm curious how exactly the defaulting logic works.
erquhart
erquhartOP•15mo ago
Ah that was it - I don't use underscores in my index names (although I have begun migrating to it). I'm actually trying to use it to just get by the id field, as I thought there was a by_id index, but maybe this helper doesn't work for that. Which makes sense given the field name starts with an underscore. A simple example:
export const hydrateSchedule = async (
db: DatabaseReader,
schedule: Doc<'schedules'>
) => {
const budgetMetadata = await db
.query('budget_metadata')
.withIndex('by_budget_id', (q) => q.eq('budgetId', schedule.budgetId))
.unique()
if (!budgetMetadata) {
throw Error(`Budget metadata not found for budget ${schedule.budgetId}`)
}
return {
...schedule,
reserveId: schedule.reserveId || budgetMetadata.defaultReserveId,
}
}
export const hydrateSchedule = async (
db: DatabaseReader,
schedule: Doc<'schedules'>
) => {
const budgetMetadata = await db
.query('budget_metadata')
.withIndex('by_budget_id', (q) => q.eq('budgetId', schedule.budgetId))
.unique()
if (!budgetMetadata) {
throw Error(`Budget metadata not found for budget ${schedule.budgetId}`)
}
return {
...schedule,
reserveId: schedule.reserveId || budgetMetadata.defaultReserveId,
}
}
ian
ian•15mo ago
I'm tempted to drop the by_ prefix, but I'm worried about breaking folks: re-making an index could be slow if there's a lot of data
erquhart
erquhartOP•15mo ago
Possible it to make it configurable and leave by_ as default?
ian
ian•15mo ago
If you can make that work, submit a PR? I can try at some point, but sounds like a lot of TypeScript gymnastics
ian
ian•14mo ago
@erquhart the 0.1.13 version of relationship helpers now let you either have your fieldname or "by_${fieldname}" as your index name. More info here
Database Relationship Helpers
Traverse database relationships in a readable, predictable, and debuggable way. Support for one-to-one, one-to-many, and many-to-many via utility func...
erquhart
erquhartOP•14mo ago
Oh nice!!

Did you find this page helpful?