Docs

Get Started

Fetching data with React#

useQuery#

React hook that uses the main design of GQty.

This hook returns the core query object, and it will detect the usage of it, and suspend (if enabled) when the data is being fetched for the first time, and update the component when any of the requested data changes.

Features#

  • Composability
  • Advanced 'stale-while-revalidate' behavior, allowing you to set any value to re-trigger re-validation.
  • Extra $state in the returned value, which is extra valuable for Non-Suspense usage.

Suspense Example#

import { Suspense } from 'react'; import { useQuery } from '../gqty'; function Example() { const query = useQuery({ // boolean | undefined suspense: true, // boolean | object | number | string | null // If you put an object to trigger re-validation on-demand, it should be a `memoized` value from `useMemo` staleWhileRevalidate: true, // ((error: GQtyError) => void) | undefined onError(error) {}, }); return <p>{query.helloWorld}</p>; } function Container() { return ( <Suspense fallback="Loading..."> <Example /> </Suspense> ); }

Non-Suspense Example#

See Non-Suspense usage for more details

import { useQuery } from '../gqty'; function Example() { const query = useQuery({ // boolean | undefined suspense: false, // boolean | object | number | string | null // If you put an object to trigger re-validation on-demand, it should be a `memoized` value from `useMemo` staleWhileRevalidate: true, // ((error: GQtyError) => void) | undefined onError(error) {}, }); if (query.$state.isLoading) { return <p>Loading...</p>; } return <p>{query.helloWorld}</p>; }

prepare#

useQuery offers a nice helper that allows for good optimizations, allowing you to do a prepass over specific parts of your expected query, before your component ends rendering the first time.

It's basically a function that is called right before the useQuery returns, allowing your component to either Suspend immediately or add a conditional render using the $state.isLoading property.

The function receives as parameters:

  • The prepass helper.
  • Your auto-generated query object.

Since it's a function called before the hook itself returned, the returned value/values are undefined, and unfortunately TypeScript can't know that.

Suspense example#

import { useQuery } from '../gqty'; function ExampleSuspense() { const query = useQuery({ prepare({ prepass, query }) { prepass(query.users, 'id', 'name', 'dogs.name'); }, suspense: true, }); return ( <> {query.users.map(({ id, name, dogs }) => { return ( <p key={id}> Name: {name} <br /> Dogs: <br /> <ul> {dogs.map(({ name }) => { return <li key={name}>{name}</li>; })} </ul> </p> ); })} </> ); } // ... <Suspense fallback="Loading..."> <ExampleSuspense /> </Suspense>;

Non-Suspense example#

import { useQuery } from '../gqty'; function Example() { const query = useQuery({ prepare({ prepass, query }) { prepass(query.users, 'id', 'name', 'dogs.name'); }, suspense: false, }); if (query.$state.isLoading) return <p>Loading...</p>; return ( <> {query.users.map(({ id, name, dogs }) => { return ( <p key={id}> Name: {name} <br /> Dogs: <br /> <ul> {dogs.map(({ name }) => { return <li key={name}>{name}</li>; })} </ul> </p> ); })} </> ); }

graphql HOC#

graphql is a Higher-Order Component alternative to useQuery, designed specially for Suspense usage.

For this function it is expected to use the query object exported by the core client, and it will detect the usage of it, and suspend (if enabled) when the data is being fetched for the first time, and update the component when any of the requested data changes.

Features#

  • Specify Suspense fallback directly
  • Intercept the data requests faster than useQuery, allowing it to prevent an extra render.
  • Basic 'stale-while-revalidate' behavior

Example#

import { Suspense } from 'react'; import { graphql, query } from '../gqty'; const Example = graphql( function Example() { return <p>{query.helloWorld}</p>; }, { // boolean | { fallback: NonNullable<ReactNode> | null } | undefined suspense: true, // ((error: GQtyError) => void) | undefined onError(error) {}, // boolean | undefined staleWhileRevalidate: true, } ); function Container() { return ( <div> <Suspense fallback="Loading..."> <Example /> </Suspense> </div> ); }

Difference between "useQuery" and "graphql"#

If you read both, you could see that both do similar stuff, but enable different things based on it's nature and React constraints.

The graphql HOC is targeted specially for Suspense usage, since it can intercept the data requests before the render phase and Suspend right away, and also specify the fallback directly, but since it's a HOC, it's not as nice to use as hooks.

On the other hand, useQuery enables very nice composability, but it lacks the ability to intercept the Render Phase if not using useQuery "prepare" helper, which in practice it only means 1 extra render, which most of the time it doesn't matter.

And it's important to note that for Non-Suspense usage we encourage to always use useQuery.

usePaginatedQuery#

Paginated focused queries, in which you specify a function and initial arguments, which is going to be automatically called on first-render or via custom fetchMore calls.

Features#

  • Suspense support
  • Partial Fetch policy support ('cache-first', 'cache-and-network' or 'network-only')
  • Custom data merge function, with included uniqBy & sortBy helpers.
  • If no new args are defined in your fetchMore call, the previous or initial args are used.
  • You can override the existing fetchPolicy in the second parameter of fetchMore, for example, to force a refetch.
  • The helper functions are included in third parameter of main function.

Example#

import { usePaginatedQuery, ConnectionArgs } from '../gqty'; // ... function Example() { const { data, fetchMore, isLoading } = usePaginatedQuery( ( // Auto-generated query object query, // You have to specify the arguments types, in this example we are re-using auto-generated types input: ConnectionArgs, // Core helpers, in this example we are just using `getArrayFields` { getArrayFields } ) => { const { nodes, pageInfo: { hasNextPage, endCursor }, } = query.users({ ...input, }); return { nodes: getArrayFields(nodes, 'name'), hasNextPage, endCursor, }; }, { // Required, only used for the first fetch initialArgs: { first: 10, }, // Optional merge function merge({ data: { existing, incoming }, uniqBy }) { if (existing) { return { ...incoming, // If using 'cache-and-network', you have to use `uniqBy` nodes: uniqBy([...existing.nodes, ...incoming.nodes], (v) => v.id), }; } return incoming; }, // Default is 'cache-first' fetchPolicy: 'cache-and-network', // Default is `false`, it only applies for the initial fetch. suspense: true, } ); return ( <div> <ul> {data?.nodes.map(({ id = 0, name }) => { return <li key={id}>{name}</li>; })} </ul> {isLoading && <p>Loading...</p>} {data?.hasNextPage && data.endCursor ? ( <button disabled={isLoading} onClick={() => { fetchMore({ first: 10, after: data.endCursor, }); }} > Load more </button> ) : null} </div> ); }

useTransactionQuery#

Alternative to graphql and useQuery that works via pre-defined functions, which allows for extra features that are not available in useQuery & graphql HOC.

Features#

  • Polling
  • Conditional skipping
  • Automatic call on variable change
  • Lifecycle functions onCompleted & onError
  • Suspense support
  • Fetch policy support

Example#

import { useTransactionQuery } from '../gqty'; // ... function Example() { const { data, error, isLoading } = useTransactionQuery( (query, args: string) => { return query.hello({ name }); }, { variables: 'John', // By default is 'cache-first' fetchPolicy: 'cache-and-network', // Polling every 5 seconds pollInterval: 5000, // By default is `true` notifyOnNetworkStatusChange: true, onCompleted(data) {}, onError(error) {}, suspense: false, // By default is `false` skip: false, } ); if (isLoading) { return <p>Loading...</p>; } if (error) { return <p>Error! {error.message}</p>; } return <p>{data}</p>; }

useLazyQuery#

Queries meant to be called in response of events, like button clicks.

Features#

  • Lifecycle functions onCompleted & onError
  • Suspense support
  • Partial Fetch policy support (no 'cache-first')
import { useLazyQuery } from '../gqty'; function Example() { const [getName, { isLoading, data }] = useLazyQuery( (query, name: string) => { return query.hello({ name }); }, { onCompleted(data) {}, onError(error) {}, // Default is 'network-only' fetchPolicy: 'cache-and-network', retry: false, suspense: false, } ); if (isLoading) { return <p>Loading...</p>; } if (data) { return <p>{data}</p>; } return ( <button onClick={() => getName({ args: 'John', }) } > Get Name </button> ); }

useRefetch#

Refetch giving specific parts of the schema or via functions

Example with functions#

import { useRefetch, useQuery } from '../gqty'; function Example() { const refetch = useRefetch(); const query = useQuery(); return ( <div> <button onClick={() => { refetch(() => query.helloWorld); }} > Refetch </button> <p>{query.helloWorld}</p> </div> ); }

Example with objects#

In this case, you have to specify an object type, scalars won't work (for those, you have to use functions).

It will automatically refetch everything previously requested under that tree

import { useRefetch, useQuery } from '../gqty'; function Example() { const refetch = useRefetch(); const query = useQuery(); return ( <div> <button onClick={() => { // It will refetch both, 'name' & 'email' refetch(user); }} > Refetch </button> <p>{query.user.name}</p> <p>{query.user.email}</p> </div> ); }

prepareQuery#

Prepare queries on module-level.

Features#

Example#

import { useState } from 'react'; import { prepareQuery } from '../gqty'; const { preload, refetch, usePrepared, callback } = prepareQuery((query) => { return query.helloWorld; }); function Example() { const { data } = usePrepared(); return <p>{data}</p>; } function Container() { const [show, setShow] = useState(false); return ( <div> <button onClick={() => { if (show) { refetch(); } else { preload(); } }} > {show ? 'Refetch' : 'Show Example'} </button> {show && <Example />} </div> ); }

Fetch Policy#

Fetch policies allow you to have fine-grained control over your fetch behaviors

NameDescription
cache-firstClient executes the query against the cache, if all requested data is present in the cache, that data is returned.
Otherwise, it executes the query against your GraphQL server and returns the data after caching it.

Prioritizes minimizing the number of network requests sent by your application.
cache-and-networkClient executes the full query against both the cache and your GraphQL server.

Provides a fast response while also helping to keep cached data consistent with server data.
network-onlyClient executes the full query against your GraphQL server, without first checking the cache.

The query's result is stored in the cache.
no-cacheClient executes the full query against your GraphQL server, without first checking the cache.

The query's result is NOT stored in the cache.