{"id":22382358,"url":"https://github.com/jcoreio/apollo-magic-refetch","last_synced_at":"2025-07-31T03:31:43.671Z","repository":{"id":57182345,"uuid":"141387580","full_name":"jcoreio/apollo-magic-refetch","owner":"jcoreio","description":"magically refetches relevant apollo graphql queries after creates, deletes, and association changes","archived":false,"fork":false,"pushed_at":"2024-03-04T23:10:09.000Z","size":1027,"stargazers_count":31,"open_issues_count":0,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-28T19:14:09.159Z","etag":null,"topics":["apollo","apollo-client","apollographql","cache-invalidation","graphql","refetch"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcoreio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-18T05:57:23.000Z","updated_at":"2024-03-23T01:51:20.000Z","dependencies_parsed_at":"2024-06-20T00:07:47.850Z","dependency_job_id":"bb4d6ac9-3898-4c25-907f-ab363c645f8b","html_url":"https://github.com/jcoreio/apollo-magic-refetch","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/jcoreio/apollo-magic-refetch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fapollo-magic-refetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fapollo-magic-refetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fapollo-magic-refetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fapollo-magic-refetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcoreio","download_url":"https://codeload.github.com/jcoreio/apollo-magic-refetch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fapollo-magic-refetch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267983365,"owners_count":24176058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo","apollo-client","apollographql","cache-invalidation","graphql","refetch"],"created_at":"2024-12-05T00:12:41.794Z","updated_at":"2025-07-31T03:31:43.337Z","avatar_url":"https://github.com/jcoreio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# apollo-magic-refetch\n\n[![CircleCI](https://circleci.com/gh/jcoreio/apollo-magic-refetch.svg?style=svg)](https://circleci.com/gh/jcoreio/apollo-magic-refetch)\n[![Coverage Status](https://codecov.io/gh/jcoreio/apollo-magic-refetch/branch/master/graph/badge.svg)](https://codecov.io/gh/jcoreio/apollo-magic-refetch)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)\n[![npm version](https://badge.fury.io/js/apollo-magic-refetch)](https://badge.fury.io/js/apollo-magic-refetch)\n\nHandling Apollo cache updates after creating and deleting objects, or\nassociating and dissociating objects, remains a\n[poorly solved problem](https://github.com/apollographql/apollo-client/issues/899).\n`update` and `refetchQueries` props on `Mutation`s couple different areas of\nyour app in a way you probably don't want, and they don't scale well as you add\nmore queries over objects you may create/delete.\n\nTruly solving the problem will probably require changes to the apollo client\nand cache code.\n\nUntil that happens, this is probably your best bet!\n\n# Table of Contents\n\n- [How it works](#how-it-works)\n- [Current limitations](#current-limitations)\n- [ES environment requirements](#es-environment-requirements)\n- [Type metadata usage](#type-metadata-usage)\n- [Handling Deletions](#handling-deletions)\n- [Handling Creation](#handling-creation)\n- [Handling associations being broken](#handling-associations-being-broken)\n- [Handling associations being created](#handling-associations-being-created)\n- [API](#api)\n  - [`refetch(client, typenameOrTerms, [predicate, [idField]])`](#refetchclient-typenameorterms-predicate-idfield)\n  - [`refetch.fetchTypeMetadata(client)`](#refetchfetchtypemetadataclient)\n  - [`refetch.setTypeMetadata(typeMetadataPromise)`](#refetchsettypemetadatatypemetadatapromise)\n  - [`typesQuery`](#typesquery)\n\n## How it works\n\nAfter you delete an object, you tell `apollo-magic-refetch` what `typename` and\n`id` was deleted, and it refetches all active queries that contain that object\nanywhere within their current data!\n\nSimilarly, after you create an object, you tell it the `typename` of the created\nobject and it refetches all active queries that contain an object of that type\nin their selections. This is a bit less efficient than handling deletes, but\nway easier than anything else at the time of writing.\n\nSince only active queries can be refetched, data in the cache for inactive\nqueries will remain out-of-date. For that reason, I would recommend using the\n`cache-and-network` policy on all queries you're not planning to `update` after\nall pertinent mutations.\n\n## Current limitations\n\n- Interfaces and union types are not supported yet. This means if they are\n  anywhere in your results, this library may fail to refetch when it should.\n- Lists of lists are not supported yet.\n\n## ES environment requirements\n\nIf you are building for legacy browsers with a bundler like Webpack, make sure\nto add a rule to transpile this package to ES5.\n\nIf you are not using a bundler that supports the `module` property in\n`package.json`, make sure to install `babel-runtime`.\n\n## Type metadata usage\n\n`apollo-magic-refetch` uses type metadata from GraphQL determine which queries\nneed to be refetched; the client must get this metadata from the server.\nIf your schema is large enough it may be a prohibitive amount of metadata.\n`refetch` operations will be delayed until this metadata is fetched.\nTo prefetch this metadata via a GraphQL introspection query, do:\n\n```js\nimport client from './wherever/you/create/your/apollo/client'\nimport refetch from 'apollo-magic-refetch'\n\n// initiate the prefetch\nrefetch.fetchTypeMetadata(client)\n```\n\nIf your server forbids client introspection queries, you will have to fetch the\nmetadata by other means. For instance, you could execute the required introspection\nquery on the server, and serve the result on a custom REST route:\n\n```js\nimport { execute } from 'graphql'\nimport schema from './path/to/your/graphql/schema'\nimport express from 'express'\nimport { typesQuery } from 'apollo-magic-refetch'\n\nconst app = express()\n\nconst typeMetadataPromise = execute(schema, typesQuery)\n\napp.get('/graphql/refetchTypeMetadata', (req, res) =\u003e {\n  typeMetadataPromise.then((data) =\u003e res.json(data))\n})\n```\n\nAnd then pass this data to `refetch.setTypeMetadata` **before you ever call\n`refetch()`**:\n\n```js\nimport refetch from 'apollo-magic-refetch'\n\n// accepts a promise that resolves to the graphql execution result.\nrefetch.setTypeMetadata(\n  fetch('/graphql/refetchTypeMetadata').then((res) =\u003e res.json())\n)\n```\n\n## Handling Deletions\n\nTypically you call `refetch` within the `update` callback of your `Mutation`\nthat deletes objects. You just have to call `refetch` with the `__typename`\nthat was deleted (in this case, `Device`) and the `id` of the deleted object.\nThis refetches any active queries that contain the deleted object in cached data.\n\nFor mutations that delete multiple things at once, you may pass an array or `Set`\nof ids to `refetch`, or make multiple calls to `refetch` in your `update` method.\n\n```js\nimport * as React from 'react'\nimport gql from 'graphql-tag'\nimport refetch from 'apollo-magic-refetch'\nimport {Mutation, ApolloConsumer} from 'react-apollo'\n\nconst mutation = gql`\nmutation destroyDevice($deviceId: Int!) {\n  destroyDevice(deviceId: $deviceId)\n}\n`\n\nconst DestroyDeviceButton = ({deviceId}) =\u003e (\n  \u003cApolloConsumer\u003e\n    {client =\u003e (\n      \u003cMutation\n        mutation={mutation}\n        update={() =\u003e refetch(client, 'Device', deviceId)}\n      /\u003e\n        {destroyDevice =\u003e (\n          \u003cbutton onClick={destroyDevice({variables: {deviceId}})}\n        )}\n      \u003c/Mutation\u003e\n    )}\n  \u003c/ApolloConsumer\u003e\n)\n```\n\n## Handling Creation\n\nTypically you call `refetch` within the `update` callback of your `Mutation`\nthat creates objects. You just have to call `refetch` with the `__typename`\nthat was created.\n\nUnlike deletions, you don't pass the `id` of the created\nobject. Without a specific `id` to search for, it simply refetches all active\nqueries that contain any object of the requested `__typename` in their cached\ndata, in case the created object belongs in the new results. This is less\nefficient than refetching queries containing a specific `id`, but far easier\nthan manually inserting the created object into each relevant query.\n\nIn this example, the `__typename` of the object being created is `Device`.\n\n```js\nimport * as React from 'react'\nimport gql from 'graphql-tag'\nimport refetch from 'apollo-magic-refetch'\nimport {Mutation, ApolloConsumer} from 'react-apollo'\nimport CreateDeviceForm from './CreateDeviceForm'\n\nconst mutation = gql`\nmutation createDevice($values: CreateDevice!) {\n  createDevice(values: $values) {\n    id\n  }\n}\n`\n\nconst CreateDeviceFormContainer = () =\u003e (\n  \u003cApolloConsumer\u003e\n    {client =\u003e (\n      \u003cMutation\n        mutation={mutation}\n        update={() =\u003e refetch(client, 'Device')}\n      /\u003e\n        {createDevice =\u003e (\n          \u003cCreateDeviceForm\n            onSubmit={(values) =\u003e createDevice({variables: {values}})}\n          /\u003e\n        )}\n      \u003c/Mutation\u003e\n    )}\n  \u003c/ApolloConsumer\u003e\n)\n```\n\n## Handling associations being broken\n\nIn this example, a view shows a list of `Organization`s, each containing a\nsublist of `User`s. When one or more users is removed from an organization,\nit makes the following call:\n\n```js\nrefetch(client, [\n  ['User', userIds],\n  ['Organization', organizationId],\n])\n```\n\nPassing an array to `refetch` means to only refetch queries containing all of\nthe conditions in the array. So the query below would be refetched, but a query\ncontaining only `Organizations` or a query containing only `User`s would not.\n\n```js\nimport * as React from 'react'\nimport gql from 'graphql-tag'\nimport refetch from 'apollo-magic-refetch'\nimport {Mutation, ApolloConsumer} from 'react-apollo'\nimport OrganizationView from './OrganizationView'\n\nconst query = gql`\nquery {\n  Organizations {\n    id\n    name\n    Users {\n      id\n      username\n    }\n  }\n}\n`\n\nconst mutation = gql`\nmutation removeUsersFromOrganization($organizationId: Int!, $userIds: [Int!]!) {\n  result: removeUsersFromOrganization(organizationId: $organizationId, userIds: $userIds) {\n    organizationId\n    userIds\n  }\n}\n`\n\nconst OrganizationViewContainer = ({organization: {id, name, Users}}) =\u003e (\n  \u003cApolloConsumer\u003e\n    {client =\u003e (\n      \u003cMutation\n        mutation={mutation}\n        update={(cache, {data: {result: {organizationId, userIds}}}) =\u003e\n          refetch(client, [\n            ['User', userIds],\n            ['Organization', organizationId],\n          ])\n        }\n      \u003e\n        {removeUsersFromOrganization =\u003e (\n          \u003cOrganizationView\n            organization={organization}\n            onRemoveUsers={userIds =\u003e removeUsersFromOrganization({\n              variables: {organizationId, userIds},\n            })}\n          /\u003e\n        )}\n      \u003c/Mutation\u003e\n    )}\n  \u003c/ApolloConsumer\u003e\n)\n\nconst OrganizationsViewContainer = () =\u003e (\n  \u003cQuery query={query}\u003e\n    {({data}) =\u003e {\n      const {Organizations} = data || {}\n      if (!Organizations) return \u003cdiv /\u003e\n      return (\n        \u003cdiv\u003e\n          \u003ch1\u003eOrganizations\u003c/h1\u003e\n          {Organizations.map((organization) =\u003e (\n            \u003cOrganizationViewContainer\n              key={organization.id}\n              organization={organization}\n            /\u003e\n          )}\n        \u003c/div\u003e\n      )\n    }}\n  \u003c/Query\u003e\n)\n```\n\n## Handling associations being created\n\nAssuming the same `Organization`s/`User`s schema as above, the example performs\nthe necessary refetches when a user is created and added to an organization:\n\n```js\nrefetch(client, [['User'], ['Organization', organizationId]])\n```\n\nIn this case no `ids` are given for `User`, so any query containing the an\n`Organization` with the given `organizationId` in its results and selecting any\n`User`s would be refetched. (This doesn't perfectly exclude cases that fetch\nUsers and Organizations separately, instead of one nested inside the other, but\nit's better than nothing).\n\n```js\nimport * as React from 'react'\nimport gql from 'graphql-tag'\nimport refetch from 'apollo-magic-refetch'\nimport { Mutation, ApolloConsumer } from 'react-apollo'\nimport CreateUserForm from './CreateUserForm'\n\nconst mutation = gql`\n  mutation createUser($organizationId: Int!, $values: CreateUser!) {\n    result: createUser(organizationId: $organizationId, values: $values) {\n      organizationId\n      id\n      username\n    }\n  }\n`\n\nconst CreateUserFormContainer = ({ organizationId }) =\u003e (\n  \u003cApolloConsumer\u003e\n    {(client) =\u003e (\n      \u003cMutation\n        mutation={mutation}\n        update={() =\u003e\n          refetch(client, [['User'], ['Organization', organizationId]])\n        }\n      \u003e\n        {(createUser) =\u003e (\n          \u003cCreateUserForm\n            onSubmit={(values) =\u003e\n              createUser({\n                variables: { organizationId, values },\n              })\n            }\n          /\u003e\n        )}\n      \u003c/Mutation\u003e\n    )}\n  \u003c/ApolloConsumer\u003e\n)\n```\n\n## API\n\n### `refetch(client, typenameOrTerms, [predicate, [idField]])`\n\n```js\nimport refetch from 'apollo-magic-refetch'\n```\n\nScans active queries in the given `ApolloClient` and refetches any that contain\ndata matching the given type(s)/id(s).\n\n#### Arguments\n\n##### `client: ApolloClient`\n\nThe `ApolloClient` in which to scan active queries.\n\n##### `typenameOrTerms: string | Array\u003cTerm\u003e`\n\nThe `__typename` of the GraphQL type that was created or deleted, or an array of\n`[typename, predicate, idField]` tuples (`predicate` and `idField` are optional). If an\narray is given, a query must match all of the conditions in the array to be\nrefetched.\n\n##### `predicate: any` (_optional_)\n\nA single id, an array of ids, or a `Set` of ids that were deleted, or a\npredicate function that takes an instance of the GraphQL type and returns `true`\nif the query should be refetched. If given, only active queries whose current\nresult matches the predicate or contains an object with the given `typename` and\n`id` will be refetched.\n\n##### `idField: string` (_optional, default_: `'id'`)\n\nThe name of the id field in the type that was deleted. This is only used if\n`predicate` is not an id, array, or `Set` of ids, rather than a `function`.\n\n### `refetch.fetchTypeMetadata(client)`\n\nPrefetches type metadata by running an introspection query on the given on\n`ApolloClient`. The server must support client introspection queries;\notherwise use [`refetch.setTypeMetadata`](#refetchsettypemetadatatypemetadatapromise).\n\n#### Arguments\n\n##### `client: ApolloClient`\n\nThe client to fetch type metadata from.\n\n### `refetch.setTypeMetadata(typeMetadataPromise)`\n\nSets the type metadata to use for determing which queries to refetch.\nUse this method if your server forbids client introspection queries.\n\n#### Arguments\n\n##### `typeMetadataPromise: TypeMetadata | Promise\u003cTypeMetadata\u003e`\n\nThe result of executing the [`typesQuery`](#typesquery) GraphQL query\nor a `Promise` that will resolve to the result.\n\n### `typesQuery`\n\n```js\nimport { typesQuery } from 'apollo-magic-refetch'\n```\n\nThe parsed GraphQL introspection query that gets all of the type metadata\nneeded to determine which queries to refetch. Use this if your server forbids\nclient introspection queries; execute this query on the server side and send\nthe result to the client code that calls\n[`refetch.setTypeMetadata`](#refetchsettypemetadatatypemetadatapromise).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fapollo-magic-refetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcoreio%2Fapollo-magic-refetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fapollo-magic-refetch/lists"}