How to Collect Documentation Statistics in Vuepress

Obonguko - Nov 17 '22 - - Dev Community

Collecting user statistics on documentation sites can help creators understand common user behaviors, giving them more information to make decisions and create better experiences.

In this article, we'll learn how to use Appwrite and Vuepress to collect and store the following data:

  • Likes (when a reader clicks a like button)
  • Views (when a reader views the page)

We can find the finished code for this article on GitHub here.

Prerequisites

  • Working knowledge of Javascript and Vue.js
  • Docker installation
  • An Appwrite instance; check out this article on how to set up an instance locally or via a one-click install on DigitalOcean or Gitpod

Installing Vuepress

Vuepress is a minimalistic static site generator with a Vue-powered theming system and a default theme that has been optimized for writing technical documentation.

We'll need to create a Vuepress starter project by navigating to the desired directory and running the command below in our terminal.

npx create-vuepress-site appwrite-stats && cd appwrite-stats
Enter fullscreen mode Exit fullscreen mode

The command will create a project called appwrite-stats which will interactively ask a series of questions that will be used to build our site's metadata. Let’s answer them accordingly.
Once it’s done creating the project, the second command will navigate into the project directory.

Once the installation completes, let's run the following command in the terminal:

cd docs && npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

The command navigates into the docs folder - which Vuepress creates during installation - installs all of the required dependencies, and then runs the build server at [http://localhost:8080](http://localhost:8080/).

When we visit http://localhost:8080 on our browser, we should see the following screen:

Installing Appwrite

Appwrite is an open-source end-to-end back-end server solution that enables developers to build applications faster.

To use Appwrite in our Vuepress application, we'll need to install the Appwrite SDK for web applications.

In the /appwrite-stats/docs directory on our terminal, let's run the command below to install the Appwrite SDK:

npm install appwrite
Enter fullscreen mode Exit fullscreen mode

Next, let's create a utils folder, and in it, we'll add an index.js file with the following content:

import { Client, Databases } from "appwrite";
const client = new Client()
  .setEndpoint("API_ENDPOINT") // Your API Endpoint
  .setProject("PROJECT_ID"); // Your project ID
export const database = new Databases(client);
Enter fullscreen mode Exit fullscreen mode

Don't worry about the dummy values, we will fill them in later sections of this tutorial.

Setting up an Appwrite Database

First, let's ensure that the Docker desktop is running our local Appwrite instance:

Once the Appwrite instance is verified, let's go ahead to open localhost:80 (our specified port during Appwrite installation) in the browser to sign into Appwrite's console:

On the console, click the Create Project button, enter appwrite-stats as the name, and click on Create.

On the Settings tab of the Home page of our newly created project, let's take note of our Project ID and API Endpoint.

Creating an Appwrite Collection and Attributes

On the left side of the Appwrite Console dashboard, click on the Database tab. Click on the Add Database button to create a new database. Creating a new database will lead us to the Collection page.


Next, we'll create a collection in our database tab by clicking the Add Collection button. After creating a Collection, go to the Permissions section on the Settings page. We want to assign Read and Update Access with a Role value of any. We can customize these roles later to specify who has access to our database. We'll click on Update to save our changes.

Next, let's go to the Attributes tab on our collection to create the properties we want our document to have:

Let's create integer attributes of viewsCount and likesCount and set their default values to zero.

Creating an Appwrite Document

After creating the attributes, let's head to the Documents tab of our database.

Click on the Add Document button to create a new document.

We had already specified default values of zero for our attributes earlier, so we can click the Create button to create the document.

Let's also note and copy our Document ID, Collection ID, and Database ID, which we'll use to perform functions to update the values depending on the user action.

Building Components in Vuepress

Vuepress pages are written primarily in markdown format; however, being built with Vue.js, Vuepress lets us create and embed Vue.js components in our markdown pages.

Building a Likes Component
We'll build a "likes" component on our site's pages.
Users could click on the component to like a specific page and view the page's total number of likes.

To get started, let's navigate to the /src/.vuepress/components folder in our project directory, create a file named likes-component.vue, and paste in the following code:

<template>
        <div class="like-container">
            <button @click="likePage">
                <svg
                    xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 24 24"
                    width="30"
                    height="30"
                >
                    <path fill="none" d="M0 0H24V24H0z" />
                    <path
                        d="M12.001 4.529c2.349-2.109 5.979-2.039 8.242.228 2.262 2.268 2.34 5.88.236 8.236l-8.48 8.492-8.478-8.492c-2.104-2.356-2.025-5.974.236-8.236 2.265-2.264 5.888-2.34 8.244-.228zm6.826 1.641c-1.5-1.502-3.92-1.563-5.49-.153l-1.335 1.198-1.336-1.197c-1.575-1.412-3.99-1.35-5.494.154-1.49 1.49-1.565 3.875-.192 5.451L12 18.654l7.02-7.03c1.374-1.577 1.299-3.959-.193-5.454z"
                    />
                </svg>
            </button>
            <p>{{ numberOfLikes }} likes</p>
        </div>
    </template>
    <script>
    import { database } from "../../utils/index.js";
    export default {
        data() {
            return {
                numberOfLikes: 0,
            };
        },
        mounted() {
            this.getLikesCount();
        },
        methods: {
            async likePage() {
                this.numberOfLikes++;
                try {
                    await database.updateDocument(
                        '[DATABASE_ID]',
                        '[COLLECTION_ID]',
                        '[DOCUMENT_ID]',
                        {
                            likesCount: this.numberOfLikes,
                        }
                    );
                    this.getLikesCount();
                } catch (error) {
                    console.log(error);
                }
            },
            async getLikesCount() {
                try {
                    let promise = await database.listDocuments(
                        "[DATABASE_ID]",
                        "[COLLECTION_ID]"
                    );
                    this.numberOfLikes = promise.documents[0].likesCount;
                } catch (error) {
                    console.log(error);
                }
            },
        },
    };
    </script>
    <style lang="css">
    .like-container {
        display: flex;
        align-items: center;
        gap: 10px;
    }
    button {
        cursor: pointer;
    }
    p {
        font-size: 20px;
    }
    </style>
Enter fullscreen mode Exit fullscreen mode

The code snippet above does the following:

  • Imports the created database instance from the /utils/index.js file.
  • The getLikes method gets invoked during the page load using the mounted lifecycle hook, the method gets invoked; the total number of existing likes is fetched from our Appwrite database and assigned to the numberOfLikes data variable.
  • When a user clicks on the button, the @click= "likePage" event handler invokes the likePage method, increments the number of likes, and stores the data on our database. Once done, the getLikesCount method gets invoked to get the current number of likes and store it in the numberOfLikes variable so that when a user refreshes the page, the data persists.

Building a Number-of-Views Component
We'll build a views-count component on our site's pages. Users can see the total number of page views a particular page has.

In the /src/.vuepress/components, create a views-component.vue file, and paste in the following code snippet:

   <template>
        <div class="views-container">
            <p>{{ numberOfViews }} Views</p>
        </div>
    </template>

    <script>
    import { database } from "../../utils/index.js";
    export default {
        data() {
            return {
                numberOfViews: 0,
            };
        },
        mounted() {
            this.addToViewCount();
        },
        methods: {
            async addToViewCount() {
                try {
                    await this.getViewCount();
                    this.numberOfViews++;
                    await database.updateDocument(
                        "[DATABASE_ID]",
                        "[COLLECTION_ID]",
                        "[DOCUMENT_ID]",
                        {
                            viewsCount: this.numberOfViews,
                        }
                    );
                } catch (error) {
                    console.log(error);
                }
            },
            async getViewCount() {
                try {
                    let promise = await database.listDocuments(
                        "[DATABASE_ID]",
                        "[COLLECTION_ID]"
                    );
                    this.numberOfViews = promise.documents[0].viewsCount;
                } catch (error) {
                    console.log(error);
                }
            },
        },
    };
    </script>

    <style lang="css">
    .views-container {
        font-size: 20px;
    }
    </style>
Enter fullscreen mode Exit fullscreen mode

Let's analyze what the code snippet above does:

  • It creates an HTML template with a p tag that displays the number of views.
  • It imports our Appwrite database instance from the ../../utils/index.js file.
  • As their naming suggests, these two methods: getViewCount and addToViewCount, get the total number of page views and increment the total number of page views from our Appwrite database.
  • When the page first loads, using the mounted lifecycle hook, the addToViewCount method invokes the getViewCount method, and the total number of existing views is fetched from our Appwrite database and assigned to the numberOfViews data variable. Once that's done, the numberOfViews variable is incremented by one and then stored on our Appwrite database.

Using the Components in Our Markdown Files

Now, we've finished creating our components and can use them on any of our desired pages.

Let's use it in the using-vue.md file located in the /src/guide directory.

At the bottom of the page, we’ll add the following code snippet:

...
### Enjoyed this page? Give it a like:
<likes-component/>
### This page has:
<views-component/>
Enter fullscreen mode Exit fullscreen mode

On the browser, let's go to the http://localhost:8080/guide/using-vue.html page. Let's see the result of what we've got so far:

As we can see, when we click the like button, it increments the count, and when we leave to another page and back, the number of page views also increases.

Conclusion

This article discussed how to use Appwrite's database functionality and, more importantly, how to collect documentation statistics using Vuepress.

Resources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .