Urql: Mutations not invalidating cache

Created on 20 Feb 2019  路  10Comments  路  Source: FormidableLabs/urql

I'm fairly certain that this is a bug... Or, at least a change in behavior between v0.x and v1.0.

Whenever I execute a mutation, it seems like the cache does not update unless I manually refetch.

For example, when I run this mutation:

mutation createPost($input: CreatePostInput!) {
  createPost(input: $input) {
    post {
      id
      content
      createdAt
    }
  }
}`

I get back a response with the new post data (as the docs say is required). However, my posts collection does not update unless I manually make a refetch call. The pre-1.0 code seemed to do this without having to manually refetch.

On a related note, are there any sorts of best practices around optimistic updates? So, if I execute a mutation, is there some way to directly manipulate the cache until it's invalidated?

bug 馃悰

All 10 comments

Whenever I execute a mutation, it seems like the cache does not update unless I manually refetch.

That doesn't seem right; do you have some code that we can test on a CodeSandbox or sth?
As long as a query containing posts鈥攊n your case鈥攊s mounted, a mutation manipulating posts should invalidate this query and force it to refetch automatically. This is entirely controlled from the cacheExchange and is not implemented in the component/hook themselves, which simply listens to these refetches. This does in fact work in our examples/1-getting-started.

On a related note, are there any sorts of best practices around optimistic updates? So, if I execute a mutation, is there some way to directly manipulate the cache until it's invalidated?

I'm currently planning out a normalised cache that can be used as a drop-in replacement for the default cacheExchange. This cache would normalise entities, would update queries optimistically and in reaction to other queries that update data, and would allow some more granular definitions of lists, optimistic updates, invalidation, etc, like Apollo's and Relay's caches do.

Hmm, this is very strange. I'm running example 1 with no problems, and I don't think I'm doing anything drastically different in my code.

I don't have a sandbox, as this is embedded in a larger app, and I'm not sure the best way to extract it.

Am I doing anything obviously wrong here? When the mutation resolves, response is giving me the updated data, but query.data is stale.
https://github.com/scyclow/friendworld/blob/urql-mutation/ui/src/containers/Threads/index.tsx#L94

Or, maybe in my configuration, since I'm seeing this behavior with all my mutations?
https://github.com/scyclow/friendworld/blob/urql-mutation/ui/src/index.tsx

If it's not obvious I'll try to isolate it a bit further.

I think I've at least narrowed it down a bit. At this point, it seems like the operationCache for my payload is empty. I'm not sure what that means, or where things are supposed to be added to it, but the cacheExchange "retriggers query" test fails when I give it a similar input.

Here is my response:

data: {
  createPost: {
    ...
    __typename: "CreatePostPayload"
  },
  __typename: "Mutation"
}

my operationCache:

{
  CreatePostPayload: Set(0) {},
  Thread: Set(1) {2258917924},
  User: Set(2) {2258917924, 3114238441},
}

and my resultCache

Map(2) {
  2258917924 => { ... },
  3114238441 => { ... }
}

It looks like some type names are missing there. Can you post the requests that have been sent to your API? @andyrichardson has recently refactored the typename rewriter that adds __typename fields to all requests, so some might be missing?

Edit: to clarify since my comment was a little imprecise; I don't see any reference to a "Post" typename which I assume would be the shared type between your mutation and query?

Here are the three requests I'm making to the API, where the last one is the mutation:

{"query":"{
  currentUser {
    id
    username
    avatarUrl
    alerts: alertsList(condition: {read: false}) {
      id
      content
      link
      __typename
    }
    __typename
  }
  __typename
  }
","variables":{}}

{"query":"query threadById($id: Int!) {
  thread: threadById(id: $id) {
    id
    title
    posts: postsList {
      id
      content
      createdAt
      author {
        id
        username
        avatarUrl
        postStats: authoredPosts {
          totalCount
          __typename
        }
        __typename
      }
      __typename
    }
    __typename
  }
  currentUser {
    id
    __typename
  }
  __typename
  }
","variables":{"id":5}}

{"query":"mutation createPost($input: CreatePostInput!) {
  createPost(input: $input) {
    post {
      id
      content
      createdAt
      author {
        id
        avatarUrl
        username
        __typename
      }
      __typename
    }
    __typename
  }
  __typename
  }
","variables":{"input":{"threadId":5,"content":"red","tags":"{\"hashtags\":[],\"usernames\":[]}"}}}

The mutation returns:

{
  "data": {
    "createPost": {
      "post": {
        "id": 66,
        "content": "red",
        "createdAt": "2019-02-20T19:28:09.739439",
        "author": {
          "id": "0a04ff42-a2c6-4e1f-bda9-80c493abefea",
          "avatarUrl": "https://www.healthyfamiliesbc.ca/sites/hfbcprox-prod.health.gov.bc.ca/files/thumbnails/getting-child-to-eat-breakfast.jpg",
          "username": "steve_p",
          "__typename": "User"
        },
        "__typename": "Post"
      },
      "__typename": "CreatePostPayload"
    },
    "__typename": "Mutation"
  }
}

I took a closer look at the request made by the example app:

{
  "data": {
    "toggleTodo": {
      "id": "2",
      "__typename": "Todo"
    },
    "__typename": "Mutation"
  }
}

I think the issue might be with how the payload is specified. In the example API, toggleTodo returns a payload of Todo -- which appears to be picked up by the operationCache. However, my mutation, createPost, returns a CreatePostPayload type, which has Post nested in it. So in my case, it's not picking up on the new post data because it's nested in another type.

So I think the issue here is that the cache exchange isn't recognizing nested types on mutation responses.

There's a good amount of data here to go off and it sounds like it's an implementation issue. I'll take a look 馃憤

@scyclow Just published 1.0.4 which fixes this bug. We were indeed not traversing responses fully. We also shipped a couple of other fixes :+1:

We seem to be having this exact issue and our data is not refetching as the cache is not being invalidated. Has anyone else started seeing this issue again?

@bubbaspaarx can you open a new issue or discussion thread please? I'm sure we can find a quick solution 馃憤

Was this page helpful?
0 / 5 - 0 ratings