Getting started with URQL, A Universal React Query Library
In this article we will use URQL to make Queries & Mutations from client
Installing urql is as quick as you'd expect. Firstly, install it with your package manager of choice:
yarn add urql # or npm install --save urql
Then, if you haven't already, make sure that all peer dependencies are installed as well:
yarn add react react-dom graphql # or
npm install --save react react-dom graphql
Note: Most libraries related to GraphQL specify graphql as their peer dependency so that they can adapt to your specific versioning requirements. The library is updated frequently and remains very backwards compatible, but make sure it will work with other GraphQL tooling you might have installed.
Like similar libraries that manage state and data, you will need to wrap your app with urql's <Provider>. This <Provider> holds the Client that is used to manage data, requests, the cache, and other things. It's the "heart" of urql and holds all of its core logic.
This example creates a Client, passes it a GraphQL API's URL, and provides it using the <Provider>.
Every component and query underneath the <Provider> in the tree now has access to the client and will call the client when it needs to execute GraphQL requests.
To illustrate how this works, the next example will use urql's <Query> component to fetch some GraphQL data.
When this component is mounted it will send the query and variables to your GraphQL API. Here we're using fetching to see whether the request is still being sent and is loading, error to see whether any errors have come back, and finally data to get the result.
Whenever the query or variables props change, the <Query> component will send a new request and go back into the fetching state.
The shape of the result include data and error which is rather similar to the response a GraphQL API sends back by default. However, the error is not the plural errors. urql wraps any network error or GraphQL errors in a CombinedError which is more convenient to handle and observe.
You're not limited to just passing in strings as queries. You can also pass in a fully parsed AST in the form of DocumentNode instead. For this purpose you can use graphql-tag.
This can be extremely helpful, since it enables syntax highlighting in some editors. It also can be used to preparse the GraphQL query using babel-plugin-graphql-tag or the included Webpack loader.
You only have to make a small adjustment. Install graphql-tag and you can immediately write tagged template literals instead:
Keep in mind that it makes sense to give your queries unique names. In this case we've chosen GetTodos, since we're simply listing out some Todos.
There always comes a point when an app will also need to send mutations to the GraphQL API. A mutation's response is very similar to a query's response, but often they're used in multiple use cases.
Sometimes you care about the response, sometimes you don't, sometimes it might make more sense to imperatively use the mutations' result.
To support all these use cases urql's <Mutation> component is quite flexible. The render prop API passes down an object that contains the executeMutation method that accepts variables as its first argument. When called it will return a Promise with the mutations result.
However, the render prop API will expose the result as well, like the <Query> component exposes it, with a fetching, data, and an error property.
Here's an example of an imperative use case where we create a todo.
In this example, when the button is clicked, the component will call the passed in executeMutation method, i.e. the addTodo prop. When an error occurs it changes it states to reflect that in the UI and it displays an error message.
While this is a common use case, urql offers an alternative approach to this, by using the result directly from the render props. So let's look at another example.
This example looks very similar, but as we can see there's sometimes no need to maintain state to handle a mutation's result, when it's not used as a fire-and-forget.
urql will by default come with a simple "document" cache. Each query with variables that is requested from a GraphQL API, the result will be cached completely. When the same query and variables are requested again, urql's default cache will then return the cached result. This result is also invalidated when a mutation with similar __typenames was sent.
Using urql's default behaviour this means we sometimes need a way to refetch data from the GraphQL API and skip the cache, if we need fresh data.
The easiest way to always display up-to-date data is to set the requestPolicy to 'cache-and-network'. Using this policy urql will first return a cached result if it has one, and subsequently it will send a new request to the API to get the up-to-date result.
A requestPolicy can be passed as a prop:
Including 'cache-and-network' there are four request policies in total:
cache-first: The default policy. It doesn't send a request to the API when a result can be retrieved from the cache.
cache-only: It never sends a request and always uses the cached or an empty result.
network-only: This skips the cache entirely and always sends a request.
cache-and-network: As stated above, this returns the cached result and then also sends a request to the API.
Next, we can take a look at how to use 'network-only' to force a refetch imperatively. In our previous example this would come in handy to refresh the list of todos.
As can be seen, the <Query> render props also expose an executeQuery method, which isn't unlike the <Mutation>component's executeMutation method.
We can call this method to rerun the query and pass it a requestPolicy different. In this case we'll pass 'network-only' which will skip the cache and make sure we actually refresh our todo list.