npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

effector-apollo

v0.5.0

Published

Effector bindings for Apollo Client

Downloads

26

Readme

effector-apollo

A lightweight wrapper around Apollo Client to interoperate with ☄️ Effector. Create and manage your GraphQL Queries & Mutations declaratively, with Effector, while using powerful Apollo Client features, like normalized cache and Apollo Link.

🔗 Installation & use

$ npm install effector-apollo
# or
$ yarn add effector-apollo
# or
$ pnpm add effector-apollo

Note that this library requires effector@23 and @apollo/client as peer dependencies, so make sure you have them installed in your project.

API

createQuery

Creates a new query that allows you to read data from GraphQL server or cache on request.

Options:

  • client: ApolloClient | Store<ApolloClient> that the query will use to fetch data
  • document: DocumentNode describing your query
  • context: DefaultContext? allows you to (optionally) provide arbitrary context to your Apollo Link

Returns: a new Query instance

Query

Commands:

  • start: EventCallable<Variables> unconditionally starts your query, and will send a network request
  • refresh: EventCallable<Variables> will refresh the query, using cache if possible
  • reset: EventCallable<void> resets your query to its initial state

Query state:

  • $data: Store<Data | null> containing your query data
  • $error: Store<ApolloError | null> possibly containing query execution error
  • $status: Store<'initial' | 'pending' | 'done' | 'fail'> reflecting current status of your query, which is also split into separate 'convenience' stores
    • $idle: true if the Query has not ever started
    • $pending: true if the Query is currently fetching
    • $failed: true if the Query has failed with some ApolloError
    • $succeeded: true if the Query has succeeded with data
    • $finished: true if the Query has finished with either success or failure

Query state events:

  • finished.success: Event<{ variables: Variables; meta: QueryMeta, data: Data }> is fired when your Query succeeds, providing variables that you called the query with, and data that the query has returned
  • finished.failure: Event<{ variables: Variables; meta: QueryMeta, error: ApolloError }> is fired when your Query fails, providing variables and the corresponding error
  • finished.finally: Event is fired when your Query finishes with either status: "done" or status: "fail", and will provide you with data/error

Example usage

const query = createQuery({
  client,
  document: gql`
    query user {
      user {
        id
        name
      }
    }
  `,
})

sample({
  clock: appStarted,
  target: query.start,
})

createMutation

Creates a new mutation to modify data on your GraphQL server.

Options:

  • client: ApolloClient | Store<ApolloClient> that the mutation will use to fetch data
  • document: DocumentNode describing your mutation
  • context: DefaultContext? allows you to (optionally) provide arbitrary context to your Apollo Link

Returns: a new Mutation instance

Mutation

Commands:

  • start: EventCallable<Variables> unconditionally starts your Mutation, and will send a network request immediately
  • reset: EventCallable<void> resets your mutation to its initial state

Mutation state:

  • $status: Store<'initial' | 'pending' | 'done' | 'fail'> reflecting current status of your Mutation, which is also split into separate 'convenience' stores
    • $idle: true if the Mutation has not ever started
    • $pending: true if the Mutation is currently fetching
    • $failed: true if the Mutation has failed with some ApolloError
    • $succeeded: true if the Mutation has succeeded with data
    • $finished: true if the Mutation has finished with either success or failure

Mutation state events:

  • finished.success: Event<{ variables: Variables; data: Data }> is fired when your Mutation succeeds with data that the GraphQL server has returned
  • finished.failure: Event<{ variables: Variables; error: ApolloError }> is fired when your Mutation fails with the corresponding execution error
  • finished.finally: Event that's fired when your Mutation finishes with either status: "done" or status: "fail", and will provide you with data/error

createFragmentBinding

createFragmentBinding sets up a light, read-only binding into Apollo Cache. This can come in handy to access entities from your Apollo Cache without requiring a specific Query to do that.

Like useFragment from Apollo Client, fragment binding does not trigger network requests on its own. Rather, it keeps data in sync between Apollo Cache and Effector.

Unlike useFragment, however, createFragmentBinding's primary purpose is to provide you with singleton-like entities that your business logic might requre. Common examples would be: currently logged in User entity, a specific Setting, or feature flags.

Options:

  • client: ApolloClient | Store<ApolloClient> that provides cached data

  • document: DocumentNode with a single fragment definition you want to bind

  • id: Store<string> | Store<StoreObject> to identify a specific fragment

    • When provided with Store<string>, binding will treat this as a ready-to-use Canonical Cache ID (like User:137)

    • When provided with Store<StoreObject>, binding treats this as an object with key fields that uniquely identify your fragment

      Unless you customize your cache ID, id or _id are your key fields. So, Store<{ id: string }> is usually the way to go!

      If you did customize cache ID for your fragment, provide all necessary key fields set in your type policy.

      💡 You do not need to provide __typename in this Store, as it is inferred from your fragment.

  • variables?: Store<Variables> that your fragment requires to resolve

    • You can omit this if your fragment does not require any variables
    • ⚠️ If the fragment does need variables, you must provide this option with correct Variables, otherwise you'll never receive data
  • setup: Event that the binding will initialize on (upon trigger)

    • Usually, that would be your appStarted event, but you can also use any other trigger, like Query.finished.success
  • teardown: Event tears down the binding, clearing $data and stopping listening for updates

  • optimistic?: boolean can be set to false to disable reading optimistic cache

