dannyelo
dannyelo4mo ago

Optimizing Function Calls by Avoiding Redundant Abstractions

The problem I’m facing is that I’m using an extra layer of abstraction in my code that doesn’t seem necessary. I have a function called getCurrentUserInternal, which is basically just a wrapper around another function called getCurrentUser. I’m using this wrapper to access user information inside another function, getFacturapiLiveInstance. The problem is that getCurrentUser accepts QueryCtx, and in the action I only have access to ActionCtx. Is there a better way? convex/users.ts
export const getCurrentUser = async (ctx: QueryCtx) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error('Called getCurrentUser without authentication present')
}

const user = await ctx.db
.query('users')
.withIndex('by_token', (q) =>
q.eq('tokenIdentifier', identity.tokenIdentifier),
)
.unique()

if (!user) {
throw new Error('User not found')
}

return user
}

export const getCurrentUserInternal = internalQuery({
args: {},
handler: async (ctx) => {
return await getCurrentUser(ctx)
},
})
export const getCurrentUser = async (ctx: QueryCtx) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error('Called getCurrentUser without authentication present')
}

const user = await ctx.db
.query('users')
.withIndex('by_token', (q) =>
q.eq('tokenIdentifier', identity.tokenIdentifier),
)
.unique()

if (!user) {
throw new Error('User not found')
}

return user
}

export const getCurrentUserInternal = internalQuery({
args: {},
handler: async (ctx) => {
return await getCurrentUser(ctx)
},
})
convex/facturapi_usenode.ts
'use node'

// ...imports

export const getFacturapiLiveInstance = async ({
organizationId,
ctx,
}: {
organizationId: Id<'organizations'>
ctx: ActionCtx
}): Promise<Facturapi> => {
await ctx.runQuery(internal.users.getCurrentUserInternal)
await getCurrentUser(ctx) -> // this ctx is an ActionCtx

// rest of the code...
}
'use node'

// ...imports

export const getFacturapiLiveInstance = async ({
organizationId,
ctx,
}: {
organizationId: Id<'organizations'>
ctx: ActionCtx
}): Promise<Facturapi> => {
await ctx.runQuery(internal.users.getCurrentUserInternal)
await getCurrentUser(ctx) -> // this ctx is an ActionCtx

// rest of the code...
}
4 Replies
Convex Bot
Convex Bot4mo 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
jamwt4mo ago
In short, no -- b/c actions aren't inside the reactor. so they don't have access to MutationCtx and QueryCtx as they're not deterministic. in terms of cleaner patterns, I think you can use customAction to clean this pattern up in general: https://stack.convex.dev/custom-functions and https://github.com/get-convex/convex-helpers/blob/7847cfb9cfb45397e1b0ec099360f721a9463bf1/packages/convex-helpers/server/customFunctions.ts#L296
Customizing serverless functions without middleware
Re-use code and centralize request handler definitions with discoverability and type safety and without the indirection of middleware or nesting of wr...
jamwt
jamwt4mo ago
customQuery/Mutation/Action at this point are so canonical, we're probably going to move them out of convex-helpers and put them into some standard util lib that ships with base convex soon so we encourage more projects to use them
dannyelo
dannyeloOP4mo ago
Thank you Jamie, I'll take a look 🙏

Did you find this page helpful?