MegaHydronics
MegaHydronics3d ago

Accessing database in an action

I'm hoping to better understand why ActionCtx does not expose db. This forces me to write much longer code like the first below, instead of the second.
export const set = internalAction({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const exists = await ctx.runQuery(internal.db.user._exists, { key })
if (exists) return

const response = await fetch(...)
const args = await response.json()
ctx.runMutation(internal.db.user._insert, args)
},
})

export const _insert = internalMutation({
args: v.object(userSchema),
handler: async (ctx, args) => {
await ctx.db.insert('user', args)
},
})

export const _exists = internalQuery({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const user = await ctx.db.query('user').withIndex('key', q => q.eq('key', key)).unique()
return user !== null
},
})
export const set = internalAction({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const exists = await ctx.runQuery(internal.db.user._exists, { key })
if (exists) return

const response = await fetch(...)
const args = await response.json()
ctx.runMutation(internal.db.user._insert, args)
},
})

export const _insert = internalMutation({
args: v.object(userSchema),
handler: async (ctx, args) => {
await ctx.db.insert('user', args)
},
})

export const _exists = internalQuery({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const user = await ctx.db.query('user').withIndex('key', q => q.eq('key', key)).unique()
return user !== null
},
})
export const set = internalAction({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const user = await ctx.db.query('user').withIndex('key', q => q.eq('key', key)).unique()
if (user !== null) return

const response = await fetch(...)
const args = await response.json()
await ctx.db.insert('user', args)
},
})
export const set = internalAction({
args: { key: v.string() },
handler: async (ctx, { key }) => {
const user = await ctx.db.query('user').withIndex('key', q => q.eq('key', key)).unique()
if (user !== null) return

const response = await fetch(...)
const args = await response.json()
await ctx.db.insert('user', args)
},
})
2 Replies
Convex Bot
Convex Bot3d ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
jamwt
jamwt3d ago
hi! queries and mutations need to be deterministic, and actions don't. we'd create race conditions and inconsistencies if we allowed access to the raw db object in actions. for most users, the results would be surprising and unintuitive. however. We do not love at all that it's so verbose to specify queries or mutations that only exist to service one particular action. we'd love to have a way to inline those to make things more ergonomic. but we haven't figured out how to do it yet, especially as it relates to like JavaScript semantics bundler behavior, etc. this is a long topic, but stuff like closures being allowed to mutate vars in the outer scope are particularly nasty and can make unpredictable program behavior