Apollo-client: Key an object in the cache by multiple keys

Created on 22 Jul 2017  路  15Comments  路  Source: apollographql/apollo-client

dataIdFromObject and customResolvers together are a very powerful mechanism for making an application feel super fast by returning data from the cache. I want to be able to key a single object in the cache by multiple keys.


Let me illustrate with an example. Imagine this type:

type Community {
  id: ID!
  slug: String!
}

extend type Query {
  community(id: ID, slug: string): Community
  communities: [Community]
}

Note: This is based on our real-world need, but simplified

When a user visits their homepage we query communities and display them on the page. They can then click one of those items, which directs them to /<community.slug>, which kicks off a community(slug: <community.slug>) query.

To make the cache aware that the data for that community has already loaded I use a custom dataIdToObject which keys the cache entry by slug rather than ID:

dataIdToObject: (result) => {
  if (result.slug) return `${result.__typename}:${result.slug}`;
}

This means I can then implement a custom resolver for this data type and it'll be loaded from cache:

customResolvers: {
  Query: {
     community: (_, { slug }) => toIdValue(client.dataIdFromObject({ __typename: 'Community', slug }))
  }
}

There is one issue now though, which is that if I kick off a query programmatically for community(id: "some-id") it won't have a cache hit (since that's now keyed by slug) and it'll go to the network even though we already have the data in the cache.


What I'd thusly want to do is key that data with two separate keys, once with the slug and once with the ID, so that no matter how I query a community I get a cache hit:

dataIdToObject: (result) => {
  if (result.slug) return [`${result.__typename}:${result.slug}`,`${result.__typename}:${result.id}` ];
}

Is this feasible? Has this been brought up before?

Most helpful comment

Just jumping to say that we're having the exact same use-case as max is outlining here. Has there been any progress since?

All 15 comments

This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!

Uhh this is weird, should this be marked as stale? I feel like it should be marked as an ongoing discussion 馃槄

This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!

Could somebody add a label to the issue or something?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!

Pleeeeasseeeeeee

@mxstbr sorry about that! I've added discussion so it will stay open! This is a really interesting idea! I think we can probably support something like this in the InMemoryCache!

@mxstbr swinging back around to this to let you know I've added it to the post 2.0 features milestone to get looked into!

Wait a second... I'm a collaborator on this repo... So I could theoretically assign issues to milestones...

馃槇

Just kidding, just kidding, don't worry. Thanks, excited to have this looked at!

Just jumping to say that we're having the exact same use-case as max is outlining here. Has there been any progress since?

To help provide a more clear separation between feature requests / discussions and bugs, and to help clean up the feature request / discussion backlog, Apollo Client feature requests / discussions are now being managed under the https://github.com/apollographql/apollo-feature-requests repository.

This feature request / discussion will be closed here, but anyone interested in migrating this issue to the new repository (to make sure it stays active), can click here to start the migration process. This manual migration process is intended to help identify which of the older feature requests are still considered to be of value to the community. Thanks!

I fixed it with

const cache = new InMemoryCache({
  dataIdFromObject: object => {
    switch (object.__typename) {
      case 'CompositeKey':
        return `${object.id1}-${object.id2}` // creates a composite key
      default:
        return defaultDataIdFromObject(object) // fall back to default handling
    }
  }
}

This seems to work. Is it a good idea?

@francoisromain Correct me if I'm wrong, but it seems to me that you need both id1, as well as id2 available when you're performing your query. Sometimes you only have one of the two available, for example when a user loads a page using a slug, you might not have the id available.

Running into this exact problem, was this solved/implemented in library?

Was this page helpful?
0 / 5 - 0 ratings