• Build a blog with Nuxt (vue.js), Strapi and Apollo
  • Maxime Castres
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: vitoxli
  • Proofread by: Jessica

Build the blog using Nuxt (vue.js), Strapi, and Apollo

introduce

A few weeks ago, I pondered my Internet habits, specifically what I like to read when I’m relaxed. What I usually do is do a search and then go to the links that interest me the most. It turned out THAT I was reading articles about other people’s life experiences all the time, and that was a far cry from what I originally searched for!

Blogs are great for sharing experiences, ideas or testimonials. Strapi makes it easy to create a blog! So, you must have guessed what this article is about. Let’s learn how to create a blog using Strapi.

The target

If you follow our blog, you should have already learned how to use Gatsby to create a blog. But what if you switch to another language? Today we are going to learn how to create a blog using vue.js.

The goal of this article is to create a blog site that uses Strapi as a back end, Nuxt as a front end, and Apollo to request the Strapi API through GraphQL.

You can download the source code at GitHub: github.com/strapi/stra…

The preparatory work

To follow this tutorial, you’ll need to have Strapi and Nuxt installed on your computer, but don’t worry, let’s install them together!

This tutorial uses Strapi V3.0.0-beta.17.5.

Make sure you have Node V.12 installed.

The installation

Create a folder called blog-strAPI and jump to it!

  • mkdir blog-strapi && cd blog-strapi

Install the back-end

This part is easy because there’s a great package called Create Strapi-app in Beta-9 that allows you to create a StrAPI project in seconds without having to install StrAPI globally, so let’s give it a try.

(In this tutorial, we will use YARN as a package management tool)

  • yarn create strapi-app backend --quickstart --no-run.

This command line creates everything you need for the back end. Remember to add –no-run, because it will prevent the application from automatically starting the service, and we did it because we need to install some nice Strapi plugins.

Now that you know we need to install some plugins to enhance our application, let’s install the popular GraphQL plugin:

  • yarn strapi install graphql

Once installed, you can start the Strapi service and create your first administrator account via Strapi Dev. This account has all permissions for the application, so choose a password that is not secure, like password123.

Strapi runs at http://localhost:1337

Very good! Now that Strapi is in place, we can start creating the Nuxt application.

Install the front

Now that the easy part is done, let’s develop our blog!

Install Nuxt

Create the Nuxt front-end service with the following command:

  • yarn create nuxt-app frontend

Note: The terminal will prompt for some details about the project. This information is not relevant to our blog, so we can ignore it. Still, I strongly recommend that you read the official documentation. Let’s go ahead and just keep pressing Enter!

Again, after the installation is complete, you can start the front-end application to ensure smooth progress.

cd frontend  
yarn dev
Copy the code

You might want someone to read your blog or you might want your blog to be “cute and nice”, we’ll use the popular CSS framework UiKit to set styles and use Apollo to query Strapi via GraphQL.

Install dependencies

Make sure you are in the frontend folder before running the following command:

Install the Apollo,

  • yarn add @nuxtjs/apollo graphql

Modules and Apollo must be set up in nuxt.config.js.

  • innuxt.config.jsAdd the following modules and Apollo configuration to:

/frontend/nuxt.config.js

. modules: ['@nuxtjs/apollo',].apollo: {  
  clientConfigs: {
    default: {
      httpEndpoint: process.env.BACKEND_URL || "http://localhost:1337/graphql"}}},...Copy the code

Since we already installed the GraphQL plug-in when we installed the back end, we don’t need to install it again. This approach makes projects more consistent).

Install Uilkit

UIkit is a lightweight modular front-end framework for developing fast and powerful Web interfaces.

  • yarn add uikit

Now, you need to initialize UIkit Js in the Nuxt application by creating a plug-in.

  • create/frontend/plugins/uikit.jsFile and copy/paste the following code:
import Vue from 'vue'

import UIkit from 'uikit/dist/js/uikit-core'  
import Icons from 'uikit/dist/js/uikit-icons'

UIkit.use(Icons)  
UIkit.container = '#__nuxt'

Vue.prototype.$uikit = UIkit  
Copy the code
  • Add the following part in your nuxt.config.js file
. css: [ 'uikit/dist/css/uikit.min.css', 'uikit/dist/css/uikit.css', '@assets/css/main.css' ], /* ** Plugins to load before mounting the App */ plugins: [ { src: '~/plugins/uikit.js', ssr: false } ], ...Copy the code

As you can see, we configured both UIkit and main.css! Now we need to create the main.css file.

a {  
  text-decoration: none;
}

h1  {  
  font-family: Staatliches;
  font-size: 120px;
}

#category {
   font-family: Staatliches;
   font-weight: 500;
}

#title {
  letter-spacing:.4px;
  font-size: 22px;
  font-size: 1.375 rem;
  line-height: 1.13636;
}

#banner {
  margin: 20px;
  height: 800px;
}

#editor {
  font-size: 16px;
  font-size: 1rem;
  line-height: 1.75;
}

.uk-navbar-container {
  background: #fff ! important;
  font-family: Staatliches;
}

img:hover {  
  opacity: 1;
  transition: opacity 0.25 s cubic-bezier(0.39, 0.575, 0.565, 1);
}
Copy the code

Note: you do not need to understand the contents of this file. Just some styles;)

Let’s add beautiful fonts (Staatliches) to the project!

  • Add the following objects tonuxt.config.jsIn the filelinkIn the array
link: [  
      { rel: 'stylesheet'.href: 'https://fonts.googleapis.com/css?family=Staatliches'}]Copy the code

Perfect! Restart the service and get ready to be amazed by your app’s front end.

Design data structure

Finally! We’ll build the article’s data structure by creating the article content type:

  • Look in your StrAPI admin panel and click in the sidebarContent Type Builder

  • Click on theAdd A Content TypeAnd name itarticle

Now you will create all the fields for your content type:

  • Create the following fields:
    • title:StringType (mandatory)
    • content:Rich TextType (mandatory)
    • image:MediaType (mandatory)
    • published_at:DateType (mandatory)

Click Save! Your first content type is now created. You might want to create your first article right now, but before you do, there’s one more thing to do: open up your article content type permissions

  • Click on theRoles & PermissionThen selectpublic.
  • Of the selected articlefindfindoneSelect and save.

Great! Now you’re ready to create your first article and get it in GraphQL Playground.

  • There’s more to creating your first article!

Examples are as follows

Great! Now, you might want to actually get the article through the API!

  • Go to http://localhost:1337/articles

Isn’t that great! You can also use GraphQL Playground to try to get articles

To create a classification

You may want to set up a category for articles (News, Trends, Opinions). You will do this by creating another content type in StrAPI.

  • Create one with the following fieldscategoryContent type
    • name:Stringtype

Click Save!

  • inArticleCreated in the content typeRelationThe new fields, as shown in the picture below,There are many articles under one category.

  • Click on theRoles & PermissionAnd click thepublic. Selective classificationfindfindoneSelect and save.

You can now select a category for the article in the right sidebar.

Now that we’re familiar with Strapi, let’s get to the front end!

Create a layout for your application

Nuxt stores the default layout in the layouts/default.vue file. Let’s make it our own!

<template>  
  <div>

    <nav class="uk-navbar-container" uk-navbar>
        <div class="uk-navbar-left">

          <ul class="uk-navbar-nav">
              <li><a href="#modal-full" uk-toggle><span uk-icon="icon: table"></span></a></li>
              <li>
                <a href="/">Strapi Blog
                </a>
              </li>
          </ul>

        </div>

        <div class="uk-navbar-right">
          <ul class="uk-navbar-nav">
              <! -- <li v-for="category in categories"> <router-link :to="{ name: 'categories-id', params: { id: category.id }}" tag="a">{{ category.name }} </router-link> </li> -->
          </ul>
        </div>
    </nav>

    <div id="modal-full" class="uk-modal-full" uk-modal>
        <div class="uk-modal-dialog">
            <button class="uk-modal-close-full uk-close-large" type="button" uk-close></button>
            <div class="uk-grid-collapse uk-child-width-1-2@s uk-flex-middle" uk-grid>
                <div class="uk-background-cover" style="background-image: The url (' https://images.unsplash.com/photo-1493612276216-ee3925520721?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&f it=crop&w=3308&q=80 3308w');" uk-height-viewport></div>
                <div class="uk-padding-large">
                    <h1 style="font-family: Staatliches;">Strapi blog</h1>
                    <div class="uk-width-1-2@s">
                        <ul class="uk-nav-primary uk-nav-parent-icon" uk-nav>
                          <! -- <li v-for="category in categories"> <router-link class="uk-modal-close" :to="{ name: 'categories-id', params: { id: category.id }}" tag="a">{{ category.name }} </router-link> </li> -->
                        </ul>
                    </div>
                    <p class="uk-text-light">Built with strapi</p>
                </div>
            </div>
        </div>
    </div>

    <nuxt />
  </div>
</template>

<script>

export default{}</script>
Copy the code

As you can see, two pieces of code are commented out.

  <! -- <li v-for="category in categories"> <router-link :to="{ name: 'categories-id', params: { id: category.id }}" tag="a">{{ category.name }} </router-link> </li> -->.<! -- <li v-for="category in categories"> <router-link class="uk-modal-close" :to="{ name: 'categories-id', params: { id: category.id }}" tag="a">{{ category.name }} </router-link> </li> -->

Copy the code

In fact, you want to be able to list every category in the navigation bar. To do this, we need to use Apollo to get them, let’s write the query!

  • createapollo/queries/categoryFolder and create in itcategories.gqlThe content of the document is as follows:
query Categories {  
  categories {
    id
    name
  }
}
Copy the code
  • Uncomment and replace with the following codedefault.vueIn the filescriptThe contents of the tag.
<script>  
import categoriesQuery from '~/apollo/queries/category/categories'

export default {  
  data() {
    return {
      categories: [].}},apollo: {
    categories: {
      prefetch: true.query: categoriesQuery
    }
  }
}

</script>  
Copy the code

Note that the current code is not designed to show many categories, so you may encounter some UI issues. And this article should be short, so you can improve the code yourself by lazy loading, etc.

For now, links don’t work, we’ll deal with that later in the tutorial;)

Creating the article component

This component will display all your articles on separate pages, so listing them in one component is not a bad idea.

  • createcomponents/Articles.vueThe file contains the following contents:
<template>  
  <div>

    <div class="uk-child-width-1-2" uk-grid>
        <div>
          <router-link v-for="article in leftArticles" :to="{ name: 'articles-id', params: {id: article.id} }" class="uk-link-reset">
            <div class="uk-card uk-card-muted">
                 <div v-if="article.image" class="uk-card-media-top">
                     <img :src="'http://localhost:1337' + article.image.url" alt="" height="100">
                 </div>
                 <div class="uk-card-body">
                   <p id="category" v-if="article.category" class="uk-text-uppercase">{{ article.category.name }}</p>
                   <p id="title" class="uk-text-large">{{ article.title }}</p>
                 </div>
             </div>
         </router-link>

        </div>
        <div>
          <div class="uk-child-width-1-2@m uk-grid-match" uk-grid>
            <router-link v-for="article in rightArticles" :to="{ name: 'articles-id', params: {id: article.id} }" class="uk-link-reset">
              <div class="uk-card uk-card-muted">
                   <div v-if="article.image" class="uk-card-media-top">
                       <img :src="'http://localhost:1337/' + article.image.url" alt="" height="100">
                   </div>
                   <div class="uk-card-body">
                     <p id="category" v-if="article.category" class="uk-text-uppercase">{{ article.category.name }}</p>
                     <p id="title" class="uk-text-large">{{ article.title }}</p>
                   </div>
               </div>
             </router-link>
          </div>

        </div>
    </div>

  </div>
</template>

<script>  
import articlesQuery from '~/apollo/queries/article/articles'

export default {  
  props: {
    articles: Array
  },
  computed: {
    leftArticlesCount(){
      return Math.ceil(this.articles.length / 5)
    },
    leftArticles(){
      return this.articles.slice(0.this.leftArticlesCount)
    },
    rightArticles(){
      return this.articles.slice(this.leftArticlesCount, this.articles.length)
    }
  }
}
</script>  
Copy the code

As you can see, thanks to the GraphQL query, you can get the article, let’s write it!

  • To create aapollo/queries/article/articles.gqlThe file contains the following contents:
query Articles {  
  articles {
    id
    title
    content
    image {
      url
    }
    category{
      name
    }
  }
}
Copy the code

That’s great! Now you can create your home page.

Index page

Let’s use the new component to list every article on the index page!

  • updatepages/index.vueCode in the file:
<template>  
  <div>

    <div class="uk-section">
      <div class="uk-container uk-container-large">
        <h1>Strapi blog</h1>

        <Articles :articles="articles"></Articles>

      </div>
    </div>

  </div>
</template>

<script>  
import articlesQuery from '~/apollo/queries/article/articles'  
import Articles from '~/components/Articles'

export default {  
  data() {
    return {
      articles: [].}},components: {
    Articles
  },
  apollo: {
    articles: {
      prefetch: true.query: articlesQuery,
      variables () {
        return { id: parseInt(this.$route.params.id) }
      }
    }
  }
}
</script>
Copy the code

That’s great! Now you can actually get the article through the GraphQL API!

The article page

If you click on the article, there’s nothing there right now. Let’s create the article page together!

  • createpages/articlesFolder and create in it_id.vueThe file code is as follows:
<template>  
  <div>

      <div v-if="article.image" id="banner" class="uk-height-small uk-flex uk-flex-center uk-flex-middle uk-background-cover uk-light uk-padding" :data-src="'http://localhost:1337' + article.image.url" uk-img>
        <h1>{{ article.title }}</h1>
      </div>

      <div class="uk-section">
        <div class="uk-container uk-container-small">
            <div v-if="article.content" id="editor">{{ article.content }}</div>
            <p v-if="article.published_at">{{ moment(article.published_at).format("MMM Do YY") }}</p>
        </div>
      </div>

  </div>
</template>

<script>  
import articleQuery from '~/apollo/queries/article/article'  
var moment = require('moment')

export default {  
  data() {
    return {
      article: {},
      moment: moment,
    }
  },
  apollo: {
    article: {
      prefetch: true.query: articleQuery,
      variables () {
        return { id: parseInt(this.$route.params.id) }
      }
    }
  }
}
</script>  
Copy the code

Just get an article here and let’s write a query!

  • createapollo/queries/article/article.gqlContains the following code:
query Articles($id: ID!). { article(id:$id) {
    id
    title
    content
    image {
      url
    }
    published_at
  }
}

Copy the code

Okay, so you might want to display blog content in Markdown syntax?

  • throughyarn add @nuxtjs/markdownitThe installationmarkdownit.
  • Add it tonuxt.config.jsFile in the module and add the configuration of the Mardownit object below:
. modules: ['@nuxtjs/apollo'.'@nuxtjs/markdownit'].markdownit: {  
    preset: 'default'.linkify: true.breaks: true.injected: true},...Copy the code
  • By replacing the code responsible for displaying the content_id.vueThe contents of the file.
.<div v-if="article.content" id="editor" v-html="$md.render(article.content)"></div>.Copy the code

classification

Now let’s create a page for each category!

  • createpages/categoriesFolder and create in it_id.vueFile that contains the following code:
<template>  
  <div>

    <client-only>
    <div class="uk-section">
      <div class="uk-container uk-container-large">
        <h1>{{ category.name }}</h1>

        <Articles :articles="category.articles || []"></Articles>

      </div>
    </div>
  </client-only>
  </div>
</template>

<script>  
import articlesQuery from '~/apollo/queries/article/articles-categories'  
import Articles from '~/components/Articles'

export default {  
  data() {
    return {
      category: []}},components: {
    Articles
  },
  apollo: {
    category: {
      prefetch: true.query: articlesQuery,
      variables () {
        return { id: parseInt(this.$route.params.id) }
      }
    }
  }
}
</script>  
Copy the code

Don’t forget to write queries!

  • createapollo/queries/article/articles-categoriesContains the following contents:
query Category($id: ID!). { category(id:$id) {
    name
    articles {
         id
      title
      content
      image {
        url
      }
      category {
        id
        name
      }
    }
  }
}
Copy the code

That’s great! Navigation is now possible by category 🙂

conclusion

Congratulations, you have successfully completed this tutorial. Hope you like it!

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.