DEV Community

Pablo Berganza
Pablo Berganza

Posted on • Originally published at pablo.berganza.dev

You don't really need Apollo

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())
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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)
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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
  )
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (11)

Collapse
jakesweb profile image
Jacob Colborn

My first lesson in GraphQL used Apollo on the client-side. It was probably too much to start with since I was also learning how to use React and how to build the GraphQL API, as well. Apollo just added this extra thing to learn. I never felt that I could use anything else, but just a simple fetch for json data seems like such a good use case to simply learning. Especially if you are just trying to learn how to set up the GraphQL APIs.

Collapse
pabloabc profile image
Pablo Berganza Author • Edited on

Exactly my story for a long time!
Using fetch should even be enough for a lot of more "serious" apps. And if you want caching, swr already gives that to you in a much lighter package.

Collapse
itsjzt profile image
Saurabh Sharma

If you need good caching but dont want to have apollo, then minimalist GraphQL client urql

Collapse
pabloabc profile image
Pablo Berganza Author

That's actually what I like to use if I feel the need for a more complete library! I've only had good experiences with it.

Collapse
viralsangani profile image
Viral Sangani

I feel apollo is overrated. Since I used REST in the past, I am more familiar with fetch and Axios. Now I have been using Graphql for few projects, and fetch works just fine. Though I haven't used swr in the past, now I will.

Collapse
pabloabc profile image
Pablo Berganza Author

Part of the reason of this post is that a lot of people coming from REST will find Apollo as their first choice if they look for GraphQL. I'm trying to say fetch (and of course Axios) is perfectly fine for this!

swr offers caching, which is one of the features people choose Apollo for. That's why I mentioned it 😁

Thanks for your comment!

Collapse
viralsangani profile image
Viral Sangani • Edited on

Hey, I was following your suggestion of useSWR in graphql, but I got stuck. I want to send a graphql request onPress event.
I tried the this,

const [shouldFetch, setShouldFetch] = React.useState(false)
Enter fullscreen mode Exit fullscreen mode
const { data } = useSWR(
    shouldFetch
        ? [query, { firstName, lastName, email, password1, password2 }]
        : null,
        gqlFetcher
    )
Enter fullscreen mode Exit fullscreen mode

Didn't work. How do I send Graphql request onPress() with useSWR??

Collapse
pabloabc profile image
Pablo Berganza Author • Edited on

Hey! I don't see anything particularly wrong on what you're showing me here. So whatever is causing you an issue should be somewhere else?

I just quickly made you a Codesandbox showing how to do this here.

Note that useMemo is necessary here since it prevents a new variables object from being created if name does not change. If it weren't there, every time the component re-rendered a new request to the GraphQL API would be made creating an infinite loop in some cases.

Collapse
sortbyrandom profile image
sort by random

Thanks for the tip to use useMemo. I was getting into an infinite loop as well, and had no idea what to do.

Collapse
viralsangani profile image
Viral Sangani

Thank you so much, It helped a lot. I was not using useMemo, and getting in the loop.

Thread Thread
pabloabc profile image
Pablo Berganza Author

I'm glad I could help!

Yeah that's a little detail to remember. Since useSWR shallowly compares each argument, and each newly created object's reference is different, you have to make sure to pass the same reference (not create a new object) if you don't want a re-fetch.