Apollo Query Result

Proof of concept implementation of Suspense for Data Fetching for Apollo.

WARNING! This is not a real package (yet)!

Implementation Details

Check out the useQueryResource.

useObservableQuery

First, we assign a unique ID to every query. By default, it is an operation name, but it can be configured explicitly.
Then, we create an ObservableQuery and cache it in a Suspense Cache.
Every ID corresponds to exactly one ObservableQuery.

const observableQuert = useObservableQuery(document, { variables });

useObservableQueryState

Next, we subscribe to ObservableQuery updates using new useSyncExternalStore API.
Similar to the standard useQuery.

const { loading, data, networkError, graphQLErrors } = useObservableQueryState(observableQuery);

settleObservableQuery

To suspend ObservableQuery we have a helper that suspends query while loading is true.
It also uses Suspense Cache to maintain a map
between ObservableQuery and a suspension `Promises.

settleObservableQuery(observableQuery); // Suspends!

useQueryResource

Finally, we wrap everything together into a QueryResource.

const resource = useQueryResource(document, { variables });

resource.read(); // Suspends!

// You can also work with data as usual
resource.isLoading();
resource.error();

Example

import { gql } from "@apollo/client";
import { useQueryResource } from "@hired/apollo-query-resource";

const GetPostQuery = gql`
  query GetPost($postId: Int!) {
    post(id: $postId) {
      id
      title
      body
    }
  }
`;

interface GetPostResult {
  post: {
    id: number;
    title: string;
    body: string;
  }
}

interface interface GetPostVariables {
  postId: number;
}

export const Post({ postId }: { postId: number }) {
  const resource = useQueryResource<GetPostResult, GetPostVariables>(GetPostQuery, { variables: { postId } })

  // No errors, no loading, just data!
  const { post: { title, body }} = resource.read(); // Suspends!

  return (
    <>
      <h1>{title}</h1>
      <p>{body}</p>
    <>
  );
}

Requirements

Currently only works with React 18 experimental.

GitHub

View Github