Resolvers should never destructively modify the contextValue argument. This ensures consistency across all resolvers and prevents unexpected errors.
I’d like some help unpacking this statement.
- "Never destructively modify" I guess means that you can add properties to the context, but not remove or mutate them?
- "Ensures consistency across all resolvers" I guess refers to situations where query resolvers are processed in parallel such that a context value could change unexpectedly mid-execution. But what about mutation resolvers which are processed in series – would it be safe to mutate the context in that case?
My issue is this:
- I load the authenticated user as part of the context initialisation function.
- I put the user object into the context to be used by resolvers.
- I have a
setUserDetails
mutation which can update the user. - If I run a query with two mutations (e.g.
setUserDetails
followed bysendWelcomeEmail
), the second mutation sees the original user details, not the updated ones unless I mutate the context.
This leads to "unexpected errors" which was the thing we were trying to avoid in the first place.
So my question is: is it OK to mutate the context in a mutation resolver? Or is there another recommended approach to avoid this issue?
2
Answers
You seem to be quite aware of the issues that can originate from mutating the context value. I think the intention of the docs is to prevent people from using the context to pass around values. I think, ideally the context is created once and then never modified.
I think in your case, your context also somewhat serves as a cache (which is not uncommon), so I think it should be fine to invalidate or update cache entries after an update.
Nevertheless, I would consider the following:
{ id: 1, companyId: 2, role: 'USER' }
and load everything else from the db, when we need it. Your mutation is probably called very seldomly and does not slow down significantly, if you load the email from the database in the resolver.Running two mutations (or a mutation and a query) in the same network request carries risks if one depends on the results of the other. I don’t believe the order of execution is guaranteed, and if each makes async calls then they could interleave.
The simplest way to approach this is to define a mutation that performs both the change in the user information and sends the welcome email. This is qualitatively different than a later mutation that just changes user information long after the user has been onboarded.
Even if the
sendWelcomeEmail
mutation looked at the db for user information instead of the context there is no guarantee that the user record would have been updated by the other mutation in time.