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 (
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:
Smaller layouts display less information than larger layouts. Additionally, decreasing the size of a browser window hides away more and more supplemental content the smaller the browser window becomes. Because RESTful APIs define the shape of the response sent back to the client, mobile clients tend to over-fetch data compared to their corresponding desktop/web clients, assuming both consume the same set of APIs.
Building UIs involves composing components into higher-order components. At the higher levels of the component hierarchy, these higher-order components represent sections and layouts of an application. Each component encapsulates its own logic and data. Therefore, if each component fetches different pieces of information, such as widgets on an analytics dashboard, then a client must send many simultaneous (or successive) requests to satisfy all of its data requirements. This bottleneck is known as the n+1 requests problem, which is REST-specific and happens when a client fetches one collection resource (i.e.,
/repositories), followed by n singleton resources (i.e.,
/repositories/<id>). Often, the collection resource under-fetches data because it lacks sufficient information for each of its items.
UIs constantly change to reflect the growing needs of users. Endpoints of RESTful APIs may be tied to a particular view or component. This tight coupling makes it difficult for developers to rapidly iterate upon the UI in response to user feedback because any adjustments to the UI may affect its data requirements and result in waiting for the addition of new endpoints and/or the modification of existing endpoints.
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:
Sending Queries and Mutations via POST Requests
Built-In View Layer Integrations for React, iOS and Android
Caching Mechanisms for Pagination and Storing Recent Query Results
Customizable Data Flow with Apollo Link ("Middleware" Between Apollo Client and a GraphQL Server)
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 (
computed, etc.) and lifecycle hook registrations (
onUnmounted, etc.) within a single component option,
setup. Using Vue Apollo v4 methods, such as
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:
How to bootstrap a Vue CLI project.
How to integrate Apollo Client into a Vue 3 application using the Vue Apollo library.
The improved readability of Vue code using the Composition API.
A demo of Vue 3 and Apollo using the GitHub GraphQL API.
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
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
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
GitHub's GraphQL API#
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
When setting permissions for the access token, enable repository privileges.
.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.
Note: Environment variables prefixed with
VUE_APP_ can be accessed via
process.env within Vue applications. Without this prefix, environment variables are
vue-apollo into Vue 3 Application#
@vue/apollo-composable as dependencies:
@apollo/client- A GraphQL client that offers a declarative API for state management, caching and updating the UI on queries/mutations. The Apollo ecosystem offers view-layer integrations for front-end technologies such as React and Vue.
To rapidly style the UI interface, we will be using the Tailwind CSS framework. Install
autoprefixer as dependencies:
tailwindcss- An atomic CSS framework that provides single-purpose, utility classes over component-based classes. This package installs Tailwind as a PostCSS plugin.
postcss- A tool for processing and transforming CSS via plugins.
autoprefixer- postcss plugin to automatically prepend css rules with vendor prefix
Then, create a minimal Tailwind configuration file (
tailwind.config.js) at the root of the project directory.
-p flag creates a minimal PostCSS configuration file (
postcss.config.js) at the root of the project directory, alongside the generated
tailwind.config.js, set the
purge option to a list of filenames/globs for PurgeCSS to analyze and remove unused CSS.
public/index.html, add these two CSS classes to the
<body /> element:
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
autoprefixer, and then reinstall these dependencies with the PostCSS (v7) compatibility build.
main.ts (the entry point of the Vue 3 application), create an
Let's pass an object containing configuration options to the Apollo Client:
link- Apollo Client supports middleware between itself and a GraphQL server via links. On eachoutgoing GraphQL request, a link can customize the request, such as adding/editing its headers, or introduce a side-effect, such as logging. Composing multiple links forms a complex, sequential chain of network behavior.
cache- Apollo Client caches the results of GraphQL queries to speed up the execution of subsequent queries and save bandwidth on repeat queries. This will be set to a new instance of an
InMemoryCache, which is automatically provided by the
For this application, we will define two links:
The first link (
ApolloLink) sets the
Authorizationheader to a bearer token with the GitHub personal access token.
The last link (
HttpLink) sends the GraphQL request to a destination (in this case, the GitHub GraphQL API). This is often the last link defined.
To inject the Apollo Client into the application and allow child components to access the Apollo Client, call the
provide method within the
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...
Our application requires three child components:
<SearchBar />- The search bar of the search client. When the user types a query into the search bar, a new list of repositories is fetched based on the current query.
<RepositoryList />- The list of repositories retrieved from the GitHub GraphQL API.
<Repository />- An individual repository of the repository list. It has a button in the top-right corner for starring/unstarring the repository. Additionally, it displays the following information:
Description (Truncated to One Line)
Timestamp of the Last Update
Total Number of Stargazers
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.
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
handleInputChange event handler to avoid sending the
search event on every single input change.
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.
Store the queries and mutations within a single file. This application requires only one query and two mutations:
search- A query for retrieving a list of repositories based on the arguments passed to it.
addStar- A mutation for starring a repository.
removeStar- A mutation for unstarring a repository.
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.
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
result- An object that represents the data returned by Apollo from a GraphQL API.
loading- A Boolean value that indicates whether the query is still fetching data or not.
error- An error that is encountered while fetching the data. It contains a
messagethat describes the cause of the error.
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).
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
starRepo functions to unstar or star a repository respectively. Each of these functions execute the composition function
useMutation, which accepts a GraphQL document (
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
readQuery method) and write the result of the mutation to the appropriate repository entity (via the
writeQuery method). Don't forget to also increment/decrement the stargazers count!
Putting it altogether...
Run the application locally:
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: