Early 2018 we released the first version of our minimalist GraphQL client urql. For the last year, we’ve been rethinking, rearchitecting, and rebuilding the core of the library, and a few months ago we silently launched urql v1.0.

Today, with the release of the new documentation site, we’re happy to call urql a stable, production-ready GraphQL client library for both small and large React applications.

New urql

New features in v1 include React Hooks for GraphQL queries, mutations and subscriptions, and a new powerful extensibility mechanism called Exchanges.

If you've used urql before, this might sound like we've rebuilt it from the ground up and are heading in a different direction. And indeed we are.

Origin story

The initial motivation for the project was to go back to basics. urql was created out of frustration with the maximalist approach of GraphQL client libraries like Apollo and Relay. These projects are incredibly powerful, but loaded with so many features that the sheer size of their APIs (not to mention their code bundles!) raise the barrier to entry to using GraphQL in your React application.

As the old saying goes, the simplest GraphQL client is just a fetch call. However, going down this path, you'll eventually have to reinvent functionality like caching and figure out how to integrate it to your UI library.

The initial version of urql attempted to hit the sweet spot between these two approaches. It made it easy to get started, and provided all the functionality we needed in simple GraphQL projects. But as we worked with various GraphQL applications, we began to understand how libraries like Apollo got where they did—every project has slightly different requirements, and supporting them all leads to inevitable complexity.

Instead of going down the same path, we wanted to keep the core of urql small and easy to work with, and provide an extensibility mechanism that allows app authors (that's you!) to scale and customize their GraphQL library to suit their needs.

Introducing Exchanges

Apollo has the concept of Links, which allow you to reach into your GraphQL requests, change how and when they're sent, and how eventual responses are treated.

With urql we wanted to go further and move all logic to a similar construct, called Exchanges. Essentially, Exchanges are an operation pipeline that allows you to customize and augment every aspect of the GraphQL client from how data is cached to how components receive their data.

Since we want urql to be a fully functional client when you first install and use it, it already ships with Exchanges that make up the small core of a basic GraphQL client. They are also excellent templates if you're writing new exchanges.

Some of the more interesting ones are:

  • dedupExchange for de-duplicating in-flight requests
  • cacheExchange that can short-circuit the network request and return a cached result, or pass the operation onwards and cache the ensuing response
  • fetchExchange to handle network requests
  • subscriptionExchange which can optionally be added to support GraphQL subscriptions

The great thing about urql Exchanges is that they are flexible, composable, and interchangeable. We've already had some great suggestions from our users, and we look forward to an ecosystem of Exchanges growing as the community begins to adopt the new version of urql. Exchanges are also fully tree-shakeable, so any built-in exchanges you don't use don't end up bloating your client bundle!

One of the trickiest things to do well in GraphQL clients is performant and consistent caching that supports every use case, and we're particularly excited about caching now being something we can customize!

Hooks & onwards

We also wanted to further simplify the usage of GraphQL in React applications, which is why we now provide useQuery, useMutation and useSubscription Hooks alongside the classic render prop pattern:

import { useQuery, useMutation } from 'urql';
import { getTodosQuery, addTodoMutation } from './queries';

const TodoForm = () => {
  const [getResponse] = useQuery({ query: getTodosQuery });
  const [addResponse, addTodo] = useMutation(addTodoMutation);

  if (getResponse.fetching) {
    return 'Loading...';
  } else if (getResponse.error || addResponse.error) {
    return 'Oh no!';
  }

  return (
    <>
      <ul>
        {getResponse.data.todos.map(({ id, text }) => (
          <li key={id}>{text}</li>
        ))}
      </ul>
      <button onClick={() => addTodo({ text: 'something!' })}>
        Add something!
      </button>
    </>
  );
};

This reduces the amount of code you need to write to wire up your GraphQL queries, and improves client rendering performance by avoiding additional component nesting.

Going forward, as the React community will move to data fetching with React Suspense, thanks to urql's lean core and flexibility, we are well-positioned to take advantage of Suspense and avoid handling loading and error states in every component!

Type Safety

GraphQL has been one of the most impactful improvements to how we write front-end applications since the introduction of React. One close competitor is the proliferation of type systems. That's why urql is written in TypeScript with thoughtful type coverage of the entire API.

Looking to the future, we're also very excited about ReasonML and BuckleScript as a more strongly typed alternative to TypeScript. That's why we've written first-class Reason bindings for urql.

Fun fact: the lightweight callbag-style Wonka streams that urql Exchanges use to communicate with each other are written in Reason and transpiled to JavaScript with TypeScript typings, providing a unified, type-safe async primitive for urql apps, whether they're written in JavaScript, TypeScript, or Reason!

Experiment!

Out of the box, urql is a lightweight, batteries-included GraphQL client. That's pretty cool, but we are even more excited about what you will be able to build with urql in the future.

We're already working on several exchanges to extend urql and provide more options for applications that will use it. This includes a normalized cache, which brings us another step closer to building a smarter GraphQL client.

GraphQL is still a new, but rapidly growing, technology, and we don't believe we've yet landed on the optimal approaches for every problem.

In the past year we've seen a lot of experimentation in the GraphQL Server ecosystem, but less so on the client-side. We hope that urql's extensibility will enable the community (yep, you!) to experiment with new approaches to GraphQL client-side challenges without having to reinvent the entire client stack.

Check out urql documentation, star the project on GitHub, and let us know what you think on Twitter!