Returns: a new FragmentBinding

FragmentBinding

A live binding into ApolloCache for a specific fragment.

  • $data: Store<Data | null> containing your fragment data (or null if no fragment was found in Cache)
  • $active: Store<boolean> marking if your binding is active or not

watchQuery

watchQuery allows you to subscribe a particular query to Apollo Cache. By default, Query only reads data through a request, and does not read cache (unlike Apollo's React hooks).

This operator allows you to connect Query to cache if you expect other parts of your app to request the same query. This will help you avoid extra requests.

Options:

  • query: Query that you want to subscribe to cache
  • optimistic?: boolean can be set to false to disable reading optimistic cache
  • client?: ApolloClient | Store<ApolloClient> to use cache from. Will use the client from createQuery if not provided

keepFresh

Enables automatic refreshes for a query, ensuring that the data stays up-to-date in response to specific events or triggers.

Options:

  • query: Query you want to keep fresh
  • triggers: Array<Event | TriggerProtocol> containing triggers that will invalidate the query and initiate a network request for fresh data. Trigger can be either:
  • enabled?: Store<boolean> that controls whether the automatic refresh is enabled or disabled. If ommited, defaults to always enabled

Fetch the query when network connection is restored

import { keepFresh, createQuery } from "effector-apollo"
import { trackNetworkStatus } from "@withease/web-api"

const userQuery = createQuery({ client, document: USER_QUERY })

// If the connection is lost, fetch User as soon as it is restored
keepFresh(userQuery, {
  triggers: [trackNetworkStatus],
})

Refetch a related query when a mutation fails

import { keepFresh, createQuery, createMutation } from "effector-apollo"

const userQuery = createQuery({ client, document: USER_QUERY })
const updateCountryMutation = createMutation({ client, document: UPDATE_COUNTRY_MUTATION })

// When update fails, data may become inconsistent,
// so we refetch User, ensuring data is most up to date
keepFresh(userQuery, {
  triggers: [updateCountryMutation.finished.failure],
})

paginate

paginate creates a fetchMore-like function for your query, allowing for easy pagination powered by Apollo Cache.

Note that this operator requires two things from you to work properly:

  1. You must define a proper Field Policy on a paginated field using a merge function of InMemoryCache. This allows to store paginated results in the cache
  2. The Query must not be "idle" when you use pagination. Fetch the Query using .refresh or .start before paginating.

Triggering the event returned by paginate(query) will

  • shallowly merge last used query variables and new variables provided to paginate
  • execute the query to fetch the next page based on updated variables
  • merge pages using your policy (as defined in InMemoryCache)
  • store results in query.$data

Read more about Pagination API in Apollo.

Options:

  • query: Query that will be paginated

Returns: EventCallable<Partial<Variables>> which you can call to fetch the next page

Paginate a query using a cursor in a Relay-style pagination.

import { createQuery, paginate } from "effector-apollo"
import { gql } from "@apollo/client"

const document = gql`
  query list($cursor: String) {
    items(cursor: $cursor) {
      nodes {
        id
      }

      pageInfo {
        endCursor
      }
    }
  }
`

const listQuery = createQuery({ client, document })

const nextPageRequested = createEvent<void>()

sample({
  // when the next page is requested
  clock: nextPageRequested,
  // take the end cursor of the list
  source: listQuery.$data.map(({ items }) => items.pageInfo.endCursor),
  // construct an update to query variables
  fn: (cursor) => ({ cursor }),
  // launch pagination
  target: paginate(listQuery),
})

optimistic

optimistic helps you define an optimistic response for your mutation. This will fill in data in Apollo Cache when running the mutation, so that your UI is responsive to user action. See more in Apollo Client "Optimistic results" documentation.

Options:

  • mutation: Mutation that you want to define an optimistic response for
  • fn: Variables => Data function that constructs an optimistic response for a mutation
  • client?: ApolloClient | Store<ApolloClient> to write response to. Will use the client from createMutation if not provided

💬 Pick your library

When starting a new project from scratch, please, take a look at Farfetched, a great data fetching tool, before using effector-apollo.

This library is an interoperability layer for projects that already use Apollo Client. It makes your life easier by giving you access to your GraphQL data from Effector.

This library strives to keep its API similar to Farfetched so that your future migration to this tool is simpler.

Releases policy

Versions 0.x.x may contain breaking changes in minor releases, which will be documented.