Skip to content

Instantly share code, notes, and snippets.

@garethpbk
Created November 20, 2018 18:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garethpbk/5ac4deb2aca875b22b1422abad19fbf8 to your computer and use it in GitHub Desktop.
Save garethpbk/5ac4deb2aca875b22b1422abad19fbf8 to your computer and use it in GitHub Desktop.

REST Easy with Apollo Client

GraphQL is taking the web development world by storm, and along with it a whole new set of tools, practices, and packages to learn. During my experience learning GraphQL I've encountered several hurdles and questions, and from talking to others I gather that these are common for developers trying to learn the technology:

  1. Where the heck do I start? Schemas, queries, resolvers, mutations, Apollo, Relay...huh?
  2. I work on the front-end and have little or no control over my backend. Can I even use GraphQL?
  3. I've heard about this GraphQL thing and want to try it out. What's an easy way to do give GraphQL a shot?
  4. Rewriting our entire backend in GraphQL is unfeasible - I've got code to ship and deadlines to meet. Can I use GraphQL without committing to massive changes?

Apollo Client, a toolset by the Meteor Development Group that manages your application's data via the power of GraphQL, is a popular way to integrate GraphQL into front-end applications. One of the Apollo platform's biggest strengths is its broad set of links that extends the Client's capability - kind of like middleware for GraphQL applications. Links can be chained together to shape your data exactly to your specifications, and offer powerful tools for common needs like error handling and state management.

One of Apollo Client's links offers solutions to all four of the questions above: apollo-link-rest, which allows existing REST API endpoints to be communicated with via GraphQL. This means you can start using GraphQL on the front-end, through Apollo Client, without any need for a GraphQL server on the back-end or any need to modify the back-end at all. It's a great way to try GraphQL without getting overwhelmed and for front-end developers who want to see what all the fuss is about. Whether or not it's suitable for full-scale production applications will depend on the team and application - if you want to have a fully GraphQL-powered application, it's generally recommended to leverage GraphQL on both ends; in this situation, apollo-link-rest offers a useful intermediary that can help the transition from a traditional REST setup to a GraphQL one (e.g. a front-end team doesn't have to wait for a GraphQL back-end to be functional).

Without further adue, let's take a dive into Apollo Client and see how apollo-link-rest can be used to manage data from a REST API in a React application. Our API will come from JSONPlaceholder, which offers a mock REST API with everything you'd expect for a CRUD app, minus persistent storage. We'll use create-react-app to get our project up-and-running, and explore how Apollo Client works with React, as well as noting some issues with apollo-link-rest - as the project maintainers note, it's a library that's under active development.

The finished repo for this project can be found at https://github.com/garethpbk/apollo-rest-example.

A live deployment is available at https://apollo-link-rest-jsonplaceholder.netlify.com/. Note that the Apollo dev tools are disabled by default in production.

Creating The Project and Adding Dependencies

The first step is to create a new React App - commands here will use yarn:

yarn create react-app apollo-rest-example

Next, there's a few dependencies to add:

  1. apollo-client

    *the head honcho, the actual GraphQL client

  2. apollo-cache-inmemory

    *the recommended data store for Apollo Client 2.x

  3. apollo-link

    *allows usage of all the cool links to extend the client

  4. apollo-link-rest

    *the specific rest link

  5. graphql

    *the JavaScript implementation of the GraphQL specification

  6. graphql-anywhere

    *allows GraphQL queries to be run anywhere, without a server or schema

  7. graphql.macro

    *for importing .graphql files into .js files

  8. react-apollo

    *the React implementation of Apollo Client

  9. @reach/router

    *for client-side routing, of course!

Let's add them all:

yarn add apollo-client apollo-cache-inmemory apollo-link apollo-link-rest graphql graphql-anywhere graphql.macro react-apollo @reach/router

Sidenote: graphql-tag can be used in place of graphql.macro if you'd rather co-locate your queries and mutations directly in component files; I'll give examples of both. @reach/router is a personal preference; it doesn't matter what router is used so long as it can pass URL parameters as props.

Cool! We've got our project set up and dependencies installed.

Project Structure and Instantiating Apollo Client

It's time to set up Apollo Client and add a REST endpoint, but first we want to clean up and organize our project structure:

  1. Delete serviceWorker.js and in index.js remove the serviceWorker import and serviceWorker.unregister(); - we don't need offline support for this example project.
  2. In the src directory, create two new directories: components and graphql - if you're using graphql-tag, only create a components directory.
  3. Move App.js and App.css into components; delete App.test.js and logo.svg. Update the import path of the App component in index.js accordingly.
  4. In App.js, remove the logo import. Delete unneeded JSX in the return method, so that you've got something like this:

https://gist.github.com/c97fac6d087e60dec3e351bcfad77898

Our project structure should now look like this:

https://gist.github.com/6a6e5fa486b9684d18db210926fb2faa

Go ahead and run yarn start to make sure the development server starts. At this point it's a good idea to copy styling from the finished project into index.css and App.css - as this isn't a CSS tutorial we won't be going over that in detail.

index.css

app.css

Great! Let's move on to setting up Apollo Client. Open App.js and add the imports needed to get an instance of Apollo Client running:

https://gist.github.com/30e40c306ce9d26b38f4c98f1f371eba

We now need to create two pieces: 1) a RestLink pointing to the REST endpoint and 2) an ApolloClient to wrap our application. Use the imports from their respective packages:

https://gist.github.com/c05b9714eb91fae59f9714554a51993e

This link is passed into an instance of ApolloClient, along with a cache from apollo-cache-inmemory:

https://gist.github.com/2265f40021b58660ad35a44ba56bdb82

We'll return to dataIdFromObject later - it allows customization of how objects in the cache are accessed, which will come in handy when we're looking at single entries queried from the cache.

The last part of setting up Apollo is to wrap our entire App component with the <ApolloProvider> component from react-apollo. It takes one prop, client, to which we pass the ApolloClient we created. The returned JSX of App.js now looks like:

https://gist.github.com/714da9c763716e371a3043090e195f2a

The <ApolloProvider> component is a lot like React's context provider component; anything that is a child of <ApolloProvider> lives within the client's context and can access the cool stuff provided by Apollo Client.

We're now ready to start building queries and getting data from the REST API into our application. This is a good time to install the Apollo Client Chrome Devtools if you haven't - a very useful Chrome extension for debugging and viewing the cache. With the application running, open up the inspector and look for the Apollo tab - if you see it, then your application is successfully running Apollo Client.

Writing Queries & Using the Component

GraphQL's method of fetching data is based around queries, which are kind of like GET requests in a traditional REST API. Unlike GET requests, queries follow a rigid, declarative structure that tell GraphQL exactly what data needs to be returned. This fits neatly with GraphQL's strong type system - we won't get into it in detail here, but a GraphQL schema strictly defines what data types are available, and every associated query is a reflection of that schema (you may be wondering how a non-typed REST endpoint can be used with strongly-styped GraphQL - we'll find out soon).

Let's create our first query, which will be used to get data for all users from the JSONPlaceholder API. In the graphql directory create a file called GET_ALL_USERS_QUERY.graphql - if you're using graphql-tag instead of graphql.macro, open up App.js instead and bear with me.

Sidenote: if you're using VSCode, the GraphQL for VSCode extension is an essential quality-of-life addon.

GraphQL queries look kind of like JSON - curly brackets ahoy. In GET_ALL_USERS_QUERY.graphql, write the following, and then we'll dig into it:

https://gist.github.com/67dc4c4be6ea44d1f5c5786b3c4e0671

