React/Preact

This guide covers how to install and setup urql and the Client, as well as query and mutate data, with React and Preact. Since the urql and @urql/preact packages share most of their API and are used in the same way, when reading the documentation on React, all examples are essentially the same, except that we'd want to use the @urql/preact package instead of the urql package.

Getting started

Installation

Installing urql is as quick as you'd expect, and you won't need any other packages to get started with at first. We'll install the package with our package manager of choice.

yarn add urql graphql
# or
npm install --save urql graphql

To use urql with Preact, we have to install @urql/preact instead of urql and import from that package instead. Otherwise all examples for Preact will be the same.

Most libraries related to GraphQL also need the graphql package to be installed as a peer dependency, so that they can adapt to your specific versioning requirements. That's why we'll need to install graphql alongside urql.

Both the urql and graphql packages follow semantic versioning and all urql packages will define a range of compatible versions of graphql. Watch out for breaking changes in the future however, in which case your package manager may warn you about graphql being out of the defined peer dependency range.

Setting up the Client

The urql and @urql/preact packages export a method called createClient which we can use to create the GraphQL client. This central Client manages all of our GraphQL requests and results.

import { createClient } from 'urql';
const client = createClient({
url: 'http://localhost:3000/graphql',
});

At the bare minimum we'll need to pass an API's url when we create a Client to get started.

Another common option is fetchOptions. This option allows us to customize the options that will be passed to fetch when a request is sent to the given API url. We may pass in an options object, or a function returning an options object.

In the following example we'll add a token to each fetch request that our Client sends to our GraphQL API.

const client = createClient({
url: 'http://localhost:3000/graphql',
fetchOptions: () => {
const token = getToken();
return {
headers: { authorization: token ? `Bearer ${token}` : '' },
};
},
});

Providing the Client

To make use of the Client in React & Preact we will have to provide it via the Context API. This may be done with the help of the Provider export.

import { createClient, Provider } from 'urql';
const client = createClient({
url: 'http://localhost:3000/graphql',
});
const App = () => (
<Provider value={client}>
<YourRoutes />
</Provider>
);

Now every component and element inside and under the Provider can use GraphQL queries that will be sent to our API.

Queries

Both libraries offer a useQuery hook and a Query component. The latter accepts the same parameters, but we won't cover it in this guide. Look it up in the API docs if you prefer render-props components.

Run a first query

For the following examples, we'll imagine that we're querying data from a GraphQL API that contains todo items. Let's dive right into it!

import { useQuery } from 'urql';
const TodosQuery = `
query {
todos {
id
title
}
}
`;
const Todos = () => {
const [result, reexecuteQuery] = useQuery({
query: TodosQuery,
});
const { data, fetching, error } = result;
if (fetching) return <p>Loading...</p>;
if (error) return <p>Oh no... {error.message}</p>;
return (
<ul>
{data.todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};

Here we have implemented our first GraphQL query to fetch todos. We see that useQuery accepts options and returns a tuple. In this case we've set the query option to our GraphQL query. The tuple we then get in return is an array that contains a result object, and a re-execute function.

The result object contains several properties. The fetching field indicates whether the hook is loading data, data contains the actual data from the API's result, and error is set when either the request to the API has failed or when our API result contained some GraphQLErrors, which we'll get into later on the "Errors" page.

Variables

Typically we'll also need to pass variables to our queries, for instance, if we are dealing with pagination. For this purpose the useQuery hook also accepts a variables option, which we can use to supply variables to our query.

const TodosListQuery = `
query ($from: Int!, $limit: Int!) {
todos (from: $from, limit: $limit) {
id
title
}
}
`;
const Todos = ({ from, limit }) => {
const [result, reexecuteQuery] = useQuery({
query: TodosListQuery,
variables: { from, limit },
});
// ...
};

As when we're sending GraphQL queries manually using fetch, the variables will be attached to the POST request body that is sent to our GraphQL API.

Whenever the variables (or the query) option on the useQuery hook changes fetching will switch to true, and a new request will be sent to our API, unless a result has already been cached previously.

Pausing useQuery

In some cases we may want useQuery to execute a query when a pre-condition has been met, and not execute the query otherwise. For instance, we may be building a form and want a validation query to only take place when a field has been filled out.

Since hooks in React can't just be commented out, the useQuery hook accepts a pause option that temporarily freezes all changes and stops requests.

In the previous example we've defined a query with mandatory arguments. The $from and $limit variables have been defined to be non-nullable Int! values.

Let's pause the query we've just written to not execute when these variables are empty, to prevent null variables from being executed. We can do this by setting the pause option to true:

const Todos = ({ from, limit }) => {
const [result, reexecuteQuery] = useQuery({
query: TodosListQuery,
variables: { from, limit },
pause: !from || !limit,
});
// ...
};

Now whenever the mandatory $from or $limit variables aren't supplied the query won't be executed. This also means that result.data won't change, which means we'll still have access to our old data even though the variables may have changed.

Request Policies

As has become clear in the previous sections of this page, the useQuery hook accepts more options than just query and variables. Another option we should touch on is requestPolicy.

The requestPolicy option determines how results are retrieved from our Client's cache. By default, this is set to cache-first, which means that we prefer to get results from our cache, but are falling back to sending an API request.

Request policies aren't specific to urql's React API, but are a common feature in its core. You can learn more about how the cache behaves given the four different policies on the "Document Caching" page.

Reexecuting Queries

The useQuery hook updates and executes queries whenever its inputs, like the query or variables change, but in some cases we may find that we need to programmatically trigger a new query. This is the purpose of the reexecuteQuery function, which is the second item in the tuple that useQuery returns.

Triggering a query programmatically may be useful in a couple of cases. It can for instance be used to refresh the hook's data. In these cases we may also override the requestPolicy of our query just once and set it to network-only to skip the cache.

const Todos = ({ from, limit }) => {
const [result, reexecuteQuery] = useQuery({
query: TodosListQuery,
variables: { from, limit },
});
const refresh = () => {
// Refetch the query and skip the cache
reexecuteQuery({ requestPolicy: 'network-only' });
};
};

Calling refresh in the above example will execute the query again forcefully, and will skip the cache, since we're passing requestPolicy: 'network-only'.

Furthermore the reexecuteQuery function can also be used to programmatically start a query even when pause is set to true, which would usually stop all automatic queries.

There are some more tricks we can use with useQuery. Read more about its API in the API docs for it.

Mutations

Both libraries offer a useMutation hook and a Mutation component. The latter accepts the same parameters, but we won't cover it in this guide. Look it up in the API docs if you prefer render-props components.

Sending a mutation

Let's again pick up an example with an imaginary GraphQL API for todo items, and dive into an example! We'll set up a mutation that updates a todo item's title.

const UpdateTodo = `
mutation ($id: ID!, $title: String!) {
updateTodo (id: $id, title: $title) {
id
title
}
}
`;
const Todo = ({ id, title }) => {
const [updateTodoResult, updateTodo] = useMutation(UpdateTodo);
};

Similar to the useQuery output, useMutation returns a tuple. The first item in the tuple again contains fetching, error, and data — it's identical since this is a common pattern of how urql presents operation results.

Unlike the useQuery hook, the useMutation hook doesn't execute automatically. At this point in our example, no mutation will be performed. To execute our mutation we instead have to call the execute function — updateTodo in our example — which is the second item in the tuple.

Using the mutation result

When calling our updateTodo function we have two ways of getting to the result as it comes back from our API. We can either use the first value of the returned tuple, our updateTodoResult, or we can use the promise that updateTodo returns.

const Todo = ({ id, title }) => {
const [updateTodoResult, updateTodo] = useMutation(UpdateTodo);
const submit = newTitle => {
const variables = { id, title: newTitle || '' };
updateTodo(variables).then(result => {
// The result is almost identical to `updateTodoResult` with the exception
// of `result.fetching` not being set.
// It is an OperationResult.
});
};
};

The result is useful when your UI has to display progress on the mutation, and the returned promise is particularly useful when you're adding side effects that run after the mutation has completed.

Handling mutation errors

It's worth noting that the promise we receive when calling the execute function will never reject. Instead it will always return a promise that resolves to a result.

If you're checking for errors, you should use result.error instead, which will be set to a CombinedError when any kind of errors occurred while executing your mutation. Read more about errors on our "Errors" page.

const Todo = ({ id, title }) => {
const [updateTodoResult, updateTodo] = useMutation(UpdateTodo);
const submit = newTitle => {
const variables = { id, title: newTitle || '' };
updateTodo(variables).then(result => {
if (result.error) {
console.error('Oh no!', result.error);
}
});
};
};

There are some more tricks we can use with useMutation.
Read more about its API in the API docs for it.

Reading on

This concludes the introduction for using urql with React or Preact. The rest of the documentation is mostly framework-agnostic and will apply to either urql in general, or the @urql/core package, which is the same between all framework bindings. Hence, next we may want to learn more about one of the following to learn more about the internals: