Internal queries/mutations/actions vs helper functions
Hi everyone! I'm trying to understand the difference between internal queries/mutations/actions and regular helper functions. I'm going to write down my thoughts here, in a contrived example., and I hope you can help me understand it better. Thanks!
4 Replies
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!
Cool good questions. I can go through them one at a time
1. Do you have to
export const myPublicQuery = query(...)
? Yes, the export is necessary for Convex to register the function.
2. Should you call ctx.runQuery
in a query? You can, but it's usually unnecessary. It spins up a new javascript environment and does argument validation, so there's more overhead than just calling a helper function. ctx.runQuery
is mainly useful for calling code in components. https://www.convex.dev/components
3. Can you call a Convex function directly as if it were a javascript function? Technically yes, but this is discouraged and we will probably disallow it soon. Refactoring out the handler as a helper function is better, because the behavior is more explicit. One example of unexpected behavior is in this thread https://discord.com/channels/1019350475847499849/1302240820119994368/1309162182960877589 . Calling the Convex function directly does not run argument validation and doesn't create an isolated environment (which is unexpected, as evidenced by you thinking it does run argument validation)
4. Do you need to export internalQueries? Yes, this is necessary to register the function with in the Convex runtime.Components
Independent, modular, TypeScript building blocks for your backend.
Just to add on some general things about
query
, internalQuery
and helper functions:
- anything using the query
wrapper that is exported is a public Convex function -- you can call it from any of the Convex clients (e.g. useQuery(api.my.func)
) as well as from other convex functions (ctx.runQuery
, and if this were a mutation or action, ctx.scheduler
)
- anything using the internalQuery
wrapper that is exported is an internal Convex function -- it's not callable from Convex clients, but is callable from convex functions (ctx.runQuery
and ctx.scheduler
for mutations + actions)
- helper functions are just normal JS functions -- Convex clients don't have any way to directly call them, and neither does anything on ctx
As Lee said, if you're trying to share common code, you want a helper function as opposed to ctx.runQuery
(https://docs.convex.dev/production/best-practices/#use-helper-functions-to-write-shared-code). But if you're changing JS environments (like calling a query from an action), using components, or scheduling functions to run in the future, you will want an internalQuery
. And then anything you want to call from your client code will be a public query
Best Practices | Convex Developer Hub
Here's a collection of our recommendations on how best to use Convex to build
Thanks Lee and sshader. This was very helpful - I appreciate it.