milk enjoyer
milk enjoyer13mo ago

Do actions have the same transactional guarantees as mutations?

I understand that mutations are run transactionally, but is it the same for actions? Let's say the action fails halfway but triggers a write before it fails. Will the write still go through?
12 Replies
jamwt
jamwt13mo ago
Individual mutations are transactional but not actions. So if an action had run a mutation, and that mutation succeeded, then the action later fail, the initial write would indeed apply. And in fact its changes could be observed by other concurrent query / mutations / actions even while the original action is still running.
milk enjoyer
milk enjoyerOP13mo ago
I see. And what if my mutation calls another mutation, which calls another mutation etc? If one fails, every single one will fail? And when they fail what happens to any actions that were triggered within any of those mutations? I have another question regarding security (I am using Clerk for authentication). From what I am seeing, it seems like there is no way to enforce security through useMutation, so the best way is probably to route everything through a backend API and verify their clerk ID etc there first and check if their corresponding user entry within the database has the right permissions, is that correct? But even if I do this, given that there is no security implementation on top of mutations, if a malicious user could guess my mutation names and pass in another user id not belonging to himself, does that mean that he could potentially mutate things that he is not supposed to? given that mutations, queries, and actions run in a different runtime from my apps also means that i cannot verify if the clerk user is who he claims he is (edit: I just realized there is a function ctx.auth.getUserIdentity() but this is stangely not working at all for me as it always returns null even though the rest of the integration works fine, i followed the clerk tutorial on convex docs.)
jamwt
jamwt13mo ago
Currently, mutations cannot call each other. however, if you directly call a mutation's "handler function" and pass it the same MutationCtx, they will just compose as one giant mutation the actions (as invoked by the scheduler) will still atomically only run if the entire combined mutation succeeds / commits re your other questions...
I just realized there is a function ctx.auth.getUserIdentity() but this is stangely not working at all for me as it always returns null even though the rest of the integration works fine, i followed the clerk tutorial on convex docs
The key is to get this working. we can help you figure out what's up. getUserIdentity returns a secure representation of the logged-in user that cannot be forged, so fixing your auth problem is the essential step to securing all your mutations
ian
ian13mo ago
For mutations that you only want to call from actions, you can make them internalMutation which can't be called from the outside world, only from actions or scheduled from other mutations
milk enjoyer
milk enjoyerOP13mo ago
from some initial testing, it seems like the reason why it doesn't work is that i am sending multiple calls (due to server vs client and client rerenders) to convex but convex only runs the first one and ignores the rest. This first request usually comes from the server (I use the node API to fetch initial on server side first using Nextjs server components, then keep it reactive afterwards using useQuery on the client side). But the thing is that the user is actually authed on the server side too, but that is not recognized as the convex provider is client side only. A solution I can think of would be to create 2 functions, one for the server side call with maybe some kind of secret between them that only my server knows, and another one for my client to call that uses getUserIdentity
jamwt
jamwt13mo ago
@milk enjoyer yes, server to server often uses a shared secret for auth there have been some threads about this before, I'll see if I can find one looks like @CodingWithJamal ran into something similar here: https://discord.com/channels/1019350475847499849/1167613557957992458/1167613557957992458
ian
ian13mo ago
My recent post on stack.convex.dev on customizing functions has an example of a shared api key
milk enjoyer
milk enjoyerOP13mo ago
This works well, solves the auth problem for server side calls. Thanks a lot!
ChrisLi
ChrisLi7mo ago
Hi @ian , I have a question here. As mutations are run transactionally, assume inside a mutation, I scheduled another mutation call, when the first mutation failed, will the scheduled mutation be cancelled or it will not get affected?
jamwt
jamwt7mo ago
The scheduling is also transactional. So the job only runs if the first mutation succeeds and commits.
ian
ian7mo ago
@ChrisLi ^
ChrisLi
ChrisLi7mo ago
Thanks for your replies! They are really helpful!

Did you find this page helpful?