Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apollo-magic-refetch

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apollo-magic-refetch

magically refetches relevant queries after creates and deletes

  • 1.1.5
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
211
increased by11.05%
Maintainers
1
Weekly downloads
 
Created
Source

apollo-magic-refetch

Build Status Coverage Status semantic-release Commitizen friendly npm version

Handling Apollo cache updates after creating and deleting objects, or associating and dissociating objects, remains a poorly solved problem. update and refetchQueries props on Mutations couple different areas of your app in a way you probably don't want, and they don't scale well as you add more queries over objects you may create/delete.

Truly solving the problem will probably require changes to the apollo client and cache code.

Until that happens, this is probably your best bet!

Table of Contents

How it works

After you delete an object, you tell apollo-magic-refetch what typename and id was deleted, and it refetches all active queries that contain that object anywhere within their current data!

Similarly, after you create an object, you tell it the typename of the created object and it refetches all active queries that contain an object of that type in their selections. This is a bit less efficient than handling deletes, but way easier than anything else at the time of writing.

Since only active queries can be refetched, data in the cache for inactive queries will remain out-of-date. For that reason, I would recommend using the cache-and-network policy on all queries you're not planning to update after all pertinent mutations.

Current limitations

Interfaces and union types are not supported yet. This means if they are anywhere in your results, this library may fail to refetch when it should.

Also, lists of lists are not supported, if for whatever reason you are using lists of lists in your schema (I haven't even checked if this is possible).

Recursive queries are not currently working (https://github.com/jcoreio/apollo-magic-refetch/issues/2); currently it stops at objects of the target type, rather than looking at their descendants.

ES environment requirements

If you are building for legacy browsers with a bundler like Webpack, make sure to add a rule to transpile this package to ES5.

If you are not using a bundler that supports the modules.root property in package.json, make sure to install babel-runtime.

Type metadata usage

apollo-magic-refetch uses type metadata that it must fetch from GraphQL. 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, do:

import client from './wherever/you/create/your/apollo/client'
import getSchemaTypes from 'apollo-magic-refetch/getSchemaTypes'

// initiate the prefetch
getSchemaTypes(client)

Handling Deletions

In this example, the __typename of the object being deleted is Device and it uses the standard id field. If instead the field were called tag, for instance, you would pass 'tag' after deviceId in the call to refetch.

You may pass an array or Set of ids in place of a single deviceId.

import * as React from 'react'
import gql from 'graphql-tag'
import refetch from 'apollo-magic-refetch'
import {Mutation, ApolloConsumer} from 'react-apollo'

const mutation = gql`
mutation destroyDevice($deviceId: Int!) {
  destroyDevice(deviceId: $deviceId)
}
`

const DestroyDeviceButton = ({deviceId}) => (
  <ApolloConsumer>
    {client => (
      <Mutation
        mutation={mutation}
        update={() => refetch(client, 'Device', deviceId)}
      />
        {destroyDevice => (
          <button onClick={destroyDevice({variables: {deviceId}})}
        )}
      </Mutation>
    )}
  </ApolloConsumer>
)

Handling Creation

When you omit the id parameter, refetch refetches all active queries that contain the requested __typename in their selections, regardless of what ids are actually in their results. This can be used after creating an object.

In this example, the __typename of the object being created is Device.

import * as React from 'react'
import gql from 'graphql-tag'
import refetch from 'apollo-magic-refetch'
import {Mutation, ApolloConsumer} from 'react-apollo'
import CreateDeviceForm from './CreateDeviceForm'

const mutation = gql`
mutation createDevice($values: CreateDevice!) {
  createDevice(values: $values) {
    id
  }
}
`

const CreateDeviceFormContainer = () => (
  <ApolloConsumer>
    {client => (
      <Mutation
        mutation={mutation}
        update={() => refetch(client, 'Device')}
      />
        {createDevice => (
          <CreateDeviceForm
            onSubmit={(values) => createDevice({variables: {values}})}
          />
        )}
      </Mutation>
    )}
  </ApolloConsumer>
)

Handling associations being broken

In this example, a view shows a list of Organizations, each containing a sublist of Users. When one or more users is removed from an organization, it makes the following call:

refetch(client, [['User', userIds], ['Organization', organizationId]])

Passing an array to refetch means to only refetch queries containing all of the conditions in the array. So the query below would be refetched, but a query containing only Organizations or a query containing only Users would not.

import * as React from 'react'
import gql from 'graphql-tag'
import refetch from 'apollo-magic-refetch'
import {Mutation, ApolloConsumer} from 'react-apollo'
import OrganizationView from './OrganizationView'

const query = gql`
query {
  Organizations {
    id
    name
    Users {
      id
      username
    }
  }
}
`

const mutation = gql`
mutation removeUsersFromOrganization($organizationId: Int!, $userIds: [Int!]!) {
  result: removeUsersFromOrganization(organizationId: $organizationId, userIds: $userIds) {
    organizationId
    userIds
  }
}
`

const OrganizationViewContainer = ({organization: {id, name, Users}}) => (
  <ApolloConsumer>
    {client => (
      <Mutation
        mutation={mutation}
        update={(cache, {data: {result: {organizationId, userIds}}}) =>
          refetch(client, [
            ['User', userIds],
            ['Organization', organizationId],
          ])
        }
      >
        {removeUsersFromOrganization => (
          <OrganizationView
            organization={organization}
            onRemoveUsers={userIds => removeUsersFromOrganization({
              variables: {organizationId, userIds},
            })}
          />
        )}
      </Mutation>
    )}
  </ApolloConsumer>
)

const OrganizationsViewContainer = () => (
  <Query query={query}>
    {({data}) => {
      const {Organizations} = data || {}
      if (!Organizations) return <div />
      return (
        <div>
          <h1>Organizations</h1>
          {Organizations.map((organization) => (
            <OrganizationViewContainer
              key={organization.id}
              organization={organization}
            />
          )}
        </div>
      )
    }}
  </Query>
)

Handling associations being created

Assuming the same Organizations/Users schema as above, the example performs the necessary refetches when a user is created and added to an organization:

refetch(client, [['User'], ['Organization', organizationId]])

In this case no ids are given for User, so any query containing the an Organization with the given organizationId in its results and selecting any Users would be refetched. (This doesn't perfectly exclude cases that fetch Users and Organizations separately, instead of one nested inside the other, but it's better than nothing).

import * as React from 'react'
import gql from 'graphql-tag'
import refetch from 'apollo-magic-refetch'
import { Mutation, ApolloConsumer } from 'react-apollo'
import CreateUserForm from './CreateUserForm'

const mutation = gql`
  mutation createUser($organizationId: Int!, $values: CreateUser!) {
    result: createUser(organizationId: $organizationId, values: $values) {
      organizationId
      id
      username
    }
  }
`

const CreateUserFormContainer = ({ organizationId }) => (
  <ApolloConsumer>
    {client => (
      <Mutation
        mutation={mutation}
        update={() =>
          refetch(client, [['User'], ['Organization', organizationId]])
        }
      >
        {createUser => (
          <CreateUserForm
            onSubmit={values =>
              createUser({
                variables: { organizationId, values },
              })
            }
          />
        )}
      </Mutation>
    )}
  </ApolloConsumer>
)

API

refetch(client, typenameOrTerms, [ids], [idField])

Arguments
client: ApolloClient

The ApolloClient in which to scan active queries.

typenameOrTerms: string | Array<Term>

The __typename of the GraphQL type that was created or deleted, or an array of [typename, ids, idField] tuples (ids and idField are optional). If an array is given, a query must match all of the conditions in the array to be refetched.

ids: any (optional)

A single id, an array of ids, or a Set of ids that were deleted. If given, only active queries whose current result contains an object with the given typename and id will be refetched.

idField: string (optional, default: 'id')

The name of the id field in the type that was deleted.

Keywords

FAQs

Package last updated on 20 Dec 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc