Introducing the new React Query client
Tags: react, graphql, javascript, webdev
Published: 2023-01-03 16:17:41



After much requests we are excited to announce the release of the new WunderGraph React Query client. This new client is built on top of [TanStack React Query](https://tanstack.com/query/v4), a powerful data fetching library for React. It allows you to consume WunderGraph queries, mutations and subscriptions fully typesafe with React Query.

We took all the lessons learned from building our new SWR integration and applied them to this new client. This means that you can use the same features as with the SWR integration, like optimistic updates, invalidating queries, and more with a similar easy to use and typesafe API.

Currently we only support React, but thanks to TanStack we are now able to add support for other frameworks like Vue, Solid.js and soon Svelte. We would love to have your help in building these integrations. Check out the [React Query source code](https://github.com/wundergraph/wundergraph/tree/main/packages/react-query) to get started. ❤️

## What's new

Let's dive a bit into what's new and how some of the React Query features work with WunderGraph.

### Queries

```ts
const { data, error } = useQuery({
  operationName: 'Weather',
  input: {
    forCity: 'Berlin',
  },
})
```

Conditionally fetching data:

```ts
const { data: user } = useUser()

const { data, error, refetch } = useQuery({
  operationName: 'ProtectedWeather',
  input: {
    forCity: 'Berlin',
  },
  enabled: !!user,
})
```

Note that `refetch` is slightly different from SWR, instead `mutate` we can call `refetch` to refetch a specific query.

Turn queries into live queries, live queries are refetched on a interval on the WunderGraph server.

```ts
const { data, error } = useQuery({
  operationName: 'Weather',
  input: {
    forCity: 'Berlin',
  },
  liveQuery: true,
})
```

### Subscriptions

Build realtime apps with subscriptions.

```ts
const { data, error, isLoading, isSubscribed } = useSubscription({
  operationName: 'Countdown',
  input: {
    from: 100,
  },
})
```

### Mutations

```ts
const { data, error, mutate, mutateAsync } = useMutation({
  operationName: 'SetName',
})

mutate({ name: 'Eelco' })

// Async mutate
const result = await mutateAsync({ name: 'Eelco' })
```

### Invalidating queries

Let's say we have a query that fetches the current user's profile in one component and we have a form that updates the profile. We can add an `onSuccess` handler to the mutation that calls `queryClient.invalidateQueries` on the `GetProfile` query and trigger a refetch and update the internal React Query cache.

```ts
const Profile = () => {
  const { data, error } = useQuery({
    operationName: 'GetProfile',
  })

  return <div>{data?.getProfile.name}</div>
}

const FormComponent = () => {
  const queryClient = useQueryClient();

  const { data, error, mutate } = useMutation({
    operationName: 'UpdateProfile',
    onSuccess() {
      // invalidate the query
      queryClient.invalidateQueries(queryKey({ operationName: 'GetProfile' }));
    },
  })

  const onSubmit = (event) => {
    e.preventDefault();
    const data = new FormData(event.target);
    mutate(data)
  }

  return <form onSubmit={onSubmit}><input name="name" /><button type="submit">Save></button></form>
}
```

Now we could even make this fully optimistic by updating the `GetProfile` cache instead and then refetching it, it would look something like this:

```ts
const FormComponent = () => {
  const queryClient = useQueryClient();

  const { data, error, mutate } = useMutation({
    operationName: 'UpdateProfile',
    onMutate: async (data) => {
      const key = queryKey({ operationName: 'GetProfile' })
      // Cancel any outgoing refetches
      // (so they don't overwrite our optimistic update)
      await queryClient.cancelQueries(key)

      // Snapshot the previous value
      const previousProfile = queryClient.getQueryData<Profile>(key)

      // Optimistically update to the new value
      if (previousProfile) {
        queryClient.setQueryData<Profile>(key, {
          ...previousProfile,
          ...data
        })
      }

      return { previousProfile }
    },
    // If the mutation fails,
    // use the context returned from onMutate to roll back
    onError: (err, variables, context) => {
      if (context?.previousProfile) {
        queryClient.setQueryData<Profile>(queryKey({ operationName: 'GetProfile' }), context.previousProfile)
      }
    },
    // Always refetch after error or success:
    onSettled: () => {
      queryClient.invalidateQueries(queryKey({ operationName: 'GetProfile' }))
    },
  })

  const onSubmit = (event) => {
    e.preventDefault();
    const data = new FormData(event.target);
    mutate(data)
  }

  return <form onSubmit={onSubmit}><input name="name" /><button type="submit">Save></button></form>
}
```

Check out the reference and example app below to learn more about the new React Query integration.

## Resources

- [React Query reference](https://docs.wundergraph.com/docs/clients-reference/react-query)
- [Next.js + React Query Example](https://docs.wundergraph.com/docs/examples/nextjs-react-query)
- [TanStack React Query](https://tanstack.com/query/v4/docs/adapters/react-query)

## Summary

You can now easily integrate WunderGraph with React Query and start building end-to-end typesafe applications with WunderGraph. Thanks go out to Tanstack for making this great data fetching library.

We would love to know more about your experience with React Query and looking forward to seeying what you build with it.
Share it in the comments below or come join us on our [Discord server](https://wundergraph.com/discord).