ibrahimyaacob
ibrahimyaacob10mo ago

anyone have done convex-helper triggers + convex-ents

can you share me how the configuration code would look like
5 Replies
Convex Bot
Convex Bot10mo 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!
ampp
ampp10mo ago
Oh yeah, this was something i asked in the depercated beta area... There were a couple ways to do this, and i could probably clean up my import... it depends on the design of your custom context, mine is rather complex as i destructive the query payload of a user session Id. It wasnt quite as confusing i thought.
triggers go before the ents code. I picked the cleanest option imho.
import {
MutationCtx as BaseMutationCtx,
QueryCtx as BaseQueryCtx,
// ActionCtx as BaseActionCtx,
query as baseQuery,
mutation as baseMutation,
action as baseAction,
internalAction as baseInternalAction,
internalQuery as baseInternalQuery,
internalMutation as baseInternalMutation,
} from './_generated/server'

const triggers = new Triggers<DataModel>()

export const triggersBaseMutation = customMutation(
baseMutation,
customCtx(triggers.wrapDB)
)

export const triggersBaseInternalMutation = customMutation(
baseInternalMutation,
customCtx(triggers.wrapDB)
)

// more normal
export const mutation = customMutation(
triggersBaseMutation,
customCtx(async (baseCtx) => {
return await simpleMutationCtx(baseCtx)
})
)
// advanced
export const sessionMutation = customMutation(triggersBaseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
return {
ctx: await advancedMutationCtx(baseCtx, sessionId),
args: { sessionId },
}
},
})

async function simpleMutationCtx(baseCtx: BaseMutationCtx) {
const ctx = {
db: baseCtx.db as unknown as undefined,
skipRules: { table: entsTableFactory(baseCtx, entDefinitions) },
}
const entDefinitionsWithRules = getEntDefinitionsWithRules(ctx as any)
const table = entsTableFactory(baseCtx, entDefinitionsWithRules)
;(ctx as any).table = table
return {
...ctx,
table,
}
}
import {
MutationCtx as BaseMutationCtx,
QueryCtx as BaseQueryCtx,
// ActionCtx as BaseActionCtx,
query as baseQuery,
mutation as baseMutation,
action as baseAction,
internalAction as baseInternalAction,
internalQuery as baseInternalQuery,
internalMutation as baseInternalMutation,
} from './_generated/server'

const triggers = new Triggers<DataModel>()

export const triggersBaseMutation = customMutation(
baseMutation,
customCtx(triggers.wrapDB)
)

export const triggersBaseInternalMutation = customMutation(
baseInternalMutation,
customCtx(triggers.wrapDB)
)

// more normal
export const mutation = customMutation(
triggersBaseMutation,
customCtx(async (baseCtx) => {
return await simpleMutationCtx(baseCtx)
})
)
// advanced
export const sessionMutation = customMutation(triggersBaseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
return {
ctx: await advancedMutationCtx(baseCtx, sessionId),
args: { sessionId },
}
},
})

async function simpleMutationCtx(baseCtx: BaseMutationCtx) {
const ctx = {
db: baseCtx.db as unknown as undefined,
skipRules: { table: entsTableFactory(baseCtx, entDefinitions) },
}
const entDefinitionsWithRules = getEntDefinitionsWithRules(ctx as any)
const table = entsTableFactory(baseCtx, entDefinitionsWithRules)
;(ctx as any).table = table
return {
...ctx,
table,
}
}
Ian also offered these configurations..
export const sessionMutation = customMutation(baseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return {
ctx: await advancedMutationCtx(withTriggersCtx, sessionId),
args: { sessionId },
}
},
})

or
export const mutation = customMutation(
baseMutation,
customCtx(async (baseCtx) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return await simpleMutationCtx(withTriggersCtx)
})
)
export const sessionMutation = customMutation(baseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return {
ctx: await advancedMutationCtx(withTriggersCtx, sessionId),
args: { sessionId },
}
},
})

or
export const mutation = customMutation(
baseMutation,
customCtx(async (baseCtx) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return await simpleMutationCtx(withTriggersCtx)
})
)
ibrahimyaacob
ibrahimyaacobOP10mo ago
hey @ampp thanks for sharing this, seems to work but my triggers are not using ents' mutation context, just vanilla convex's context
ian
ian3mo ago
I'd try to avoid doing custom mutations on top of other custom mutations, and instead use the triggers.wrapDB explicitly in your sessionMutation, and make helper functions where applicable
ampp
ampp3mo ago
Yeah so this example is the absolute best practice right?
export const sessionMutation = customMutation(baseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return {
ctx: await advancedMutationCtx(withTriggersCtx, sessionId),
args: { sessionId },
}
},
})
export const sessionMutation = customMutation(baseMutation, {
args: { sessionId: v.optional(v.union(v.string(), v.null())) },
input: async (baseCtx, { sessionId }) => {
const withTriggersCtx = await triggers.wrapDB(baseCtx);
return {
ctx: await advancedMutationCtx(withTriggersCtx, sessionId),
args: { sessionId },
}
},
})

Did you find this page helpful?