GraphQL and Urql by Example
Tags: graphql, urql, javascript, react
Published: 2019-08-27 07:49:26

According to the [docs](https://github.com/FormidableLabs/urql),

> urql is a GraphQL client that exposes a set of React components and hooks. It's built to be highly customisable and versatile so you can take it from getting started with your first GraphQL project all the way to building complex apps and experimenting with GraphQL clients.

[Urql](https://formidable.com/open-source/urql/), pronounced as `Urkel`, recently reached [v1.0](https://formidable.com/blog/2019/urql-2019/) a few months ago. Unlike [Apollo](https://www.apollographql.com/docs/react/), it is a minimalistic GraphQL Client. Urql was introduced as a response to Apollo's growing complexity of setup.

## Comparison between Apollo and Urql

A detailed comparison between Apollo and Urql can be found below (credits to [this original gist](https://gist.github.com/oddlyfunctional/74dc7e0d6df4e2e7c70fc6c4b5ed212e), edited for recent corrections):

|Features|Apollo Client|Urql|
| --- | --- | --- |
|Cache|Normalized objects|Hashing query + variables. Normalized Cache is [WIP](https://github.com/FormidableLabs/urql-exchange-graphcache)|
|Batching|With `apollo-link-batch-http` ([although it recommends deferring batching as long as possible](https://blog.apollographql.com/batching-client-graphql-queries-a685f5bcd41b))|Doesn't have a first-party solution but allows to use Apollo's Link extensions|
|Deduping|With `apollo-link-dedup` (enabled by default)|With `dedupExchange`|
|Authentication|Supports adding options to the `fetch` client or changing the network layer altogether|Supports adding options to the `fetch` client or changing the network layer altogether|
|Pagination|First-party support with [fetchMore](https://www.apollographql.com/docs/react/advanced/caching#fetchMore), also provides [several recipes](https://www.apollographql.com/docs/react/features/pagination)|No first-party support, needs to implement a custom solution|
|React Hooks|Officially supported as of [v3.0](https://blog.apollographql.com/apollo-client-now-with-react-hooks-676d116eeae2)|First-party support|
|Optimistic update|`mutate({ optimisticResponse })` ([requires manipulating the cache if inserting new data](https://www.apollographql.com/docs/react/features/optimistic-ui))|No support because of [document based cache](https://github.com/FormidableLabs/urql/issues/212#issuecomment-476232503)|
|Local state|[Support with `@client` directive](https://www.apollographql.com/docs/react/essentials/local-state)|[No official support](https://github.com/FormidableLabs/urql/issues/323)|
|Refetch after mutation|[`mutate({ refetchQueries })`](https://www.apollographql.com/docs/react/essentials/mutations)|Needs to manually call a function obtained when performing the query|
|Subscriptions|Supported|Supported|
|Community|Vibrant, easy to find answers online, [official chat](https://spectrum.chat/apollo), huge number of issues and PRs|Almost non-existent|
|Documentation|Very thorough, with several tutorials and recipes|Comprehensive|

## Setting up the GraphQL server

A [GraphQL server](https://github.com/deadcoder0904/graphql-and-urql-by-example/tree/master/server) has been made with Prisma 2 specifically for the purpose of this tutorial so make sure you clone it.

After cloning it, install the dependencies using `yarn`. This will also run the `postinstall` hook which runs `prisma2 generate` that generates the [photon](https://photonjs.prisma.io/) and [nexus-prisma types](https://github.com/prisma/nexus-prisma/) inside `node_modules/@generated` folder.

Go ahead and start the server using:

```bash
$ yarn start
```

Open up [http://localhost:4000](http://localhost:4000) to play around with the GraphQL API.

## Getting Started with Urql

Install `urql` with the package manager of your choice:

```js
$ yarn add urql
# or
$ npm install urql
```

`urql` has a `Provider` component similar to other libraries like `react-redux` which manages state and data. You need to wrap your app with the `Provider` component. This `<Provider>` component holds the `client` that is used to manage data, requests, the cache, and other things such that every component below it has an access to the client and it can query or mutate the data.

```js
import React from 'react';
import { Provider, createClient } from 'urql';

const client = createClient({
  url: "http://localhost:4000"
});

const App = () => (
	<Provider value={client}>
    {/* ... */}
	</Provider>
);

export default App;
```

## Querying data in Urql using Render Props or React Hooks

Let us query some GraphQL data using `urql`'s `Query` component.

```js
import React from 'react';
import { useQuery } from "urql";

const getPokemonData = `
	query GetPokemonData($name: String!) {
		pokemon(name: $name) {
			id
			number
			name
			attacks {
				special {
					id
					name
					damage
				}
			}
		}
	}
`;

export const ListPokemonDataQuery = ({ name = "Pikachu" }) => {
	const [{ fetching, data, error }] = useQuery({
		query: getPokemonData,
		variables: { name }
	});

	if (fetching) {
		return `Loading ${name}...`;
	} else if (error) {
		return `Oh no! Error: ${error}`;
	}

	const pokemon = data.pokemon[0];
	return (
		<>
			<h1>
				#{pokemon.number} {pokemon.name}
			</h1>
			<ul>
				{pokemon.attacks.special.map(({ name, id, damage }) => (
					<li key={name}>
						#{id} {name} - {damage}
					</li>
				))}
			</ul>
		</>
	);
};
```

The above `Query` component sends `getPokemonData` query with `name` as a variable to the GraphQL API mentioned in `url` property of `createClient`.

`Query` is a render prop which is nothing but a React component whose value is a function. This render prop gives us `fetching`, `data` and `error`. `fetching` returns a boolean whether the request is still being sent and is still loading. `data` gives us the data returned by the GraphQL API and `error` gives us whether we have any errors with the GraphQL API.

`urql` also has a first-class Hooks support so we can also use `useQuery` function.

If we rewrite the above example, it would look like:

```js
import React from "react";
import { useQuery } from "urql";

const getPokemonData = `
	query GetPokemonData($name: String!) {
		pokemon(name: $name) {
			id
			number
			name
			attacks {
				special {
					id
					name
					damage
				}
			}
		}
	}
`;

export const ListPokemonDataHook = ({ name = "Pikachu" }) => {
	const [{ fetching, data, error }] = useQuery({
		query: getPokemonData,
		variables: { name },
	})

	if (fetching) {
		return `Loading ${name}...`;
	} else if (error) {
		return `Oh no! Error: ${error}`;
	}

	const pokemon = data.pokemon[0];
	return (
		<>
			<h1>
				#{pokemon.number} {pokemon.name}
			</h1>
			<ul>
				{pokemon.attacks.special.map(({ name, id, damage }) => (
					<li key={name}>
						#{id} {name} - {damage}
					</li>
				))}
			</ul>
		</>
	);
}
```

Notice, how the `useQuery` hook simplifies the component structure. `useQuery` works like any other [React Hook](https://reactjs.org/docs/hooks-intro.html) as it takes in a value and returns a tuple. The value it takes in is a query and a variable name and it returns back a tuple containing `fetching`, `data` and `error`. Everything else is just the same.

## Mutating data in Urql using Render Props or React Hooks

Let us mutate some GraphQL data using `urql`'s `Mutation` component.

```js
import React, { useState } from 'react';
import { Mutation } from 'urql';

const addPokemon = `
  mutation AddPokemon($number: Int!, $name: String!) {
    addPokemon(data: {
      number: $number,
      name: $name
    }) {
      id
      number
      name
    }
  }
`

export const InsertPokemonMutation = () => {
  const [name, setName] = useState('')
  return (
    <Mutation query={addPokemon}>
      {({ fetching, data, error, executeMutation }) => {
        return (
          <>
            {error && <div>Error: {JSON.stringify(error)}</div>}
            <input value={name} onChange={e => setName(e.target.value)} />
            <button onClick={() => {
              if (name.trim() === "") return // return if input is empty
              executeMutation({ name, number: Math.ceil(Math.random() * 1000) })
              setName("") // clear the input
            }}>
              Add Pokemon
            </button>
            {data && (<div>
              <br/>
              Mutation successful: 
              <pre>{JSON.stringify(data, null, 2)}</pre>
            </div>)}
          </>
        )
      }}
    </Mutation>
  )
}
```

`Mutation` component takes in a query and returns `executeMutation`. `executeMutation` is a function which takes in a variable `name` and a random `number` as stated in our `addPokemon` query above and calls the `Mutation`. If the mutation is unsuccessful then an `error` is displayed. The render prop also gives you `fetching` and `data` if you want to do anything with it.

If we rewrite the above example using `useMutation` hook then it would look like:

```js
import React, { useState } from 'react';
import { useMutation } from 'urql';

const addPokemon = `
  mutation AddPokemon($number: Int!, $name: String!) {
    addPokemon(data: {
      number: $number,
      name: $name
    }) {
      id
      number
      name
    }
  }
`

export const InsertPokemonHook = () => {
  const [name, setName] = useState('')
  const [{ fetching, data, error }, executeMutation] = useMutation(addPokemon)
  return (
    <>
      {error && <div>Error: {JSON.stringify(error)}</div>}
      <input value={name} onChange={e => setName(e.target.value)} />
      <button onClick={() => {
        if (name.trim() === "") return
        executeMutation({ name, number: Math.ceil(Math.random() * 1000) })
        setName("")
      }}>
        Add Pokemon
      </button>
      {data && (<div>
        <br/>
        Mutation successful: 
        <pre>{JSON.stringify(data, null, 2)}</pre>
      </div>)}
    </>
  )
}
```

`useMutation` takes in the mutation `addPokemon` and returns the mutations state (`fetching`, `data` and `error`) and `executeMutation` function in a tuple. `executeMutation` is then called on a click of the button.

## What are Exchanges in Urql

`urql` has a concept of `exchanges`. When a `new Client()` is created you pass it a `url` and `fetchOptions`. But you can also pass it an `exchanges` array. Exchanges are operation handlers. It receives `client` and `forward` function as an object and returns a function accepting a stream of operations and returning a stream of operation results (i.e. GraphQL results).

In other words, exchanges are handlers that fulfill our GraphQL requests. They're Input/Output streams, inputs being operations, outputs being results.

By default, `urql` creates 3 different exchanges namely `dedupExchange`, `cacheExchange` and `fetchExchange`.

`dedupExchange` deduplicates pending operations. It eliminates duplicate operations waiting for a response as it wouldn't make sense to send the same operation twice at the same time.

`cacheExchange` checks operations against the cache. Depending on the `requestPolicy` cached results can be resolved instead and results from network requests are cached.

`fetchExchange` sends an operation to the API and returns results.

When a `new Client()` is created and no exchanges are passed to it then some are added automatically, which is the same as creating a `new Client()` using the following exchanges:

```js
import { Client, dedupExchange, cacheExchange, fetchExchange } from "urql";

const client = new Client({
  url: "http://localhost:4000",
  exchanges: [dedupExchange, cacheExchange, fetchExchange]
});
```

This can also be written as:

```js
import { Client, defaultExchanges } from "urql";

const client = new Client({
  url: "http://localhost:4000",
  exchanges: defaultExchanges
});
```

Now that we know what are exchanges, lets learn about subscriptions.

## Subscribing to data in Urql using Render Props or React Hooks

Go ahead and first install `subscriptions-transport-ws` using `yarn`:

```bash
$ yarn add subscriptions-transport-ws
```

To use subscriptions, we need to first add `subscriptionExchange` to our `new Client()` and also create a `new SubscriptionClient()` using websocket protocol as follows:

```js
import { SubscriptionClient } from "subscriptions-transport-ws";
import { Client, defaultExchanges, subscriptionExchange } from "urql";

const subscriptionClient = new SubscriptionClient(
  "ws://localhost:4001/graphql",
  {
    reconnect: true,
    timeout: 20000
  }
);

const client = new Client({
  url: "http://localhost:4000",
  exchanges: [
    ...defaultExchanges,
    subscriptionExchange({
      forwardSubscription: operation => subscriptionClient.request(operation)
    })
  ]
});
```

Now we can start using `Subscription` component in our App:

```js
import React from 'react'
import { Subscription } from 'urql'

const newPokemon = `
  subscription PokemonSub {
    newPokemon {
      id
      number
      name
      attacks {
        special {
          name
          type
          damage
        }
      }
    }
  }
`

const NewPokemon = () => (
  <Subscription query={newPokemon}>
    {({ fetching, data, error }) => {
      if (fetching) {
        return `Loading...`
      } else if (error) {
        return `Oh no! Error: ${error}`
      }

      const { newPokemon } = data
      return (
        <>
          <h1>
            #{newPokemon.number} {newPokemon.name}
          </h1>
          <ul>
            {newPokemon.attacks.special.map(({ name, type, damage }) => (
              <li key={name}>
                {name} ({type}) - {damage}
              </li>
            ))}
          </ul>
        </>
      )
    }}
  </Subscription>
)
```

`Subscription` component works in similar way to the `Query` component. It can take in a `query` and a `variables` prop. It also has `fetching`, `data` and `error` just like a `Query` component. The `data` and `error` of the render props will change every time a new event is received by the server.

We can also use `useSubscription` hook as follows:

```js
import React from 'react';
import { useSubscription } from 'urql';

const newPokemon = `
  subscription PokemonSub {
    newPokemon {
      id
			number
			name
			attacks {
				special {
					name
					type
					damage
				}
			}
    }
  }
`

export const NewPokemonSubscriptionHook = () => {
  const [{ fetching, data, error }] = useSubscription({ query: newPokemon }, (pokemons = [], res) => {
		return [res.newPokemon, ...pokemons] 
	})

  if (fetching) {
    return `Loading...`
  } else if (error) {
    return `Oh no! Error: ${error}`
  }
	return (
		<>
			{data.map(pokemon => {
			  const { newPokemon } = pokemon
				return (
					<div key={newPokemon.number}>
						<h1>
							#{newPokemon.number} {newPokemon.name}
						</h1>
						<ul>
							{newPokemon.attacks.special.map(({ name, type, damage }) => (
								<li key={name}>
									{name} ({type}) - {damage}
								</li>
							))}
						</ul>
					</div>
				)
			})}
		</>
	)
}
```

`useSubscription` takes in the subscription `newPokemon` and returns the subscriptions state (`fetching`, `data` and `error`). Additionally, the second argument for `useSubscription` can be an optional reducer function which works like [Array.prototype.reduce](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). It receives the previous set of data that this function has returned or `undefined`. As the second argument, it receives the event that has come in from the subscription. You can use this to accumulate the data over time, which is useful for a list for example.

# Conclusion

In this tutorial, we learnt about URQL (Universal React Query Library) which is a blazing-fast GraphQL client, exposed as a set of ReactJS components. We then laid out the differences between Apollo and Urql.

We learnt about the `Query` API, `Mutation` API and `Subscription` API provided by Urql. We also used the hooks `useQuery`, `useMutation` and `useSubscription` to reduce the callback hell boilerplate unnecessarily created by Render Props.

We also learnt about Exchanges. Finally, we created a simple Pokemon application using Urql. Urql is a new piece of technology but it is mature enough to be used in production. Although, some things like Optimistic Updates do not yet work due to lack of cache normalization but its a [work-in-progress](https://github.com/FormidableLabs/urql-exchange-graphcache) and soon will be released.