3. Using APIs
This step takes our static components and populates them with data from the GitHub GraphQL API – loading states and all. We’ll be displaying Carbon repository information in a data table.
- Fork, clone and branch
- Install dependencies
- Create access token
- Connect to Apollo
- Fetch data
- Populate data table
- Add loading
- Add pagination
- Submit pull request
Preview
The GitHub GraphQL API is very well documented, and even though the focus of this tutorial isn’t learning and using GraphQL, it’s a great opportunity to fetch Carbon-related data for this Carbon tutorial.
To do so, we’ll be using Apollo Client, the front-end component of the Apollo Platform. Apollo provides several open source tools for using GraphQL throughout your application’s stack. Apollo Client is a sophisticated GraphQL client that manages data and state in an application.
A preview of what you will build (see repositories page):
Fork, clone and branch
This tutorial has an accompanying GitHub repository called carbon-tutorial-vue that we’ll use as a starting point for each step. If you haven’t forked and cloned that repository yet, and haven’t added the upstream remote, go ahead and do so by following the step 2 instructions.
Branch
With your repository all set up, let’s check out the branch for this tutorial step’s starting point.
git fetch upstreamgit checkout -b vue-step-3 upstream/vue-step-3
Build and start app
Install the app’s dependencies:
yarn
Then, start the app:
yarn serve
You should see something similar to where the previous step left off. Stop your app with
CTRL-C
Install dependencies
We’ll shortcut this using the Vue CLI, if you’d like more information head over to Vue Apollo Installation for details.
Install the following
- - package containing everything you need to set up Apollo Clientapollo-boost
- - parses your GraphQL queriesgraphql
- - Apollo integration for Vuevue-apollo
Using the command:
vue add apollo
At the following prompts answer ‘No’ to each of the questions.
- Add example code? No
- Add a GraphQL API Server? No
- Configure Apollo Engine? No
Create access token
You’ll need a personal access token from your GitHub account in order to make requests to the GitHub API. Check out this guide to see how to get one.
When you get to the scope/permissions step, you can leave them all unchecked. We don’t need any special permissions, we just need access to the public API.
Once you have your token, we need to put it in a place where
create-vue-app
.env
process.env.MY_VARIABLE
One caveat is that we need to start our variables with
VUE_APP_
Since we don’t want to commit this file to Git, we can put it in
.env.local
.gitignore
x
VUE_APP_GITHUB_PERSONAL_ACCESS_TOKEN=xxxxxx
Go ahead and start your app with
yarn serve
Connect to Apollo
The
vue-apollo
If you open
src/main.js
import { createProvider } from 'vue-apollo';new Vue({router,apolloProvider: createProvider(),render: (h) => h(App),}).$mount('#app');
This is loading from a file the CLI created for you
src/vue-apollo.js
Update the following values:
// Use our access tokenconst AUTH_TOKEN = process.env.VUE_APP_GITHUB_PERSONAL_ACCESS_TOKEN;// Target github apiconst httpEndpoint =process.env.VUE_APP_GRAPHQL_HTTP || 'https://api.github.com/graphql';
Update only the
wsEndpoint
getAuth
defaultOptions
const defaultOptions = {// set wsEndpoint to nullwsEndpoint: process.env.VUE_APP_GRAPHQL_WS,// Use the form expected by github for authorisationgetAuth: (tokenName) => `Bearer ${tokenName}`,};
Fetch data
Imports
Add the following imports to the top of the script section of
RepoPage.vue
import gql from 'graphql-tag';
Query
Next we’ll assemble our GraphQL query to fetch only the data we need from the GraphQL API. We’ll do this using the
gql
gql
Query
vue-apollo
You can use GitHub’s explorer tool to write and test your own queries. Try copying the query below and experiment with changing the properties. You can also click the “Docs” button in the top right of the explorer to view all of the available data and query parameters.
If you’d like some more information regarding writing queries and using the Query component, we recommend Apollo’s documentation on this topic.
Add this after your imports:
const REPO_QUERY = gql`query REPO_QUERY {# Let's use carbon as our organizationorganization(login: "carbon-design-system") {# We'll grab all the repositories in one go. To load more resources# continuously, see the advanced topics.repositories(first: 75, orderBy: { field: UPDATED_AT, direction: DESC }) {totalCountnodes {
Next, we need to configure apollo in our component script, adding the following after the data() declaration.
apollo: {organization: REPO_QUERY},
At this point, we should run our query view the raw the results to verify that the request is working.
In RepoPage.vue add the following before the
RepoTable
{{ this.organization }}
When the data loads you should see the response rendered on your repository page. If not, check the console to see if there are any errors and fix.
Revert this last change and continue.
This data is not quite in the format our
RepoTable
Remove the ‘rows’ constant and its use in the data declaration, and add this computed property.
computed: {rows() {if (!this.organization) {return [];} else {return this.organization.repositories.nodes.map(row => ({...row,key: row.id,stars: row.stargazers.totalCount,
At this point you have a working table but the links column clearly isn’t what we want.
Helper component
This column in the data table will be a list of repository and home page links, so let’s create a component called
LinkList
Add the following to create your component:
A template section:
<ul class="link-list"><li><cv-link :href="url">GitHub</cv-link></li><li v-if="homepageUrl"><span> | </span><cv-link :href="homepageUrl">Homepage</cv-link></li>
A script section:
export default {name: 'LinkList',props: {url: String,homepageUrl: String,},};
And a style section:
.link-list {display: flex;}
Now let’s make use of this component in our
RepoTable
At the top of the script section import the link list component:
import LinkList from './LinkList';
And below the name of the component add:
components: { LinkList },
Then make use of it in our template replacing:
<cv-data-table-cellv-for="(cell, cellIndex) in row.data":key="`${cellIndex}`">{{cell}}</cv-data-table-cell>
with
<cv-data-table-cell v-for="(cell, cellIndex) in row.data" :key="`${cellIndex}`"><template v-if="!cell.url">{{cell}}</template><link-list v-else :url="cell.url" :homepage-url="cell.homepageUrl" /></cv-data-table-cell>
Here in order to switch between the standard rendering of a data cell we’ve wrapped our standard
{{cell}}
Using the v-if and v-else directives we switch based on the contents of the cell between the standard rendering and the LinkList component.
Checking our output again, you should now see the LinkList component rendering the final column.
Next we’ll update our row description. Update the computed property data() in
RepoTable.vue
description: row.description
Check the output again and you should find the descriptions are updated.
After this many refreshes you may have noticed a slight delay in the data loading. As outlined in the documentation, all components contained under one with an apolloProvider have a
$apollo
apolloProvider
We can use the property to react to loading state.
First let’s demonstrate that this works.
Pass the loading state into our
RepoTable
<repo-table:headers="headers":rows="rows"title="Carbon Repositories"helperText="A collection of public Carbon repositories.":loading="$apollo.loading" />
Next add this property to the
RepoTable
props: {headers: Array,rows: Array,title: String,helperText: String,loading: Boolean,},
Making use of the property to display a loading message.
Replace:
<cv-data-table:columns="columns":title="title":helper-text="helperText"></cv-data-table>
with:
<div v-if="loading">Loading...</div><cv-data-tablev-else:columns="columns":title="title":helper-text="helperText"></cv-data-table>
Here we have made use of the v-if and v-else directives to switch content based on the state of
$apollo.loading
Now that we know this is works let’s try something a bit more sophisticated and replace the div containing our loading message with use of the
CvDataTableSkeleton
<cv-data-table-skeletonv-if="loading":columns="columns":title="title":helper-text="helperText":rows="10"/>
We need to tell the loading skeleton how many rows to render, so let’s use 10 skeleton rows to prepare for the next enhancement…
Add pagination
Pagination! Instead of rendering every repository, let’s add pagination to the data table to only render 10 at a time. Depending on your specific requirements, you may need to fetch new data each time that you interact with the pagination component, but for simplicity, we’re going to make one request to fetch all data, and then paginate the in-memory row data.
Let’s start by adjusting our
PageTable
<cv-data-tablev-else:columns="columns":title="title":helper-text="helperText":pagination="{ numberOfItems: this.totalRows }"@pagination="$emit('pagination', $event)">
In the pagination event we’ve used $emit and $event to re-raise the pagination event to our
RepoPage
RepoTable
We also need to add the
totalRows
totalRows: Number,
Next to our
RepoPage
RepoTable
:rows="pagedRows":totalRows="rows.length"@pagination="onPagination"
replacing
:rows="rows"
Next in the data property of our component add values for
pageSize
pageStart
page
data() {return {headers,pageSize: 0,pageStart: 0,page: 0};},
Then before we can see our paginated table we need to add: a
pagedRows
computed: {// other computed properties// ...pagedRows() {return this.rows.slice(this.pageStart, this.pageStart + this.pageSize);}},methods: {onPagination(val) {
That does it! Your data table should fetch GitHub data on first render. You can expand each row to see the repository’s description. You can modify the pagination items per page and cycle through pages or jump to a specific page of repositories.
Mystery
Hmmm, there is at least one more issue to resolve. If you expand a row or two to see the repository descriptions you will and then change page. What happens?
Assuming you didn’t catch this earlier you will find that the expanded rows, stay expanded after paging. That is if row two was expanded before pagination it is expanded after.
This is because we chose poor values to use as our row and cell keys as we iterated over them. The result is that Vue sees these items as having the same key and makes the assumption that content but not state has changed.
To fix this add the following to the RepoPage component you should be able to find something better.
watch: {rows() {if (this.organization) {console.dir(this.organization.repositories.nodes);}}},
Hint:
Can you fix it?
Submit pull request
We’re going to submit a pull request to verify completion of this tutorial step.
Continuous integration (CI) check
Run the CI check to make sure we’re all set to submit a pull request.
yarn ci-check
Git commit and push
Before we can create a pull request, stage and commit all of your changes:
git add --all && git commit -m "feat(tutorial): complete step 3"
Then, push to your repository:
git push origin vue-step-3
Pull request (PR)
Finally, visit carbon-tutorial-vue to “Compare & pull request”. In doing so, make sure that you are comparing to
vue-step-3
base: vue-step-3