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 akey
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.
Top comments (11)
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.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!
If you need good caching but dont want to have apollo, then minimalist GraphQL client urql
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.
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.
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.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,
Didn't work. How do I send Graphql request onPress() with useSWR??
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 newvariables
object from being created ifname
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.Thanks for the tip to use
useMemo
. I was getting into an infinite loop as well, and had no idea what to do.Thank you so much, It helped a lot. I was not using useMemo, and getting in the loop.
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.