Tutorials on Apollo Client

Learn about Apollo Client from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

Should I Directly Access Data From the Apollo Client or From React Component State?

Consider the following code snippet of a React component, <App /> , that... You may have noticed that the data sent back by the mutation provides the user's information in a logIn field, and any data returned from a successful mutation automatically gets added to the local Apollo Client cache. Therefore, why do we have a user state variable when we could just access the user's information via the data field in the mutation result object? For example, like this: This can be considered an anti-pattern, but data can be either undefined or { logIn: { id: ..., token: ..., ... } } . Therefore, you would need to check if data is undefined or not directly in the body (and rendering section) of the <App /> component. Even after you determine that data is not undefined , you would still need to perform the same number of checks as before for the logIn property, etc. By using the setUser approach, you start with a baseline user object with its properties initialized to null , so you don't have to check if the user is undefined in the body (and rendering section) of the <App /> component (one less check). Additionally, with this approach, you only perform the checks for the data inside the onCompleted function. You could directly access the cache via the update function, which is called after the mutation completes and provides the cache as an argument, like so: However, the cache at this point doesn't actually have user data in the cache (to confirm this, print JSON.stringify(cache.data.data) in the update function). The user data is provided separately as the update function's second argument. You would need to manually modify the cache so that it has this user . Once the cached data is updated, the change gets broadcasted across the application and re-renders the components with active queries that correspond to the updated data. So you would need to put into each component that relies on user a call to useQuery that fetches the user . On initial page load, it's an extra, unnecessary network request since the LOG_IN already gets us the user data. But after the initial page load, if the user decides to log in or log out, then getting the user will be based on the update to the cache and having its updated user data be broadcasted to the useQuery s. In this case, it's more ideal to use setUser if it means one less network request on initial page load. As always, it's completely up to you how you want to manage state in your applications, but be sure to evaluate the trade-offs for each possible solution and pick the one that best suits your situation. Check out this Codesandbox example to see what I mean: https://codesandbox.io/embed/mutations-example-app-final-tjoje?fontsize=14&hidenavigation=1&theme=dark If you want to learn more advanced techniques with TypeScript, GraphQL and React, or learn how to build a production-ready Airbnb-like application from scratch, then check out our TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL :

Thumbnail Image of Tutorial Should I Directly Access Data From the Apollo Client or From React Component State?

Optimistic UIs with React, Apollo Client and TypeScript (Part I) - Project Overview

Liking a tweet on Twitter. Marking an e-mail as read in your G-Mail inbox. These type of simple, low-stake actions seem to happen so quickly that you can perform one action after another without having to wait for the previous to finish resolving. As the defining trait of optimistic UIs , these actions give the feeling of a highly responsive and instant UI. Psychologically speaking, they trick the user into thinking that an action has completed even though the network request it sends to the server has not been fully processed. Take, for example, the like button of a tweet. You can scroll through an entire feed and like every single tweet with zero delays between successive tweets. To observe this, open up a Twitter feed and your browser's developer console. Within the developer console, switch to the network tab and select the "Slow 3G" option under the throttling dropdown to simulate slow 3G network speeds. Slowing down network speeds lets us see the UI updates happen before the server returns a response for the action. Then, filter for network requests sent to a GraphQL API endpoint containing the text "FavoriteTweet" (in the request URL), which tells the server to mark the tweet as liked by the current user. When you click on a tweet's like button, the heart icon disappears, the like count increments by one and the text color changes to pink despite the network request still pending. While the server handles this request, the updates to the UI give the illusion that the server already finished processing the request and returned a successful response. In the below GIF, you can watch how liking multiple tweets, one after the other, immediately increments the like count of each tweet by one on the UI even if the server is busy working on previous requests. The user gets to like as many tweets as they want without waiting on any responses from the server. Upon receiving a response back from the server, the heart icon of the like button fades back in with an animation. Here's what a normal implementation of the like button might look like: Here's what Twitter's implementation of the like button looks like: Note : Twitter's UI never disables the like button. In fact, you can click on the like button as many times as you like. The UI will be updated accordingly, and the network requests for every click get sent to the server. By building UIs in this manner, the application's performance depends less on factors like the server's status/availability and the user's network connectivity. Since humans, on average, have a reaction time of 200 to 300 milliseconds , being delayed for this amount of time (or more) between actions (due to server response times) can cause not only a frustrating user experience, but also, hurt the brand's image. Being known for having a slow, unreliable, unresponsive UI makes users less likely to enjoy and engage with the UI. As long as the user perceives actions as being instant and working seamlessly, they won't ever question the application's performance. The key to adopting optimistic UI patterns is understanding the meaning of the word "optimistic." Optimistic means being hopeful and confident that something good will occur in the future. In the context of optimistic UIs, we should be confident that for some user action, the server, in at least 99% of all cases, returns a successful response, and in less than 1% of all cases, the server returns an error. In most situations, low-stake actions tend to be ideal candidates when deciding where to apply optimistic UI patterns. To determine whether an action is a low-stake action, ask yourself these questions: If the answer to all these questions is yes, then the action is a low-stake action, and thus, can update the UI optimistically with more benefits to the user experience than drawbacks. In the case of Twitter's like button: On the other hand, you should not consider optimistic UI patterns for high-stake actions, especially those involving very important transactions. For example, could you imagine a bank site's UI showing you that your check was successfully deposited, and then discovering days later, when you have to pay a bill due the next day, that it was not deposited because of the server happened to be experiencing a brief outage during that time? Think about how angry you would be at the bank and how this might sour your perception of the bank. Integrating optimistic UI updates into an application comes with challenges like managing local state such that results of an action can be simulated and reverted. However, applications built with React and Apollo Client have the necessary tools, features and APIs for easily creating and maintaining optimistic UIs. Below, I'm going to show you how to recreate a well-known optimistic UI found in a popular iOS app, Messages , with React and Apollo Client. When a user sends a message, the message appears to have been sent successfully even if the server has not yet finished processing the request. Once the server returns a successful response, there are no changes made to the UI except for a "Delivered" status text being shown beneath the most recently sent message. To get started, scaffold a new React application with the Create React App and TypeScript boilerplate template. For this project, we will be building a "public chatroom" that lets you choose which user to send messages as: Upon picking a user, the application displays the messages from the perspective of the selected user, and you can send messages as this user. Note : This server does not support real-time communications since that's outside the scope of this tutorial. You can add functionality for real-time communications with GraphQL subscriptions. Next, clone (or fork) the following GraphQL API server running Apollo Server. https://codesandbox.io/embed/apollo-server-public-chat-room-for-optimistic-ui-example-srb5q?fontsize=14&hidenavigation=1&theme=dark This server defines a GraphQL schema for a basic chat application with two object types: User and Message . It comes with a query type (for fetching user/s and message/s) and a mutation type (for adding a new message to the existing list of messages). Initially, this server is seeded with two users and twenty messages. Each resolver populates a single field with this seeded data that is stored in memory. Within the newly created React application, let's install several dependencies: Since the application will be styled with Tailwind CSS , let's set up Tailwind CSS for this application. Within the tailwind.config.js file, add the paths glob pattern ./src/**/*.{js,jsx,ts,tsx} to tell Tailwind which type of files contain React components. Since the UI features an input field, we should also the @tailwindcss/forms plugin with the strategy option set to class to leverage Tailwind CSS form component styles via CSS classes. ( tailwind.config.js ) Delete the src/App.css file and remove all of the default CSS rules in the src/index.css file. Within this file, add the standard @tailwind directives: ( src/index.css ) Add several empty directories to the src directory: To initialize an ApolloClient instance, import the Apollo Client and pass it a configuration object with two options: To make the Apollo Client instance available throughout the entire React application, wrap the <App /> component within the provider component <ApolloProvider> , which uses React's Context API. Here's what the src/index.tsx file should look like: ( index.tsx ) The application contains two child components: Since both components must know who the current user is, and the <UsersList /> component sets the current user, let's define a React context AppContext to make the current user globally available to the application's component tree. Within the src/context directory, add an index.ts file: Then, define the React context AppContext . Its value should contain a reference to the current user ( currentUser ) and a method for setting the current user ( changeCurrentUser ). ( src/contexts/index.ts ) Although we initialize the value of AppContext to an empty object, we will later set this context's value in the <App /> component, where we will pass it its actual value via its provider component's value prop. The AppContextInterface interface enforces the types allowed for each method and value specified in the context's value. You may notice a User type that is imported from a src/types/index.ts file. Within the src/types directory, add an index.ts file: Based on the GraphQL schema, define a User interface. ( src/types/index.ts ) Within the src/App.tsx file, import AppContext and wrap the child components and elements of the <App /> component with the AppContext.Provider provider component. Inside of the <App /> component's body, we define a state variable currentUser , which references the currently selected user, and a method changeCurrentUser , which calls the setCurrentUser update function to set the current user. Both currentUser and changeCurrentUser get passed to the AppContext.Provider provider component's value prop. These values satisfy the AppContextInterface interface. ( src/App.tsx ) The <UsersList /> component fetches a list of users from the GraphQL API server, whereas the <MessagesClient /> component fetches a list of messages from the GraphQL API server. To fetch data from a GraphQL API server with Apollo Client, use the useQuery Hook. This Hook executes a GraphQL query operation. It accepts two arguments: And returns a result object, which contains many properties. These are the most commonly used properties: These properties represent the state of the query and change during its execution. They can be destructured from the result object and referenced within the function body of the component, like so: For more properties, visit the official Apollo documentation here . Once it successfully fetches data from the GraphQL API server, Apollo Client automatically caches this data locally within the cache specified during its initialization (i.e., an instance of InMemoryCache ). Using a cache expedites future executions of the same queries. If at a later time, Apollo Client executes the same query, then Apollo Client can get the data directly from the cache rather then having to send (and wait on) a network request. Within the src/components/UsersList.tsx file, define the <UsersList /> component, which... ( src/components/UsersList.tsx ) Once the data has been successfully fetched, the component renders a list of users who are members of the "public chatroom." When you click on one of the users, you select them as the current user. A check mark icon appears next to the user's name to indicate that it has been selected. Since the query returns a list of users, the UsersQueryData interface contains a users property that should be set to a list of User items, like so: ( src/types/index.ts ) Note : It should match what's specified by the GraphQL query string that's passed to the useQuery Hook. To refresh the cached data with the latest, up-to-date data from the GraphQL API server, you can: To know when Apollo Client is refetching (or polling) the data, destructure out the networkStatus value from the result object, and check if it equals NetworkStatus.refetch , which indicates an in-flight refetch, or if it equals Network.poll , which indicates an in-flight poll. Note : The notifyOnNetworkStatusChange networking option tells Apollo Client to re-render the component whenever the network status changes (e.g., when a query is in progress or encounters an error). For a full list of network statuses you can check for, click here . Like the <UsersList /> component, the <MessagesClient /> component also fetches data (in this case, a list of messages) by calling the useQuery Hook. When rendering the messages, the current user's messages are aligned to the right-side of the messages client. These messages have a blue background with white text. All other messages are aligned to the left-side of the messages client. By adding the sender's initials and name to each of these messages, we can tell who sent which message. ( src/components/MessagesClient.tsx ) All that's missing from the message client, UI-wise, is an input field for sending messages. Below the messages, add a form with an input field and send button. Altogether... ( src/components/MessagesClient.tsx ) If you find yourself stuck at any point while working through this tutorial, then feel free to visit the part-1 branch of this GitHub repository here for the code. Thus far, we learned how companies like Twitter adopt optimistic UI patterns to deliver faster, snappier user experiences. We set up the project with Apollo Client, Tailwind CSS and TypeScript, and we built a UI that queries data from a GraphQL API server. Continue on to the second part of this tutorial here , in which we implement the remaining functionality: Specifically, we will dive into the useMutation Hook and learn how to manipulate data within the Apollo Client cache to update the UI optimistically.

Thumbnail Image of Tutorial Optimistic UIs with React, Apollo Client and TypeScript (Part I) - Project Overview

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Building a GraphQL Application with Vue 3 and Apollo

RESTful APIs adhere to a reliable architectural standard for transferring data statelessly over the HTTP protocol. Every endpoint of an API semantically describes how a resource should be created ( POST ), read ( GET ), updated ( PUT / PATCH ), deleted ( DELETE ), etc. Large, data-driven applications consume data from multiple third-party/in-house sources, and each one exposes a unique set of endpoints to manage different resources. Adapting these applications to support a wide range of platforms and device sizes (commonly mobile, desktop and web) may present several problems: Using Facebook's GraphQL query language, the client specifies its exact data requirements to the server via a single endpoint. Establishing a schema (written with the syntax of the GraphQL Schema Definition Language) creates a contract between the client and server that defines what data can be read from and written to the data graph by the client. This data graph centralizes all of the APIs consumed by your application by mapping each field to a resolver that populates it with a value retrieved from an endpoint of one of these APIs, a database, etc. A client can fetch data from a GraphQL server via plain HTTP and then manually update the UI accordingly. However, GraphQL clients such as Apollo Client abstract away the low-level implementation details of these features underneath a declarative API. Built by the Apollo GraphQL team, Apollo Client is an open-source GraphQL client that provides a lot of out-of-the-box functionality for communicating with a GraphQL server: To integrate Apollo Client into an application using another JavaScript library/framework besides React, which Apollo Client already has built-in support for, there exists view integration libraries within the Apollo ecosystem that provide bindings for Vue , Svelte , Angular , Ember and Web Components . The Vue Apollo library integrates Apollo Client into a Vue application. Different versions of Vue Apollo are compatible with different versions of Vue: Although Vue Apollo v4 is still in active development (alpha phase), it offers support for Vue 3's Composition API , which collocates the methods corresponding to component options ( watch , computed , etc.) and lifecycle hook registrations ( onMounted , onUnmounted , etc.) within a single component option, setup . Using Vue Apollo v4 methods, such as useQuery and useMutation , the data requirements are also placed within the setup method. This approach makes it much easier to reason about a component's code compared to the Vue Apollo Options API approach, which places the data requirements within an apollo component option (independent of the other code placed within the remaining component options). Below, I'm going to show you: Using Vue 3's Composition API and the Vue Apollo (v4) library, we will be building the following GitHub search client: To start, download this project from GitHub: This repository is based on a custom Vue CLI project template that runs Vue 3 and includes support for TypeScript, ESLint and Prettier. If you want to learn how to manually set up the base structure of this project, then proceed to the next section (" Installation "). Otherwise, you may skip the " Installation " section, download the already prepared project structure and proceed directly to the " GitHub GraphQL API " section. To generate a new Vue CLI project, run the following command in the terminal: When prompted with "Please pick a preset," select the "Manually select features" option: This project will support TypeScript. Press "Space" to select "TypeScript." When prompted with "Choose a version of Vue.js that you want to start the project with," select the "3.x (Preview)" option: Vue components will not be written with the class syntax. The Class API was officially dropped . This project will use Babel alongside TypeScript. For linting and code formatting, select the "ESLint + Prettier" option: Anytime changes to a file are saved, run the linter. For this project, let's place the Babel, ESLint, etc. configurations within their own dedicated files to avoid increasing the size of package.json . These answers are only for this project. In the future, you may want to try out different sets of project configurations to determine what specific tools make you more productive. To integrate type definitions from the schema of GitHub's GraphQL API , install @octokit/graphql-schema : Several type definitions are assigned nullable types , which will cause ESLint to raise the following error within your IDE. Inside of the .eslintrc.js file, turn off the rule @typescript-eslint/no-non-null-assertion . ( .eslintrc.js ) In 2017, GitHub publicly released its GraphQL API . GitHub's GraphQL API exposes a public schema for interacting with GitHub itself, whether fetching commit data or starring a repository, all accessible from a single endpoint. To send requests to GitHub's GraphQL API, generate an access token. This access token must be set to each request's Authorization header. When setting permissions for the access token, enable repository privileges. Create a .env file at the root of the project directory. Copy the 40 character-long access token to your clipboard. Set the environment variable VUE_APP_GITHUB_ACCESS_TOKEN to this access token. ( .env ) Note : Environment variables prefixed with VUE_APP_ can be accessed via process.env within Vue applications. Without this prefix, environment variables are undefined . First, install graphql , @apollo/client and @vue/apollo-composable as dependencies: To rapidly style the UI interface, we will be using the Tailwind CSS framework. Install tailwindcss , postcss and autoprefixer as dependencies: Then, create a minimal Tailwind configuration file ( tailwind.config.js ) at the root of the project directory. Note : The -p flag creates a minimal PostCSS configuration file ( postcss.config.js ) at the root of the project directory, alongside the generated tailwind.config.js . Inside of tailwind.config.js , set the purge option to a list of filenames/globs for PurgeCSS to analyze and remove unused CSS. ( tailwind.config.js ) Inside of public/index.html , add these two CSS classes to the <body /> element: ( public/index.html ) When you run the application, you will encounter the following error: Although the latest version of the tailwindcss PostCSS plugin (v2) is compatible with latest version PostCSS (v8), other tools within the PostCSS ecosystem may not yet be compatible with this version of PostCSS. To resolve this error, uninstall tailwindcss , postcss and autoprefixer , and then reinstall these dependencies with the PostCSS (v7) compatibility build. Inside of main.ts (the entry point of the Vue 3 application), create an ApolloClient instance. Let's pass an object containing configuration options to the Apollo Client: For this application, we will define two links: To inject the Apollo Client into the application and allow child components to access the Apollo Client, call the provide method within the createApp 's setup method to "provide" this client to the application and its children components. Since this application only interacts with a single GraphQL API, set this client as the default client. Putting it altogether... ( main.ts ) Our application requires three child components: By default, the reactive searchOptions object, which represents the arguments passed to the GitHub GraphQL API's search query, is dynamically assigned to the prop search-options of the <RepositoryList /> component. Any changes to searchOptions , particularly to query , which corresponds to the value of the search bar's input, will cause the <RepositoryList /> component to retrieve a new list of repositories. The value of query is changed whenever the search event is emitted from the <SearchBar /> component, which occurs on changes to the value of its input. ( src/App.vue ) Typing a query emits a "search" event with the current query and triggers the search function in the <App /> component. This search function sets the value of the query field in the reactive searchOptions object. Debounce the handleInputChange event handler to avoid sending the search event on every single input change. ( src/components/SearchBar.vue ) When an event is fired, this debounce function starts a timer and waits for a specific time period to elapse before calling its corresponding event handler. If another event is fired during this time period, then the previous event is ignored. The timer resets and must wait for the specific time period (now reset) to elapse before calling the new event's corresponding event handler. This debounce function invokes the event handler on the trailing edge of the timeout. ( src/utils.ts ) Store the queries and mutations within a single file. This application requires only one query and two mutations: If you decide to add more queries/mutations that return a repository/repositories, and you request for the same repository fields for those queries/mutations, then use the repo fragment to keep your code DRY. ( src/graphql/documents.ts ) Inside of the <RepositoryList /> component, fetch a list of repositories based on the searchOptions passed from the <App /> parent component. To fetch this list of repositories, the component executes the composition function useQuery , which accepts a GraphQL document ( SEARCH_REPOS ) as the first argument and query arguments ( searchOptions ) as the second argument. This function is compatible with the setup function of Vue 3's Composition API. useQuery returns an object that contains several Ref values: Sometimes, a query may return multiple top-level objects. To pick a single object from the result object returned by useQuery , use the useResult composition function. Instead of referencing the repositories from the result object as result.search.edges in the component's template, it can just be referenced as repositories . Plus, the default value assigned to repositories is the second argument passed to useResult (in this case, an empty array). ( src/components/RepositoryList.vue ) Inside of the <Repository /> component, there is a button for starring/unstarring a repository, depending on whether or not you have starred the repository previously. If you have not yet starred the repository, then clicking the button will star the repository, and vice-versa. The event handler calls either the unstarRepo or starRepo functions to unstar or star a repository respectively. Each of these functions execute the composition function useMutation , which accepts a GraphQL document ( ADD_STAR or REMOVE_STAR ) as the first argument and options (an object containing mutation arguments via the variables property, etc.) as the second argument. Similar to useQuery , this function is compatible with the setup function of Vue 3's Composition API. When a repository is starred/unstarred, we must update the cache to reflect this mutation. To understand why this is important, let's walkthrough an example. Imagine you typed the query "facebook" into the search bar's input. This will fetch all repositories relevant to "facebook" from GitHub's GraphQL API. Suppose you have already starred the facebook/react repository, and you decide to unstar it. After you unstar it, you decide to type the query "google" into the search bar's input. This will fetch all repositories relevant to "google" from GitHub's GraphQL API. If you again type the query "facebook" into the search bar's input, then this will fetch all repositories relevant to "facebook" from the Apollo Client's cache . What was cached previously for this query was a list of repositories relevant to "facebook," including the facebook/react repository. However, this repository was cached when it was still starred. Therefore, we must modify this repository in the cache to reflect that it was recently unstarred. To update the cache, set the update property in the options object to a function that provides an instance of the cache and the data returned from a mutation. This function will call the overrideMutationStarCache , which will read the already cached data (via the cache 's readQuery method) and write the result of the mutation to the appropriate repository entity (via the cache 's writeQuery method). Don't forget to also increment/decrement the stargazers count! Putting it altogether... ( src/components/Repository.vue ) Run the application locally: Visit localhost:8080 in a browser to interact with the application. In this blog post, we only explored a very small subset of the GitHub GraphQL API. Try experimenting with other aspects of the public schema of the GitHub GraphQL API. For a more difficult challenge, try connecting an existing Vue 3 application to a custom GraphQL server. If you want to learn more about Vue 3, then check out Fullstack Vue :

Thumbnail Image of Tutorial Building a GraphQL Application with Vue 3 and Apollo