How to urql, basics
Tags: react, graphql, javascript, urql
Published: 2020-03-03 15:43:41

When picking a GraphQL client for React, many default to using [Apollo](https://www.apollographql.com/docs/react/) or [Relay](https://relay.dev/docs/en/introduction-to-relay), but now there’s a new kid on the block rising in popularity over the last year: Its name is `urql`. It's not as packed with features as other GraphQL clients. Instead, `urql` aims to be minimal and highly customizable. This blog post series will start by walking you through getting started with `urql`, and then move on to more advanced topics like subscriptions, normalised caching, etc.

## Concepts

This blog series assumes a basic understanding of GraphQL. The following basic `urql` concepts will also be referenced throughout the series.

### Operations

In `urql` all operations are controlled by a central client. This client is responsible for managing GraphQL operations and sending requests. This includes things like queries, mutations, and subscriptions.

A typical `Request Operation` looks like this:

```js
{
  key: 'hash',
  operationName: 'query | mutation | subscription | teardown',
  variables: {},
  context: {
		fetchOptions: 'function | object',
		requestPolicy: 'cache-first | cache-only | network-only | cache-and-network',
		url: 'string'
  }
}
```

*The most important properties are listed in the above example; more properties can be found [here](https://formidable.com/open-source/urql/docs/api/#operation-type).*

The above `key` property is a hash of the `querystring` + `variables` used for this operation. This `key` uniquely identifies every operation, so, if we have two components dispatching the same query with the same variables, we can purposely ignore one of them to avoid duplicate requests.

With the `requestPolicy` we can dictate whether or not we want to use our cache, and whether or not we want to fetch even if there is a cache-hit.`fetchOptions` allows us to dictate what headers and other options to use with the `fetch` action.

When an Operation comes back as a `cache-hit` or as a fetched result we start calling it an `OperationResult.` This will typically look like this:

```js
{
  operation, // the operationRequest mentioned earlier
  errors, // our possible server response errors
  data, // the data received
  extensions // possible extensions attached to the response by your backend
} 
```

An `OperationResult` will then be handled by exchanges before reaching the client. 

### Exchanges

Exchanges are  middleware-like extensions that handle how operations flow through the client and how they're fulfilled. Multiple exchanges may handle each operation.

You can pass in these exchanges to the client like this:

```js
createClient({ exchanges: [exchange1, exchange2, ...] });
```

The exchanges will be executed in the order provided to the client. This means that when an operation comes in, `exchange1` will be called. When `exchange1` is done, the operation gets forwarded to `exchange2` and so on. When the last exchange completes, we get an `OperationResult`. This `OperationResult` is then sent back through the chain of exchanges in the reverse direction, and finally reaches the client.

*More info around exchanges can be found [here](https://formidable.com/open-source/urql/docs/basics/).*

### __Typename

Every type we make in our `graphql-server` will have a name and send it back when we query the `__typename` field. For example, the entity below will implicitly have an additional `__typename: 'Todo'` field.

```js
type Todo {
  id: ID!
  text: String
  completed: Boolean
}
```

The `__typename` field is useful for identifying the queries affected by a certain `mutation`. When a `mutation` receives a response with a `__typename` we're currently watching with a query, then we can assume this watched query should be invalidated.

## Getting started

If you want to follow along you can use this [template](https://codesandbox.io/s/ecstatic-wozniak-bwgqk).

For this walkthrough we'll be using React.js but note that urql [can be used outside of React](https://formidable.com/open-source/urql/docs/urql-outside-react/).

Starting out with `urql` is pretty convenient. First, we create our client. This client will process the operations and their results.

```js
// App.js
import { createClient } from 'urql';

const client = createClient({
  // This url can be used in your sandbox as well.
  url: 'https://0ufyz.sse.codesandbox.io',
});
```

The client has more options, but the url is the only mandatory one. A few exchanges are included by default:

*Find more client-options [here](https://formidable.com/open-source/urql/docs/api/#client-class).*

Next, set up a `Provider` to allow our React-tree to access the client.

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

const client = createClient(...);

export const App = () => (
  <Provider value={client}><Todos /></Provider>
);
```

At this point, our client is set up to handle incoming results, and our `App` has access to this client and can dispatch operations. The only thing we are still missing is actually dispatching operations, so let's make our first query:

```js
import { useQuery } from 'urql';

const TodosQuery = `
  query {
    todos {
      id
      text
      complete
    }
  }
`;

export const Todos = () => {
  const [result] = useQuery({ query: TodosQuery });

  if (result.fetching) return <p>Loading...</p>;
  if (result.error) return <p>Oh no... {result.error.message}</p>;

  return (
    <ul>
      {result.data.todos.map(({ id, text, complete }) => (
        <Todo
          key={id}
          text={text}
          id={id}
          complete={complete}
          disabled={result.fetching}
        />)
      )}
    </ul>
  );
}
```

In the example above, if todo results are present in the cache they will be returned synchronously (no `result.fetching`) and if they're not they will be fetched.

*More options for the useQuery hook can be found [here](https://formidable.com/open-source/urql/docs/api/#usequery-hook).*

You might worry that this architecture would result in unnecessary fetching, but the first default exchange included in your `urql-client` is the `dedupExchange`. Do you recall us talking about a unique key on each operation? We use that key to determine in that `dedupExchange` whether or not we already have an operation in progress for a given piece of data. When queries and variables are identical, a new fetch is not performed.

We are still missing one crucial part: we want to be able to mark a todo as completed. Let's refactor our application to allow each `Todo` item to toggle and persist its completed state. 

```js
import { useMutation } from 'urql';

const ToggleTodoMutation = `
  mutation($id: ID!) {
    toggleTodo(id: $id) {
      id
    }
  }
`;

export const Todo = ({ id, text, complete, disabled }) => {
  const [result, toggleTodo] = useMutation(ToggleTodoMutation);
  
  if (result.error) return <p>Something went wrong while toggling</p>;

  return (
    <li>
      <p onClick={() => toggleTodo({ id })}>
        {text}
      </p>
      <p>{complete ? 'Completed' : 'Todo'}</p>
      <button
        onClick={() => toggleTodo({ id })}
        disabled={disabled || result.fetching}
        type="button"
      > {complete ? 'Toggle todo' : 'Complete todo'}</button>
    </li>
  );
}
```

Notice the `disabled={result.fetching}` on our `Todo` component. Our example uses a document-based cache, so when we do a mutation on a certain `__typename`, queries associated with this type will be refetched. In our case, toggling the completed state of our `Todo` type will cause our `todos` query will be refetched, so we prevent additional toggles while the result is fetching.

Try opening the network-tab of your browser when this `mutation` completes. You'll see a query being triggered to refetch our `todos`. This is because our `cacheExchange` sees a mutation response with the typename "Todo"; it knows that we are currently watching an array of this type and invalidates it, triggering the refetch.

If you'd like to dig into exactly how caching and the `dedupExchange` is working, you can delay the mounting of this second component until the first has fetched. You will see the data for the query return synchronously, thanks to our `cacheExchange`. The default cache will save responses by their operation key.

You can also try altering the default caching behavior by changing the `requestPolicy` from the default `cache-first` to `cache-and-network`. This will force the query to refetch in the background.

*More options for the useMutation hook can be found [here](https://formidable.com/open-source/urql/docs/api/#usemutation-hook).*

## Conclusion

This was an introduction to `urql`, the new kid on the block for GraphQL clients. In the future we'll cover how to setup subscriptions, server-side rendering, and more.

We hope you learned something and are as excited as we are about this new library!

This is a cross-post from [Formidable](https://formidable.com/blog/2020/urql-1/)