Vue Tutorial: Implement Infinite Scroll in Vue.js Using Apollo and Hasura GraphQL
Tags: vue, vueapollo, pagination
Published: 2021-01-05 05:27:34

---
title: Vue Tutorial: Implement Infinite Scroll in Vue.js Using Apollo and Hasura GraphQL
published: true
date: 2020-12-23 15:00:39 UTC
tags: Vue, VueApollo, Pagination
canonical_url: https://hasura.io/blog/vue-tutorial-implement-infinite-scroll-vue-apollo-hasura-graphql/
---

![Vue Tutorial: Implement Infinite Scroll in Vue.js Using Apollo and Hasura GraphQL](https://hasura.io/blog/content/images/2020/12/vuejs_graphql_infinite_scroll.png)

_This articles was written by [Anthony Gore](https://twitter.com/anthonygore) as part of the **Hasura Technical Writer Program**. If you would like to publish an article about Hasura or GraphQL on our blog, apply [here](https://bit.ly/3hltrtT)._

"Infinite Scroll" is a UI feature that allows a website to show a long list of objects to a user (posts, comments, images, etc) without overwhelming their browser or network connection.

You'll be familiar with this feature if you've used social media apps like Twitter, Reddit, or Instagram which all have an implementation of this.

A key aspect of this feature is to call data on demand. This is trivial to implement using Hasura's query pagination.

In this article, we're going to implement infinite scroll in a Vue.js app using Apollo and Hasura.

Here's a break-down of the steps we'll cover:

- Quick set up of a Vue/Apollo app using Vue CLI
- Set up local Hasura with Docker
- Seed database with mock data
- Paginated database calls with Apollo's fetchMore feature
- Implement scroll loading in frontend

By reading this article, you'll gain a better understanding of the power of Hasura's GraphQL server and how it can be used effectively in a web app stack.

You'll also have a blueprint for implementing infinite scroll in your own JavaScript apps.

> If you'd like to see the complete code, check out [this GitHub repo](https://github.com/anthonygore/vue-apollo-hasura-infinite-scroll).

## Prerequisites

I assume you are familiar with the following:

- Basics of Vue & Vue CLI
- Basics of GraphQL

You'll need the following software on your system:

- Docker
- Node.js & NPM
- Vue CLI

## Creating an app with Vue CLI

To get a frontend app up and running quickly, Vue CLI is a great option. Let's create a new project by going to the terminal and typing:

```
$ vue create infinite-scroll

```

For this tutorial, you can instruct the CLI tool to create a Vue 2 app with the default settings.

Once installation completes, change into the project directory:

```
$ cd infinite-scroll

```

## Adding Vue Apollo

In case you aren't familiar with [Apollo](https://www.apollographql.com/), it's a GraphQL client implementation that makes it easy to bridge between a frontend app and a GraphQL server like HasuraDB.

[Vue Apollo](https://apollo.vuejs.org/) is, of course, a plugin for Vue.js that adds the Apollo client as a service that is easy to access from any Vue component.

Vue CLI makes it dead-simple to add the Vue Apollo plugin to your project:

```
$ vue add apollo

```

This will not only install the plugin but also automatically configure it!

## Clearing out boilerplate files

Before we continue, let's remove some of the boilerplate provided by Vue CLI so we're ready to build our own app.

Firstly, let's delete the `HelloWorld` component. We'll also empty out the `App.vue` component, though we won't delete it completely as we'll add new content to it soon.

```
$ rm src/components/HelloWorld.vue
$ > src/App.vue

```

In a moment we're going to set up our frontend app. Before we do, let's set up our Hasura database and add some mock data to it.

## Setting up a local Hasura database with Docker

As explained, we're going to get data via GraphQL from a Postgres database using Hasura.

To get up and running quickly, we're going to use Hasura in a local Docker environment. We can simply download the premade `docker-compose` file into our project root:

```
$ wget https://raw.githubusercontent.com/hasura/graphql-engine/stable/install-manifests/docker-compose/docker-compose.yaml

```

Then run `docker-compose up -d` to get it running.

If it worked, you should be able to access the Hasura console here: [http://localhost:8080/console](http://localhost:8080/console).

## Connecting Vue Apollo to HasuraDB

For our Vue Apollo app to access the Hasura GraphQL server, there is one bit of configuration we must provide - we must create a `.env` file and specify the URLs as follows:

_.env_

```
VUE_APP_GRAPHQL_HTTP=http://localhost:8080/v1/graphql
VUE_APP_GRAPHQL_WS=ws://localhost:8080/v1/graphql

```

## Data model

In this app, we're going to display a list of "posts" - think of Reddit, Twitter, or Instagram. Let's now create a table in our database to store these posts before we create some with mock data.

The structure of our `posts` table will be:

- `id` (Integer, auto-increment)
- `url` (Text)
- `title` (Text)
- `created_at` (Timestamp)

To create this table, go to the Hasura console. Go to the "Data" section and click on "Add Table".

Set the table name as `posts`. Create the columns listed above. Set the primary key to `id`. Finally, click "Add Table" and you'll be done.

## Adding dummy data

To add some mock data for our API, let's generate it using the Mockaroo dummy data generator at [https://mockaroo.com](https://mockaroo.com).

To use this, you need to tell it what fields you're looking for and the kind of data you want to use. Try the following:

- `id` (Row Number)
- `url` (Dummy Image URL)
- `title` (Animal Common Name)
- `created_at` (Datetime)

Now you need to tell it the kind of data you want to return. I suggest you select at least 100 rows in format `JSON`.

To enter this data into our table, we'll need it as a JavaScript object, but Mockaroo only gives us the JSON option.

No problem, though - we can convert JSON to JavaScript using an online conversion tool: [https://www.convertonline.io/convert/json-to-js](https://www.convertonline.io/convert/json-to-js)

So copy your JSON data from Mockaroo, then paste it in the JSON to JavaScript convertor.

## Inserting data

To insert the data we just created into our Hasura database, we'll use the GraphQL API explorer of the console. So, navigate your browser to [http://localhost:8080/console/api-explorer](http://localhost:8080/console/api-explorer)

Create this query:

```
mutation MyMutation {
  insert_posts(objects: []) {
    affected_rows
  }
}

```

Replace the array `[]` assigned to `objects` with the array of mock JavaScript objects above. Run the query and you should now have the mock data in the database.

## Creating posts component

Let's return now to our Vue Apollo app. To display each post we're going to create a simple component. These components will be fed the data from the Hasura - we'll see how to do that in the next section.

First, create the component:

```
$ touch src/components/Post.vue

```

Notice that we use a prop `post` to supply the data, then we use the properties `url` and `title` in the template.

_src/components/Post.vue_

```
<template>
  <div class="post">
    <div>
      <img :src="post.url" :alt="post.title"/>
    </div>
    <div>
      <h3>{{ post.title }}</h3>
    </div>
  </div>
</template>
<script>
export default {
  props: ['post']
}
</script>
<style>
  .post {
    display: flex;
    flex-direction: row;
  }
</style>

```

## Displaying posts

Next, we're going to display the posts in the app. Open up the file _src/App.vue_ (which we emptied earlier). Add a `template` section where we'll iterate the `post` component, using the `id` property as a key, and binding the `post` object to the `post` prop.

In the `script` section, we'll import and register the `Post` component. Go ahead and add a `post` data property as well, which will be an array. You can hard-code one or more of your mock data objects from the previous step to see if it works correctly. Of course, we won't need those in a moment when we load posts from the GraphQL server.

_src/App.vue_

```
<template>
  <div id="app">
    <post v-for="post in posts" :key="post.id" :post="post" />
  </div>
</template>

<script>
import Post from '@/components/Post';

export default {
  name: 'App',
  components: { Post },
  data() {
    return {
      posts: [
        {
          id: 1
          title: 'Euro Wallaby'
          url: 'http://dummyimage.com/134x161.bmp/5fa2dd/ffffff'
        }
      ]
    }
  }
}
</script>

```

If you haven't already, start your Vue CLI dev server: `npm run serve`. Since the Hasura GraphQL server is occupying localhost port 8080, Vue CLI will start on 8081.

Navigate to [http://localhost:8081](http://localhost:8081) in your browser and you'll see our basic app skeleton.

## Setting up pagination

Let's now use Apollo to make a query to grab the first "page" of our paginated posts. In the next section, we'll work on automatically grabbing the rest to properly implement the scroll load.

The first thing we'll do is create a const `pageSize` and set its value to `10` (or whatever integer you like). This defines how many posts are on one page. This can be defined outside the component state since it does not need to be reactive.

Next, we'll add a property `page` to the reactive component state. This keeps track of the current pagination page. Let's initialize to `0`.

_src/App.vue_

```
...

const pageSize = 10;

export default {
  ...
  data() {
    return {
      posts: []
      page: 0
    }
  }
}

```

## Fetching the first page of data

Now we use Apollo to query the GraphQL server and retrieve posts from the database. First, import the `graphql-tag` package. This would have been automatically added by Vue CLI.

Now we're going to create a query. First, access the `apollo` option on the component definition and assign it an object. Create a sub-property `posts` which tells Vue Apollo we want to use this query to add data to the `posts` reactive data property.

Assign an object to `posts` and give it a sub-property `query`. We'll assign to this our GraphQL query by using the `gql` package so we can define the query in a JavaScript string.

The query will be called `GetPostPages` and will have two arguments `$offset` and `$limit`. These values will be used to set the `offset` and `limit` arguments of the post object (from which we'll grab all available fields).

In Hasura, `limit` specifies the number of rows to include in the results `offset` determines where to slice the result set.

The final thing to do is declare these variables in Apollo by adding a `variables` sub-property to the `posts` object where we specify the initial values of these variables.

_src/App.vue_

```
...

import gql from 'graphql-tag';

export default {
  ...
  apollo: {
    posts: {
      query: gql`
        query GetPostPages ($offset: Int, $limit: Int) {
          posts (offset: $offset, limit: $limit, order_by: {created_at: asc}) {
            id
            url
            title,
            created_at
          }
        }
      `,
      variables: {
        offset: 0,
        limit: pageSize,
      },
    },
  }
}

```

Let the dev server rebuild your app and take a look at the page. Now the mock data in the Hasura database will be displayed on the page!

Note that you'll see the most current 10 posts since we ordered results by the `created_at` date, and we set the limit (page size) to 10.

## Fetching subsequent pages

Now we want to create a method that will fetch the next results and combine them with our existing results.

First, create a new Vue method `fetchMore`. In this step, we'll define the method, in the next step, we'll see how to call it.

In this method, we're going to increment the `page` reactive value. We'll then use the `fetchMore` method Apollo provides to each query for pagination. This method triggers a new query and combines the results with previous results. It's perfect for use cases like pagination.

`fetchMore` receives a config object as an argument. We'll give it a `variables` property where we'll provide the updated variable values to pass to the subsequent query.

`updateQuery` will tell Apollo how to store the new incoming posts with the existing posts. We're simply going to merge them all into a new array.

```
export default {
  ...
  methods: {
    fetchMore() {
      this.page++;
      this.$apollo.queries.posts.fetchMore({
        variables: { 
          offset: (page * pageSize), 
          limit: pageSize 
        },
        updateQuery: (existing, incoming) => ({
          posts: [...existing.posts, ...incoming.fetchMoreResult.posts]
        }),
      })
    },
  },
}

```

## Adding scroll load

In the last step, we defined a method for fetching more posts from the server. Now we want to call this anytime the user scrolls to the bottom of the page.

How do we know if the user has reached the bottom of the page? There are various browser APIs we can use for this, but it's easier to use the package `scrollmonitor` to do it for us.

So first, install scrollmonitor using NPM.

```
$ npm i scrollmonitor -S

```

Next, add an element with id `sensor` to the _App_ component underneath the list of posts. Scroll monitor will tell us when this element is visible on the page.

If the element is visible, it means the user has scrolled to the bottom!

_src/App.vue_

```
<template>
  <div id="app">
    <post v-for="post in posts" :key="post.id" :post="post" />
    <div id="sensor"></div>
  </div>
</template>

```

In the `App` script, import `scrollmonitor`. We'll then use scroll monitor to set up a callback that fires any time the element enters the page. The perfect place to set this up is in the `mounted` hook of the component.

How it works:

- We use a DOM query to get a reference to the sensor element
- We create an instance of scroll monitor to watch this element
- We can trigger a callback when the `enterViewport` event of scroll monitor fires. In this callback, we'll call `fetchMore` (note we only call it conditional on `posts` being defined otherwise it may call too early).

_src/App.vue_

```
...
import scrollMonitor from 'scrollmonitor';

export default {
  ...
  mounted () {
    const el = document.getElementById('sensor');
    const elementWatcher = scrollMonitor.create(el);
    elementWatcher.enterViewport(() => {
      if (this.posts) {
        this.fetchMore();
      }
    });
  }
}

```

With that in place, go see your app in the browser. The infinite scroll feature means every time you reach the bottom more items appear.

_This articles was written by [Anthony Gore](https://twitter.com/anthonygore) as part of the **Hasura Technical Writer Program**. If you would like to publish an article about Hasura or GraphQL on our blog, apply [here](https://bit.ly/3azZFjw)._

* * *

## About the Author

[Anthony Gore](https://twitter.com/anthonygore) provides short and sweet explanations of the latest development trends at [DevNow](https://devnow.buzz/).

<!--kg-card-end: markdown-->