Congrats, you've written a GraphQL query! Take a look at the REST endpoint at https://jsonplaceholder.typicode.com/users - the JSON of each entry here looks very similar to this query. Notice how the query only includes the first three fields - in a normal REST response the application recieves the entire chunk of JSON; with GraphQL it can request only the fields it cares about. Later on we'll add the rest of the fields to the query, but for now the first three will suffice. Let's dissect this query:

  1. query declares that this is a query - it could also be a mutation or a subscription (we'll talk about mutations later, but this tutorial won't cover subscriptions).
  2. GET_ALL_USERS_QUERY is the name of the query, used when we want to reference it elsewhere. You can call this whatever you want; I like to name my queries as explicitly as possible, including adding QUERY at the end.
  3. users specifies the type of the data being returned (sort of - it refers specifically to a field called "users" on the root query, but for now just know that we'll get our data back in an object called "users").
  4. id, name, email are the fields the application wants values returned for; they will match fields in a GraphQL schema, or REST API in this project.

If we were requesting data from a GraphQL server, this query would be all we need. However, since this is a REST endpoint, we need to add a directive that clarifies our need for apollo-link-rest. Let's add the @rest directive to our query:

https://gist.github.com/6106205a97f498213cb561292dcc29ca

SideNote: directives can be thought of as modifiers that extend what queries, mutations, and subscriptions are capable of. They're sort of controversial in GraphQL land. If you're interested in learning more, check out The power of GraphQL directives - note that the @rest directive in that article is distinct from the @rest directive we're using here from apollo-link-rest.

This is the magic of apollo-link-rest that allows a GraphQL query to mesh with an untyped REST API. The three parts of the directive:

  1. @rest informs the query that it should run through apollo-link-rest.
  2. type: "User" is a parameter that provides the query with a mock type to play nicely with GraphQL's strong typing - everything in a GraphQL schema is strongly typed, and queries and mutations need types to know what to look for, so this allows the query to pretend the REST API is also typed. Note that, like the "users" specification, you can call this type whatever you want - if folks @rest(type: "Person", path: "/users") suits your fancy, go for it. The REST API doesn't know any better.
  3. path: "/users" tells request where to look on the endpoint. As the client's base uri is https://jsonplaceholder.typicode.com, this tells the query to go to https://jsonplaceholder.typicode.com/uesrs to fetch data.

Sidenote: think for a minute about how you'd do this with a traditional fetch call - you'd hit the endpoint and get the entire JSON response back, then filter on the client-side to get just the three fields you care about and store them in state. Something like this:

https://gist.github.com/18dcb538ac0f61b4dec481e0297ab41f

Apollo saves us the trouble of all this (more on the topic of client-side filtering at the end of the article). This code is included in the example repo code for demonstrative purposes.

Cool. Next let's actually run this query in React and get the data into the application. Create a new file in components called AllUsers.js and import like so:

https://gist.github.com/9f06d69ab35ab076c7a1b5a108b61c56

Apollo Client 2.x uses the render prop pattern and has dedicated components for detailing with queries and mutations, creatively named <Query> and <Mutation>. We'll use graphql.macro to import the all users query into the component file:

const GET_ALL_USERS_QUERY = loader('../graphql/GET_ALL_USERS_QUERY.graphql');

Set up a functional component to render an unordered list that will accept data from the query - if you're familiar with render props this will feel cozy, if not it might seem a little weird. The <Query> component takes in a parameter called query, into which the all users query is plugged. Destructuring is then used to pull out three properties that reflect the state of the data being queried: data, error, and loading.

This is one of the coolest parts of Apollo Client - it provides an incredibly easy way to inform your UI what your data is doing with just a little bit of JavaScript. Here's the rest of the <AllUsers> component:

https://gist.github.com/9519dd61f9fa3a928424ff48f6e18cdf

We conditionally return different JSX depending on what state the data is in. You could return a spinner component during the loading phase, and a big red flashing light if there's an error. Use your imagination, go wild. Apollo makes it easy.

If the data loads successfully and there are no errors, the data object is made available. Notice its users property, which reflects what we called the type of data being returned in the query (if you called it folks, this would be data.folks). A <Link> component from @reach/router is set up here, as these entries will link to their respective individual user view later on.

If you're using graphql-tag instead of graphql.macro - you'll want to import gql from 'graphql-tag' and instead of loading the query from a separate file, declare right it in AllUsers.js like so (I like to put it at the bottom, below the export):

https://gist.github.com/64a3ba77e73b06bbd4a8c23857c4591b

Last we must import AllUsers.js into App.js, adding it as a route so that we're prepared to next render individual user components. Add import { Router, Link } from '@reach/router' and import AllUsers from './AllUsers to App.js, then create a <Router> component wrapping the <AllUsers> component with a path of "/". Link the header to the homepage for convenience. Add in a div with a class of App-container for layout. The component JSX in App.js now looks like:

https://gist.github.com/0612764f779fc0481eb927dd86b7d610

Sidenote: if you've never used Reach Router before, I highly recommend exploring it further at https://reach.tech/router.

With a query successfully retrieving all users and rendering to the page, the application should now look like something like this:

all-users-query

Now is a great time to pop open the Apollo dev tools and explore how queried data is cached. Check out how users is attached to the root query, and how each returned user object gets its own entry in the cache - this will be important for the next step. Notice how each user entry is referenced by its id number; this is a result of dataIdFromObject that was set earlier in the cache (try removing that and seeing how it changes the cache).

apollo-devtools-all-users

You might also notice that the built-in GraphiQL interface shows a "forward is not a function" error when the dev tools are opened up. This is a bug with apollo-link-rest that's been around for a while; it doesn't really affect anything for our purposes, but it does have some implications for trying to run queries not using the @rest directive. There's an open issue about it and it may have resolution in the future. Take some time to play around with GraphiQL if you haven't seen it before, it's another great example of GraphQL tooling.

Awesome. Let's move on to our second GraphQL piece, with which we'll retrieve data for and render individual users. It'll be a little different this time - instead of the <Query> component, we'll utilize a GraphQL fragment to get data directly from the Apollo cache using an <ApolloConsumer> component.

Nested Queries & Using GraphQL Fragments with

A fragment is set of fields that can be used to pull out part of the data from something returned by a query, and to share fields between different queries/mutations/subscriptions. The difference between queries and fragments is kind of confusing and it's not always clear where a query should be used versus a fragment. In Apollo, fragments are very useful because they can be used to target bits of data that were not directly queried, but rather stored in the cache as a result of another query.

In our project, this means we can use fragments to get data from each individual user entry, even though those individual entries were never themselves queried. We run one query - to get all the fields we want for all users - and then use a fragment to access just the field data from one user at a time. Before we can do that the all users query must be expanded to include all of the fields.

If you looked at the JSONPlaceholder /users endpoint earlier, you'll have seen that there are more fields than the three we've queried so far. Let's add the rest of those fields, and learn how apollo-link-rest deals with nested JSON data in the process. Add the following to GET_ALL_USERS_QUERY.graphql (or in the const if you're using graphql-tag):

https://gist.github.com/53786487e1199efacb1a564c14252ded

Boom, parity with the shape of the REST API's user data. Notice the new @type directive that appears in front of nested data - if you guessed that this is used to mock a type for these data, good job! This helps reinforce that GraphQL schemas are strongly typed - if we were using a schema instead of a REST API, each kind of nested data would have its own type in the schema.

There's a second way to provide types using a typePatcher to the RestLink object; I find the directive much easier but if you're dealing with deeply nested JSON a typePatcher might be the way to go - the official documentation has more information if you want to explore further.

Check out the Apollo cache and see how each user entry now has all of its data from the API. We're in position to start using fragments to render each user individually, along with all its data.

First let's add a route with a parameter to accomodate individual user views:

https://gist.github.com/7877eafa16d23321aebe85caa01ea4ee

Create SingleUser.js in components and import it into App.js, then in the graphql directory create GET_SINGLE_USER_FRAGMENT.graphql - we'll write the fragment first:

https://gist.github.com/ca89cf93044934907d8f827f9fa93cc7

Note that Apollo's documentation uses Pascal Case for fragments and uppercase Snake Case for queries and mutations, so we'll stick with that here. I use Snake Case for all const and filenames, though. Find what works for you.

Looks pretty similar to the full query, minus a @rest directive - the fragment hits the cache instead of the REST API, so no need for the directive. It's pretty self-explanatory - we want to get a fragment, called SingleUser, on an object of type User. This fragment happens to contain all of the fields that the User object does, but it can be changed to retrieve only a subset of fields as desired.

Sidenote: if you've read the Apollo docs you might have seen this regarding queries: First, we try to load the query result from the Apollo cache. If it’s not in there, we send the request to the server. So why use a fragment instead of querying an individual user, e.g. from https://jsonplaceholder.typicode.com/users/1? Doing this would mean we wouldn't have to query all the fields for all the users on intial load, right? Totally right; there's three reasons for the purposes of this article why we want to use fragments: 1) fragments are useful and it's good to learn them; 2) when we create a new user with a mutation, it won't be accessible from the remote API (no persistent storage, recall), just the cache; 3) apollo-link-rest has some issues with queries not using the @rest directive and that pesky "forward is not a function" error is liable to pop up. In a production application with a real API you probably won't want to front-load all of your data, and therefore could use specific queries instead of fragments in a situation like this.

Return to SingleUser.js and import the needed packages (remember to import graphql-tag and place the fragment in SingleUser.js directly if you're using that instead of the macro):

https://gist.github.com/5de6c9e89bdec42aa59daeadeb52302f

Something new! <ApolloConsumer> is another context-like component that allows its children direct access to the cache. It's used a lot in applications that utilize apollo-link-state to use the cache for local state management, and for us it's a convenient way to use a fragment to read a user from the cache.

Word of warning, there's going to be a lot of code in the <SingleUser> component; the bulk of it is JSX for laying out user data. The important bits, where we use <ApolloConsumer> to access the client and the fragment to extract data, are less than 10 lines total. Let's set up our consumer:

https://gist.github.com/783d32041f0664f54d3a0973d6125264

The user id is extracted from a URL param provided by Reach Router, and <ApolloConsumer> uses a render prop that passes in client. If this makes you a little uncomfortable (where is the client coming from?) you're not alone; just take it on faith that the consumer component allows automatic access to the client object. The next step is of course to write the readUserFragment() function:

https://gist.github.com/e465ef7729ad978b846f586ddd84817f

Let's break it down: readUserFragment() takes in one parameter, the client, and uses the readFragment() method on the client object to extract the desired fields specified in the fragment from the object with the specified id. If this object isn't found in the cache, an error message is returned (recall that all of the cache data is queried from the home route). Go to the running application's home route, click on a user link, and open up the console - hopefully the data is there. Note that if the if (!data) {} conditional check is removed, it'll still work, the returned data will just be null - when we add JSX rendering it will crash, though, so it's important to have it in place. Unlike on a <Query> component, you can call this data whatever you want, it's just a const set to the value of the read fragment.

This seems pretty simple, but there is quite a bit going on. It's kind of funky that even though the only query to the remote API that's been run requested all of the user data in one call, each indivdual user's data are accessible this way. If you looked at the cache in dev tools earlier and saw how each user got its own entry, separate from all users on the root query object, you've seen how it works: the Apollo cache normalizes the queried data and automatically gives each entry its own object - very handy. The dataIdFromObject that was set earlier comes into play too - we're using ES6 property value shorthand to access the user object via id; if dataIdFromObject is not set then you'd have to do id: `User:${id}` to properly access the object. The ability to customize how your cache is set up is a very valuable tool.

Ok, here's the big chunk of code I warned you about. Don't be afraid, it's just some destructuring and JSX to get all the user's data to show up in a table. Replace return <h3>Success!</h3> like so:

https://gist.github.com/garethpbk/672cb0669d6f3fa131d14817541f72cc

One quirk: the name property of company can't be destructured since there's already a name property, so it has to be accessed on the company object. Here's what our final <SingleUser> component looks like:

apollo-single-user-fragment

Lookin' good. Time to move on to the last piece we'll cover, creating new users with the <Mutation> component.

POSTing Data with Mutations

GraphQL mutations are its method of creating, updating, and deleting data. Anytime you want to change something, you'll reach for a mutation. In a REST setup you'd use POST, PUT, PATCH, or DELETE; in GraphQL it's all mutations.

Apollo 2.x offers a <Mutation> component to handle this in an application's front-end. It's a bit more complicated than the <Query> component, given that modifying data is a bit more complicated than reading it. We're going to use a mutation to create a new user that will be added to the cache (and will then be accessible with the fragment created above).

Before we proceed, another warning: there is a lot of code involved here. Most of it doesn't have anything to do with mutations or GraphQL specifically, rather it's React and JSX code for dealing with input and state management. The way we're going to set up state and the change handler to control form input is not really a good one and you probably shouldn't use it in production. Form state will be intentionally set up to mirror the shape of the API, and as such involves nested state; this isn't necessarily always bad in React but it can be quite difficult to deal with. I'm not going to go into much detail about state management since it isn't the focus, just trust that it will work for what we need it to do here. In the real world I'd encourage you to find a better, flatter way to structure state and package it for mutations. Maybe use something like immer.

With that out of the way, let's start by writing the mutation itself. Create CREATE_USER_MUTATION.graphql in the graphql directory; it'll look very similar to the query from earlier:

https://gist.github.com/a922086182a08930f304e6aedc886d1f

Two key differences/additions from query: createUser takes in a variable called input (we'll roll all the data to be sent into this one variable), and the "POST" method is specified. apollo-link-rest defaults to the "GET" method if it's not specified, thus why we didn't have to declare this in the query. As you probably guessed this could also be "PUT" or "PATCH" or "DELETE."

Unlike the query and fragment, the mutation will have its functionality split between App.js and its own file. This isn't a rule, you could just as easily keep it all in its own file or spread it across two or more dedicated files. But here we'll stick with the <App> component, so open up App.js and load these two files:

https://gist.github.com/a2fcea8adbdb8186e42d01645c9e5bfc

You'll need to import { loader } from 'graphql.macro' as well (by now you should know how to do all this with graphql-tag if you're going that route). We'll soon see why we're also loading the GET_ALL_USERS_QUERY in addition to the mutation.

Here comes the ugly part. Let's add in local state and a change handler. As mentioned this is intentionally designed to mirror the API so that it doesn't have to be formatted later and can easily be spread right in to the mutation. Feel free to rip me one in the comments if you have a nicer way of handling this that preserves the nested state format.

https://gist.github.com/garethpbk/1dd9290026c5e1a6f31ec7163fdc86e7

Next let's bring in the <Mutation> component. Import it from react-apollo, so that you've got import { ApolloProvider, Mutation } from 'react-apollo' in App.js. Underneath the <Router> component, in the div with a class of AppContainer, add another div to wrap some text and the <Mutation> component:

https://gist.github.com/1ecdb8125788ccfe5c942748d301a8eb

Like <Query> and query, <Mutation> takes in a prop called mutation into which a GraphQL mutation is passed. A render prop then passes createUser into a <CreateUser> component, which we'll write soon (it'll hold the form with inputs for setting values). As mentioned you could put the whole form here instead of its own component if you wanted, or break this whole div out into its own separate file.

createUser comes from the GraphQL mutation, but we need to write the createNewUser() function, in which the mutation request is actually sent. Under the handleChange() function set up createNewUser():

https://gist.github.com/garethpbk/d15e6747acb263142a2fa4a89b027510

Let's go over this: first we prevent the default form action, something you've probably seen many times before. We use the readQuery() method on the client object to take a peek at the array stored in the cache of all user data, and then get its length so that we can determine the proper id to assign to the new user (if your application uses something like MongoDB that automatically creates an id, this step isn't necessary) - this is the first time we need the all user query in the mutation. Now the meat: action refers to createUser that will be passed in from the <CreateUser> component's (which itself recieves createUser as a prop) we'll set up next. Recall how the GraphQL createUser mutation took in one variable, called input - this is where it's set. We feed it an id and the entire value of current state, from which it will create a new user. Last we clear local state to blank the form.

Sidenote: notice that the client object is freely accessible in createNewUser(), given that it was declared in the same file. The <SingleUser> component could have received client as a prop, then, instead of relying on <ApolloConsumer> to provide access. I wanted to give an example of using <ApolloConsumer>; if you're trying to access client in a component deep down in the component tree and don't want to prop drill, it's a lifesaver. Just like React context.

There is a lot going on here. It's ok to be confused, especially given that we're referring to things that we haven't written yet. Let's rectify that by creating CreateUser.js in components, and taking a deep breath - we've got a lot of form JSX to write:

https://gist.github.com/garethpbk/f890515713ce6b685467ec501fd75d96

Once again all of this is agnostic to GraphQL and Apollo, it's just a form with controlled inputs. The form's onSubmit action is what's important: this calls createNewUser() that we just wrote, and provides it the event and createUser action. Give it a shot - fill out the form (every field is required) and click "Create New User (GraphQL)" and see what happens.

...looks like nothing happened, other than the form blanking. Open up dev tools and look at the cache, though - you should see a new object created with id 11:

new-user-no-query

Check the root query object - it hasn't changed, no new user here. Only the individual user entry has been created. Try filling out the form with different values and sending it again - you'll see that a user with id 12 is not created, rather id 11 is re-written with the newer data. Thinking about how we assigned ids - by counting the length of the users object on the root query - this makes sense, as the root query isn't being updated. Lucky for us, the <Mutation> component accepts another prop called update that tells the client to update the root query when the mutation is successfuly submitted. Let's add it to <Mutation> in App.js:

https://gist.github.com/f4a0fd5949880b727659a6ef77a56539

Here be the second use for the GET_ALL_USERS_QUERY that was earlier loaded. This reads pretty easy: a successful mutation triggers a read of the query in the cache, and then writes the new user object to it through array concatenation. With this added, a second mutation will no longer overwrite the first one in the cache and all new users will be included on the root query (peep it in the dev tools cache), and, most excitingly, the results of the mutation will update the list of users in real time:

new-user-yes-query

Clicking on a new user will direct to its <SingleUser> view, using a fragment to read directly from the cache - even though the new user doesn't exist on the remote API. Refreshing the page, of course, will wipe out the new user(s) - no persistent storage. A real REST API with a real endpoint for accepting POST requests and creating database entries from them would successfully persist your newly added user.

One more thing before we wrap up: Apollo is also good at handling POST requests made from the fetch API that don't use a mutation to create new data; it's also useful to compare how you would do things with fetch vs GraphQL. I'm not entirely sure how it works, but a new user created via fetch will be included in the root query which will update and re-fetch automatically. Add this button too CreateUser.js, below the submit button:

https://gist.github.com/b3078d85b571fa0de92feda6ad92735a

Add the createViaFetch() function above the return:

https://gist.github.com/98b3d22b8a3e68df695e625dfd373f95

Try adding some new user data and clicking "Create New User (Fetch)" - you'll see the same result as the GraphQL mutation, including in the dev tools cache. You can freely mix creating users with mutations and fetch, Apollo doesn't seem to care. This reinforces a big strength of Apollo and the apollo-link-rest package: you can start incrementally implementing GraphQL in your application without having to rewrite all of it. You could start with converting some GET requests to <Query> components without worrying about sending any data.

Conclusion

If you've made is this far, congratulations! You now should have a good idea of how Apollo Client works and how apollo-link-rest makes it easy to start with GraphQL.

There's one major point to address that's been lurking under the surface this whole time and was briefly touched on earlier when discussing how the action of a <Query> component would be done with fetch and client-side filtering. And that is: if all of this is happening client-side, isn't the data filtering when requesting only some fields from a REST endpoint still being done client-side? Doesn't that defeat the one of the primary strengths of GraphQL, in that it can return only specific bits of data instead of entire JSON responses?

The answer is yes, there is still client-side filtering happening and yes, it does nullify the performance aspect of GraphQL's key benefit in preventing overfetching. Apollo Client abstracts it away and does it for you, but somewhere under the hood the entire JSON response is still being combed through to return only the requested fields. apollo-link-rest is awesome, but it isn't magic, and it can't replace what an actual GraphQL server will do. The full power of GraphQL can only be realized by utilizing it on both ends of an application.

Despite this apollo-link-rest is still incredibly useful for a variety of reasons, and is a great way to get started with GraphQL and to start experimenting with transitioning an application from REST to GraphQL. If you've written an application with it and later have the ability to transition to a real GraphQL API, the only changes you likely need to make will be swapping restLink for createHttpLink (or using apollo-boost), subbing in the GraphQL API uri, and removing @rest and @type directives from queries and mutations. All of the React components and ways of dealing with the cache will remain identical.

I hope you give GraphQL a shot - it's made front-end development fun again for me, and seems to be growing exponentially in popularity. It's a great skill to have in your toolkit and apollo-link-rest is a great way to start experiencing the wonders of GraphQL and Apollo Client today.

The full repository for this project can be found at: https://github.com/garethpbk/apollo-rest-example

A live deployment is available at (Apollo dev tools are disabled in production): https://apollo-link-rest-jsonplaceholder.netlify.com/

@vvkkumawat
Copy link

How can I change request headers? I want to set it to
header = {
"accept": "application/json",
"Access-Control-Allow-Origin": "*",
"authorization": localStorage.getItem('token') ? "Bearer " + localStorage.getItem('token') : "",
"content-type": "application/keyauth.api.v1+json"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment