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
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.
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.)
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 docsThe 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 mutationsFor 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 mutationsfrom 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
@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
My recent post on stack.convex.dev on customizing functions has an example of a shared api key
This works well, solves the auth problem for server side calls. Thanks a lot!
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?
The scheduling is also transactional. So the job only runs if the first mutation succeeds and commits.
@ChrisLi ^
Thanks for your replies! They are really helpful!