- Notifications
You must be signed in to change notification settings - Fork2
magically refetches relevant apollo graphql queries after creates, deletes, and association changes
License
jcoreio/apollo-magic-refetch
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Handling Apollo cache updates after creating and deleting objects, orassociating and dissociating objects, remains apoorly solved problem.update andrefetchQueries props onMutations couple different areas ofyour app in a way you probably don't want, and they don't scale well as you addmore queries over objects you may create/delete.
Truly solving the problem will probably require changes to the apollo clientand cache code.
Until that happens, this is probably your best bet!
- How it works
- Current limitations
- ES environment requirements
- Type metadata usage
- Handling Deletions
- Handling Creation
- Handling associations being broken
- Handling associations being created
- API
After you delete an object, you tellapollo-magic-refetch whattypename andid was deleted, and it refetches all active queries that contain that objectanywhere within their current data!
Similarly, after you create an object, you tell it thetypename of the createdobject and it refetches all active queries that contain an object of that typein their selections. This is a bit less efficient than handling deletes, butway easier than anything else at the time of writing.
Since only active queries can be refetched, data in the cache for inactivequeries will remain out-of-date. For that reason, I would recommend using thecache-and-network policy on all queries you're not planning toupdate afterall pertinent mutations.
- Interfaces and union types are not supported yet. This means if they areanywhere in your results, this library may fail to refetch when it should.
- Lists of lists are not supported yet.
If you are building for legacy browsers with a bundler like Webpack, make sureto add a rule to transpile this package to ES5.
If you are not using a bundler that supports themodule property inpackage.json, make sure to installbabel-runtime.
apollo-magic-refetch uses type metadata from GraphQL determine which queriesneed to be refetched; the client must get this metadata from the server.If your schema is large enough it may be a prohibitive amount of metadata.refetch operations will be delayed until this metadata is fetched.To prefetch this metadata via a GraphQL introspection query, do:
importclientfrom'./wherever/you/create/your/apollo/client'importrefetchfrom'apollo-magic-refetch'// initiate the prefetchrefetch.fetchTypeMetadata(client)
If your server forbids client introspection queries, you will have to fetch themetadata by other means. For instance, you could execute the required introspectionquery on the server, and serve the result on a custom REST route:
import{execute}from'graphql'importschemafrom'./path/to/your/graphql/schema'importexpressfrom'express'import{typesQuery}from'apollo-magic-refetch'constapp=express()consttypeMetadataPromise=execute(schema,typesQuery)app.get('/graphql/refetchTypeMetadata',(req,res)=>{typeMetadataPromise.then((data)=>res.json(data))})
And then pass this data torefetch.setTypeMetadatabefore you ever callrefetch():
importrefetchfrom'apollo-magic-refetch'// accepts a promise that resolves to the graphql execution result.refetch.setTypeMetadata(fetch('/graphql/refetchTypeMetadata').then((res)=>res.json()))
Typically you callrefetch within theupdate callback of yourMutationthat deletes objects. You just have to callrefetch with the__typenamethat was deleted (in this case,Device) and theid of the deleted object.This refetches any active queries that contain the deleted object in cached data.
For mutations that delete multiple things at once, you may pass an array orSetof ids torefetch, or make multiple calls torefetch in yourupdate method.
import*asReactfrom'react'importgqlfrom'graphql-tag'importrefetchfrom'apollo-magic-refetch'import{Mutation,ApolloConsumer}from'react-apollo'constmutation=gql`mutation destroyDevice($deviceId: Int!) { destroyDevice(deviceId: $deviceId)}`constDestroyDeviceButton=({deviceId})=>(<ApolloConsumer>{client=>(<Mutationmutation={mutation}update={()=>refetch(client,'Device',deviceId)}/>{destroyDevice=>(<buttononClick={destroyDevice({variables:{deviceId}})})} </Mutation> )}</ApolloConsumer>)
Typically you callrefetch within theupdate callback of yourMutationthat creates objects. You just have to callrefetch with the__typenamethat was created.
Unlike deletions, you don't pass theid of the createdobject. Without a specificid to search for, it simply refetches all activequeries that contain any object of the requested__typename in their cacheddata, in case the created object belongs in the new results. This is lessefficient than refetching queries containing a specificid, but far easierthan manually inserting the created object into each relevant query.
In this example, the__typename of the object being created isDevice.
import*asReactfrom'react'importgqlfrom'graphql-tag'importrefetchfrom'apollo-magic-refetch'import{Mutation,ApolloConsumer}from'react-apollo'importCreateDeviceFormfrom'./CreateDeviceForm'constmutation=gql`mutation createDevice($values: CreateDevice!) { createDevice(values: $values) { id }}`constCreateDeviceFormContainer=()=>(<ApolloConsumer>{client=>(<Mutationmutation={mutation}update={()=>refetch(client,'Device')}/>{createDevice=>(<CreateDeviceFormonSubmit={(values)=>createDevice({variables:{values}})}/>)}</Mutation>)} </ApolloConsumer>)
In this example, a view shows a list ofOrganizations, each containing asublist ofUsers. When one or more users is removed from an organization,it makes the following call:
refetch(client,[['User',userIds],['Organization',organizationId],])
Passing an array torefetch means to only refetch queries containing all ofthe conditions in the array. So the query below would be refetched, but a querycontaining onlyOrganizations or a query containing onlyUsers would not.
import*asReactfrom'react'importgqlfrom'graphql-tag'importrefetchfrom'apollo-magic-refetch'import{Mutation,ApolloConsumer}from'react-apollo'importOrganizationViewfrom'./OrganizationView'constquery=gql`query { Organizations { id name Users { id username } }}`constmutation=gql`mutation removeUsersFromOrganization($organizationId: Int!, $userIds: [Int!]!) { result: removeUsersFromOrganization(organizationId: $organizationId, userIds: $userIds) { organizationId userIds }}`constOrganizationViewContainer=({organization:{id, name, Users}})=>(<ApolloConsumer>{client=>(<Mutationmutation={mutation}update={(cache,{data:{result:{organizationId, userIds}}})=>refetch(client,[['User',userIds],['Organization',organizationId],])}>{removeUsersFromOrganization=>(<OrganizationVieworganization={organization}onRemoveUsers={userIds=>removeUsersFromOrganization({variables:{organizationId, userIds},})}/>)}</Mutation>)}</ApolloConsumer>)constOrganizationsViewContainer=()=>(<Queryquery={query}>{({data})=>{const{Organizations}=data||{}if(!Organizations)return<div/>return(<div><h1>Organizations</h1>{Organizations.map((organization)=>(<OrganizationViewContainerkey={organization.id}organization={organization}/>)}</div>)}}</Query>)
Assuming the sameOrganizations/Users schema as above, the example performsthe necessary refetches when a user is created and added to an organization:
refetch(client,[['User'],['Organization',organizationId]])
In this case noids are given forUser, so any query containing the anOrganization with the givenorganizationId in its results and selecting anyUsers would be refetched. (This doesn't perfectly exclude cases that fetchUsers and Organizations separately, instead of one nested inside the other, butit's better than nothing).
import*asReactfrom'react'importgqlfrom'graphql-tag'importrefetchfrom'apollo-magic-refetch'import{Mutation,ApolloConsumer}from'react-apollo'importCreateUserFormfrom'./CreateUserForm'constmutation=gql` mutation createUser($organizationId: Int!, $values: CreateUser!) { result: createUser(organizationId: $organizationId, values: $values) { organizationId id username } }`constCreateUserFormContainer=({ organizationId})=>(<ApolloConsumer>{(client)=>(<Mutationmutation={mutation}update={()=>refetch(client,[['User'],['Organization',organizationId]])}>{(createUser)=>(<CreateUserFormonSubmit={(values)=>createUser({variables:{ organizationId, values},})}/>)}</Mutation>)}</ApolloConsumer>)
importrefetchfrom'apollo-magic-refetch'
Scans active queries in the givenApolloClient and refetches any that containdata matching the given type(s)/id(s).
TheApolloClient in which to scan active queries.
The__typename of the GraphQL type that was created or deleted, or an array of[typename, predicate, idField] tuples (predicate andidField are optional). If anarray is given, a query must match all of the conditions in the array to berefetched.
A single id, an array of ids, or aSet of ids that were deleted, or apredicate function that takes an instance of the GraphQL type and returnstrueif the query should be refetched. If given, only active queries whose currentresult matches the predicate or contains an object with the giventypename andid will be refetched.
The name of the id field in the type that was deleted. This is only used ifpredicate is not an id, array, orSet of ids, rather than afunction.
Prefetches type metadata by running an introspection query on the given onApolloClient. The server must support client introspection queries;otherwise userefetch.setTypeMetadata.
The client to fetch type metadata from.
Sets the type metadata to use for determing which queries to refetch.Use this method if your server forbids client introspection queries.
The result of executing thetypesQuery GraphQL queryor aPromise that will resolve to the result.
import{typesQuery}from'apollo-magic-refetch'
The parsed GraphQL introspection query that gets all of the type metadataneeded to determine which queries to refetch. Use this if your server forbidsclient introspection queries; execute this query on the server side and sendthe result to the client code that callsrefetch.setTypeMetadata.
About
magically refetches relevant apollo graphql queries after creates, deletes, and association changes
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.