Search code examples
javascriptgraphqlapolloapollo-server

Modifying the context value in GraphQL resolvers


The Apollo Server docs state:

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.

  1. "Never destructively modify" I guess means that you can add properties to the context, but not remove or mutate them?
  2. "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:

  1. I load the authenticated user as part of the context initialisation function.
  2. I put the user object into the context to be used by resolvers.
  3. I have a setUserDetails mutation which can update the user.
  4. If I run a query with two mutations (e.g. setUserDetails followed by sendWelcomeEmail), 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?


Solution

  • 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:

    • Should "sendWelcomeEmail" really be a standalone mutation? Maybe your API could be designed differently. I recommend Mark-Andre's writing.
    • I assume that you are not really "caching" values, but this is probably only the case for the current user. It can be wise, to only store a few properties in the context taht don't ever change (e.g. in our API we have { 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.