How to automate your portfolio website [Part 2]
Tags: javascript, graphql, vue, hashnode
Published: 2020-10-15 11:48:54

---
title: How to automate your portfolio website [Part 2]
published: true
description: In this article, I'd like to share how you can automate publishing your Hashnode blogs to your portfolio website with the help of Hashnodes API, let me show you how I did it.
tags: JavaScript, GraphQL, Vuejs, Hashnode
canonical_url: https://ahmeds.tech/how-to-automate-your-portfolio-website-part-2
series: portfolio automation
cover_image: https://cdn.hashnode.com/res/hashnode/image/upload/v1602761590519/VNcGhW5_w.png?w=1600&h=840&fit=crop&crop=entropy&auto=format&q=60
---

To have less steps redacting the portfolio every time you publish a blog or push a new project to GitHub, automating it is the right choice.

In this article, I'd like to share how you can automate publishing your Hashnode blogs to your portfolio website with the help of Hashnodes API, let me show you how I did it.

# What should be achieved with this project

- Automatically display and link my projects from GitHub
- Automatically display and link my blogs from Hashnode

# Requirements

- JavaScript
- Vue.js
- vue-apollo

### The steps I took

After I finished preparing my last post about  [automating with github api](https://ahmeds.tech/how-to-automate-your-portfolio-website-part-1-1) I started playing with the Hashnode API.

#### Which looks like this

It is a GraphQL playground

![hashnode_api.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602679236811/GgsgGM4pD.png)

#### The documentation tab is next to the scroll bar

I searched for it quite a bit until I found it because it was the first time me seeing this UI

![hashnode_api_docu_tab.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602679368382/4ctgsFKM_.png)

## Lets start with a step for step explanation 

- specify a user

``` username: String! ``` the exclamation mark means it is required to enter what is specified, in this case it is a String.

``` age: Int? ``` the question mark means it is not necessary to enter what is specified, in this case it is an Int and it was just an example we only need the one above "username".

![specify_a_user.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602680921700/5jQzWfFI0.png)

1. choose publication
2. then posts
3. finally pick which data you want retrieve from the API

In our case we will need cover image, title, brief(description).

Also cuid and slug are going to be needed to dynamically link the blog card on your website to direct to the original post. We will discuss it later.

![specify_a_user's_stuff.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602683127527/VYUNhkuz1.png)

I'm using my username for demonstration, you can use whatever username you like.

```
query {
	user(username: "ahmedaltaai") {
		publication {
    	          posts {
                    coverImage
                    title
                    brief
                    slug
                    cuid
                    }
              }
	}
}
```

### This is the result after pressing the play button in the middle of  the screen

![hashnode_api_data.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602683477511/GToBE-k0I.png)

#### Well very nice we did it in the GraphQL playground but how can we do it in our code base?

Because I'm using Vue.js we will be using vue-apollo. There is also a version for React and Angular.

So we will install the npm package as a dependency with

``` npm i vue-apollo --save ```

Afterwards we will find a new js file in our projects src directory "vue-apollo.js"


![vue-apollo-js.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602684111236/HBYXfdNSZ.png)

#### Inside the vue-apollo.js file we need to modify two things
1. httpEndpoint
2. wsEndpoint (Web Socket)

Our API address link goes into the httpEndpoint and the wsEndpoint is going to be set to null.

![vue-apollo-js-modifyed.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602684410482/jUsKg5Ct_.png)

### Now we switch to the component where we will be making the call (I'm not using a state management system such as Vuex)

```
<script>
//we need to import graphql
//gql is the graphql query language

import gql from 'graphql-tag'

export default {
  name: 'blog',

//use the "apollo" object
//to query and retrieve the data

  apollo: {

//add an attribute which has the same name 
//as the field name in the query

    user: gql`
      query {
        user(username: "ahmedaltaai") {
          publication {
            posts {
              coverImage
              title
              brief
              slug
              cuid
            }
          }
        }
      }
    `
  }
}
</script>
```
Read the  [vue-apollo documentation](https://apollo.vuejs.org/guide/apollo/queries.html#name-matching)  for better understanding about name matchnig & co.

### Now we can just loop over the apollo object to display the data

We need to loop over the "posts" object which is in "publication" under "user".

``` v-for="post in user.publication.posts" :key="post.cuid" ```

#### This is how my component looks like

I will spare you the style section for the sake of the blogs length. You can still see the whole code on my github profile.

```
<section
        v-for="post in user.publication.posts"
        :key="post.cuid"
        class="card"
      >
        <a :href="`https://ahmeds.tech/${post.slug}`">
          <div class="cover-image">
            <img :src="post.coverImage" />
          </div>
          <div class="text">
            <div class="title">
              <h3>{{ post.title }}</h3>
            </div>
            <div class="description">
              <p>{{ post.brief }}</p>
            </div>
          </div>
        </a>
      </section>
```

As you see I'm using an "a" tag to make my card link to the original post if clicked.

You have the complete power to do it as you wish!

## How to modify the URL in the "a" tag

It will depend if you have your own domain or using a subdomain under hashnode.
- Own domain
- Subdomain under Hashnode

# Own domain

On  [api.hashnode.com](api.hashnode.com) when we queried for the data we requested a "slug"

> A slug is a human-readable, unique identifier, used to identify a resource instead of a less human-readable identifier like an id . You use a slug when you want to refer to an item while preserving the ability to see, at a glance, what the item is.

The value of slug will be your post title:

![own_domain.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1602688706064/yI-fuM9f4.png)

So in the "a" tag we want to make the "href" dynamic with a v-bind which I will be shortening to a colon " : " just like this: 

``` <a :href="`https://ahmeds.tech/${post.slug}`"> ```

To access the v-for loop "post" element 

I'm putting the link in backticks - which result in template literals (template string) 

> Template literals are string literals allowing embedded expressions.

So I can use "post" attribute from the v-for loop and fetch the slug to append it to the URL which will accumulate to a full link of my individual blog... the blog which is being clicked on.

``` https://ahmeds.tech/how-to-automate-your-portfolio-website-part-1-1 ```

# Subdomain under Hashnode

Identical procedure except we now also need the cuid

> The Customer user ID (CUID) field is a unique user identifier set by app and website owners.

A blogger's URL without a private domain will look like this:

``` <username>.hashnode.dev/<slug> ```

or

``` <username>.hashnode.dev/<slug>-<cuid> ```

I can't tell you why there are those two differences because I don't know. You have to find out which one suites you. If you pay more attention to the URLs when ready on Hashnode then you will notice and understand what I'm talking about here.

Replace <username> with your username on Hashnode, hard code it. And take the same procedure with the rest of the URL.

``` <a :href="`https://<username>.hashnode.dev/${post.slug}`">  ```

or

``` <a :href="`https://<username>.hashnode.dev/${post.slug}-${post.cuid}`">  ```

### Now the posts should be displaying on your website. Although there is a "long" loading time which I don't like and I think neither do you.

![withoutLoadingSkeleton.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1602694011403/gsfPSx3X1.gif)

Lets make it more professional with a loading skeleton

### Loading skeleton

![withLoadingSkeleton.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1602694308770/aa2eHx9H_.gif)

I won't go into detail on how to create or style a loading skeleton because there are plenty of tutorials which are online. Also it is an opportunity to get your googling skills on fleek :p 

Although I will tell you the challenge I had while setting the skeleton up.

#### Loading state on  [vue-apollo documentation](https://apollo.vuejs.org/guide/apollo/queries.html#loading-state) 

> You can display a loading state thanks to the $apollo.loading prop:

``` <div v-if="$apollo.loading">Loading...</div> ```

When I was implementing this function at the beginning it didn't work. It never displayed the skeleton. So I reversed the order of the ``` v-if ``` :

``` 
<div v-if="!$apollo.loading">
  <blog-card />
</div> 

<div v-else>
  <loading-skeleton />
<div>
```

In this case if it is NOT loading, as of NOT fetching any data then show me the blog card with the cover image, title and description else display loading skeleton.

# Easy Peazy

That was it I hope you learned something new 😃