PB ¿Hablas español?

You don't really need Apollo

Small project? Learning GraphQL? Apollo might be an overkill

Reading time:
Creation date:
Table of contents
  1. Road to front-end GraphQL
  2. What’s the alternative?
  3. Some nicer ergonomics
  4. Existing tooling
  5. Conclusion

The first time I tried GraphQL was when I would still have considered myself as just a back-end developer. About two years ago I gave myself the opportunity to learn it thanks to NestJS’s support for it and I totally felt in love with it. The fact that it’s basically self-documenting and the fact that you can ask for exactly the data you want with just one request made me feel that working with a GraphQL API as a front-end developer would feel really enjoyable.

Road to front-end GraphQL

I wanted to try to make something on the front-end side with this new knowledge, to get a better feel of it. By that time I was still learning the ropes on front-end with React (last time I did front-end, jQuery was the thing to use). So, naturally, I duckduckgo’ed graphql react and the first thing I found was Apollo. Apollo is a great tool that offers many features; you can even manage your whole application’s state with it. But it did feel a bit heavy for someone who’s just trying to learn how to use GraphQL, or for any small project in that sense. I will admit that it was really naive of me, but at the time I really thought: woah, so GraphQL is really only suitable for pretty big apps. Regardless, I kept on doing my experiments with Apollo. As I suspected I spent a lot of my time learning how to use Apollo, which is not bad per se, but of course it would feel daunting for anyone learning.

Sometime about last year I found urql which aims to be a lighter alternative to Apollo. I found this really appealing. And it was great. A simpler API and fewer features meant less time spent on the documentation and more time to actually build something with it. But it still felt pretty heavy for my use cases. Although right now I would probably choose urql over Apollo for a serious project, since I do feel Apollo tries to do too much for my taste.

Even though I haven’t worked professionally with GraphQL yet, I’ve kept on using it for my personal projects. Still, I kept on feeling that the entry point for any front-end developer learning it was quite high. If you duckduckgo (or google) react graphql your top results will be Apollo and howtographql.com. If you go to the latter, you’ll see that both Apollo and urql are listed as the beginner’s choice. This I feel is an artificially high entry point for a beginner.

What’s the alternative?

What are the minimum requirements for making a request to a GraphQL API from the browser? Well… just the fetch API. After all, you only need to make an HTTP POST request to the GraphQL endpoint. It only needs to contain a query/mutation in the body as a string and, optionally, the variables if they’re needed for the query. It doesn’t need to be a POST request, and it can have an application/graphql MIME type; but, to keep things simple, a POST request with an application/json MIME type should always work.

fetch(`${API}/graphql`, {
  method: 'post',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    query: `...`,
    variables: {
      ...
    },
  })
}).then(r => r.json())

You can turn this into a more reusable function with something like this:

async function gqlFetcher(query, variables) {
  const { data, errors } = await fetch(`${API}/graphql`, {
    method: 'post',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ query, variables }),
  }).then((r) => r.json());
  if (errors) throw errors;
  return data;
}

A GraphQL server returns a 200 response even if it contains errors, so you generally only need to check if the response contains an errors property. This is still an optimistic way of handling it, since you’re not accounting for other kinds of errors such as network errors that can return 4xx or 5xx responses. For the purposes of this post we’ll leave it like this.

Some nicer ergonomics

This approach does make you lose the nice interface that Apollo and urql provide you. You can, of course, create your own hooks that provide a friendlier interface; however, I prefer to use Vercel’s swr hook. This hook works for any kind of remote data fetching; it works by first returning data from the cache, then sending the fetch request, and finally returning the newly received data. It provides a nice interface for handling your data inside your component while keeping your UI, as they describe it, fast and reactive. The gqlFetcher function we made earlier is already compatible with the useSWR hook, so no additional work is required.

import useSWR from 'swr';

const gqlQuery = `...`;

function Component() {
  // gqlFetcher is the same function we defined earlier
  const { data, error } = useSWR(gqlQuery, gqlFetcher);

  if (error) return <div>{/*...*/}</div>; // JSX with error data
  if (!data) return <div>Loading...</div>; // Loading component
  return <div>{/*...*/}</div>; // JSX with returned data
}

In order to pass multiple arguments to the fetcher function, the swr hook allows you to pass an array as the first argument.

const gqlQuery = `...`;
const gqlVariables = {
  // ...
};

function Component() {
  const { data, error } = useSWR([gqlQuery, gqlVariables], gqlFetcher);
  // ...
}

useSWR uses the first argument as a key that serves to uniquely identify the request. If an array is passed, swr shallowly compares each element and, if any of them has changed, it revalidates the request.

Existing tooling

If you don’t feel like creating your own wrapper over fetch, you can use graphql-request. This is also a wrapper over the fetch API for making GraphQL requests that doesn’t need much work to start using it. It already handles errors nicely and is isomorphic by default (which some people might not like). The swr GitHub page already provides an example using this.

import { request } from 'graphql-request';

const API = 'https://api.graph.cool/simple/v1/movies';
const fetcher = (query) => request(API, query);

function App() {
  const { data, error } = useSWR(
    `{
      Movie(title: "Inception") {
        releaseDate
        actors {
          name
        }
      }
    }`,
    fetcher,
  );
  // ...
}

Conclusion

It feels there’s an artificially high entry level for front-end developers who want to get into the GraphQL world. The fact that Apollo and urql are shown as beginner choices for learning GraphQL can make developers feel like these kinds of tools are actually required to work with a GraphQL API. This is, in fact, not the case; you can build a fully capable web application with just the fetch API and some other small libraries for extra features. I can’t think of any small project that would actually require all the features that these big libraries have to offer. To clarify: I’m not saying you shouldn’t use these tools if you want to; I’m saying you don’t need to feel they’re required to build what you want.