text: If you’re looking to start a quick blog today, my recommendation is to go straight to WordPress.
  But what if you’re a media powerhouse and you want to deliver your content as fast as possible to multiple devices? You’ll probably also need to integrate your content with ads and other third-party services. Well, you could do that with WordPress, but you’ll come across a few problems with that platform.
  JavaScript offers superior performance to PHP in browser loading tests. In addition, modern JavaScript and its ecosystem provides a far more pleasant development experience when it comes to building new web experiences fast.
  Want to learn Vue.js from the ground up? This article is an extract from our Premium library. Get an entire collection of Vue books covering fundamentals, projects, tips and tools & more with SitePoint Premium. Join now for just $9/month.
  So there’s been a growth of headless CMS solutions — which are simply back ends for managing content. With this approach, developers can focus on building fast and interactive front ends using a JavaScript framework of their choice. Customizing a JavaScript-powered front end is much easier than making changes on a WordPress site.
  GraphCMS differs from most Headless CMS platforms in that, instead of delivering content via REST, it does so via GraphQL. This new technology is superior to REST, as it allows us to construct queries that touch on data belonging to multiple models in a single request.
  Consider the following model schema:
  Comment
  The above models have a one(Post)-to-many(Comments) relationship. Let’s see how we can fetch a single Post record attached with all linked Comment records.
  If the data is in a relational database, you have to construct either one inefficient SLQ statement, or two SQL statements for fetching the data cleanly. If the data is stored in a NoSQL database, you can use a modern ORM like Vuex ORM to fetch the data easily for you, like this:
  const post = Post.query() .with('comments') .find(1);
  Quite simple! You can easily pass this data via REST to the intended client. But here’s the problem: whenever the data requirement changes at the client end, you’ll be forced to go back to your back-end code to either update your existing API endpoint, or create a new one that provides the required data set. This back and forth process is tiring and repetitive.
  What if, at the client level, you could just ask for the data you need and the back end will provide it for you, without you doing extra work? Well, that’s what GraphQL is for.
  Before we begin, I’d like to note that this is a guide for intermediate to advanced users. I won’t be going over the basics, but rather will show you how to quickly build a Vue.js blog using GraphCMS as the back end. You’ll need to be proficient in the following areas:
  That’s all you need to know to get started with this tutorial. Also, a background in using REST will be great, as I’ll be referencing this a lot. If you’d like a refresher, this article might help: “REST 2.0 Is Here and Its Name Is GraphQL”.
  We’ll build a very simple blog application with a basic comment system. Below are the links you can visit to check out the completed project:
  Please note that a READ-ONLY token has been used in the demo and consequently the comments system won’t work. You’ll need to supply your OPEN permission token and endpoint as per the instructions in this tutorial for it to work.
  Head over to the GraphCMS website and click the “Start Building for Free” button. You’ll be taken to their signup page.
  Sign up using your preferred method. Once you’ve completed the account authentication and verification process, you should be able to access the main dashboard.
  In the above example, I’ve already created a project called “BlogDB”. Go ahead and create a new one, and call it whatever you want. After you’ve entered the name, you can leave the rest of the fields in their defaults. Click Create and you’ll be taken to their project plan.
  For the purposes of this tutorial, select the free Developer plan then click Continue. You’ll be taken to the project’s dashboard, which looks something like this:
  Go to the Schema tab. We’re going to create the following models, each with the following fields:
  Category
  Comment
  Use the Create Model button to create models. On the right side, you should find a hidden panel for Fields, which is activated by clicking the Fields button. Drag the appropriate field type onto the model’s panel. You will be presented with a form to fill in your field’s attributes. Do note at the bottom there’s a pink button labeled Advanced. Clicking it will expand the panel to give you more field attributes you can enable.
  Next, you’ll need to add the relationship between models as follows:
  Use the Reference field to define this relationship. You can add this field to any side; GraphCMS will automatically create the opposite relation field in the referenced model. When you’ve completed defining the models, you should have something like this:
  You’ve now completed the first part. Let’s now provide some data to our models.
  To add content to your models, you can simply click the Content tab in the project dashboard where you can create new records for each of your models. However, if you find this to be a slow method, you’ll be happy to know that I’ve created a GraphCMS migration tool that copies data from CSV files and uploads them to your GraphCMS database. You can find the project here in this GitHub repository. To start using the project, simply download it into your workspace like this:
  git clone [email protected]:sitepoint-editors/graphcsms-data-migration.git cd graphcsms-data-migration npm install
  Next, you’ll need to grab your GraphCMS project’s API endpoint and token from the dashboard’s Settings page. You’ll need to create a new token. For the permission level, use OPEN, as this will allow the tool to perform READ and WRITE operations on your GraphCMS database. Create a file called
  .env and put it at the root of the project:
  ENDPOINT=<Put api endpoint here> TOKEN=<Put token with OPEN permission here>
  Next, you may need to populate the CSV files in the data folder with your own. Here’s some sample data that has been used:
  // Categories.csv name Featured Food Fashion Beauty // Posts.csv title,slug,content,categories Food Post 1,food-post-1,Breeze through Thanksgiving by making this Instant Pot orange cranberry sauce,Food|Featured Food Post 2,food-post-2,This is my second food post,Food Food Post 3,food-post-3,This is my last and final food post,Food Fashion Post 1,fashion-post-1,This is truly my very first fashion post,Fashion|Featured Fashion Post 2,fashion-post-2,This is my second fashion post,Fashion Fashion Post 3,fashion-post-3,This is my last and final fashion post,Fashion Beauty Post 1,Beauty-post-1,This is truly my very first Beauty post,Beauty|Featured Beauty Post 2,Beauty-post-2,This is my second beauty post,Beauty
  You can change the content if you want. Make sure not to touch the top row, as otherwise you’ll change the field names. Please note, for the column
  categories, I’ve used the pipe
  | character as a delimiter.
  To upload the CSV data to your GraphCMS database, execute the following commands in this order:
  npm run categories npm run posts
  Each script will print out records that have uploaded successfully. The reason we uploaded
  categories first is so that the
  posts records can link successfully to existing
  category records.
  If you want to clean out your database, you can run the following command:
  npm run reset
  This script will delete all your model’s contents. You’ll get a report indicating how many records were deleted for each model.
  I hope you find the tool handy. Go back to the dashboard to confirm that data for the
  Posts and
  Categories have successfully been uploaded.
  With the back end taken care of, let’s start building our front-end blog interface.
  As mentioned earlier, we are going to build a very simple blog application powered by a GraphCMS database back end. Launch a terminal and navigate to your workspace.
  If you haven’t got Vue CLI installed, do that now:
  npm install -g @vue/cli
  Then create a new project:
  vue create vue-graphcms
  Choose to manually select features, then select the following options:
  Once the project creation process is complete, change into the project directory and install the following dependencies:
  npm install bootstrap-vue axios
  To set up
  Bootstrap-Vue in our project, simply open
  src/main.js and add the following code:
  import BootstrapVue from "bootstrap-vue"; import "bootstrap/dist/css/bootstrap.css"; import "bootstrap-vue/dist/bootstrap-vue.css"; Vue.config.productionTip = false; Vue.use(BootstrapVue);
  Next, we need to start laying down our project structure. In the
  src/components folder, delete the existing files and create these new ones:
  CommentForm.vue
  CommentList.vue
  Post.vue
  PostList.vue
  In the
  src/views folder, delete
  About.vue and create a new file called
  PostView.vue. As seen from the demo, we’ll have several category pages each displaying a list of posts filtered by category. Technically, there will only be one page that will display a different list of posts based on an active route name. The
  PostList component will filter posts based on the current route.
  Let’s first set up the routes. Open
  src/router.js and replace the existing code with this:
  import Vue from "vue"; import Router from "vue-router"; import Home from "./views/Home.vue"; import Post from "./views/PostView.vue"; Vue.use(Router); export default new Router({ mode: "history", base: process.env.BASE_URL, linkActiveClass: "active", routes: [ { path: "/", name: "Featured", component: Home }, { path: "/food", name: "Food", component: Home }, { path: "/fashion", name: "Fashion", component: Home }, { path: "/beauty", name: "Beauty", component: Home }, { path: "/post/:slug", name: "Post", component: Post } ] });
  Now that we have our routes, let’s set up our navigation menu. Open
  src/App.vue and replace the existing code with this:
  <template> <div id="app"> <b-navbar <b-navbar-toggle</b-navbar-toggle> <b-navbar-brandGraphCMS Vue</b-navbar-brand> <b-collapse is-nav <b-navbar-nav> <router-linkFood</router-link> <router-linkFashion</router-link> <router-linkBeauty</router-link> </b-navbar-nav> </b-collapse> </b-navbar> <b-container> <router-view/> </b-container> </div> </template>
  This will add a nav bar to the top of our site with links to our different categories.
  Save the file and update the following files accordingly:
  src/views/Home.vue
  <template> <div class="home"> <PostList /> </div> </template> <script> import PostList from "@/components/PostList.vue"; export default { name: "home", components: { PostList } }; </script>
  src/components/PostList.vue
  <template> <section class="post-list"> <h1>{{ category }} Articles</h1> <hr/> <p>Put list of posts here!</p> </section> </template> <script> export default { name: "PostList", data() { return { category: "" }; }, created() { this.category = this.$route.name; }, watch: { $route() { this.category = this.$route.name; } } }; </script>
  Notice that, in the
  PostList component, we’re using a custom watcher to update our
  category data property, based on our current URL.
  Now we’re ready to perform a quick test to confirm the routes are working. Spin up the Vue.js server using the command
  npm run serve. Open a browser at localhost:8080 and test each navigation link. The
  category property should output the same value we defined in route name’s attribute.
  Now that we have our routing code working, let’s see how we can pull information from our GraphCMS back end. At the root of your project, create an
  env.local file and populate it with values for the following fields:
  VUE_APP_ENDPOINT= VUE_APP_TOKEN=
  Do note that Vue.js single-page applications only load custom environment variables starting with
  VUE_APP. You can find the API endpoint and token from your GraphCMS dashboard settings page. For the token, make sure to create one with OPEN permission, as that will allow both READ and WRITE operations. Next, create the file
  src/graphcms.js and copy the following code:
  import axios from "axios"; export const ENDPOINT = process.env.VUE_APP_ENDPOINT; const TOKEN = process.env.VUE_APP_TOKEN; const headers = { "Content-Type": "application/json", Authorization: `Bearer ${TOKEN}` }; export const apiClient = axios.create({ headers }); export const POSTS_BY_CATEGORY_QUERY = ` query PostsByCategory($category: String!){ category(where: { name: $category } ){ name, posts { id slug title content categories { name } } } } `; export const POST_BY_SLUG_QUERY = ` query PostBySlug($slug: String!){ post(where: { slug: $slug }) { id title content categories { name } comments { name message } } } `; export const CREATE_COMMENT_MUTATION = ` mutation CreateComment($post: PostWhereUniqueInput!, $name: String!, $message: String!){ createComment(data: { name: $name, message: $message, post: { connect: $post }, status: PUBLISHED }) { id name message } } `;
  This helper file we just created provides two main functions:
  You can also use the API explorer in your project dashboard to test out these queries and mutations. To do this, copy the query or mutation from the code above and paste it into the top window of the API explorer. Enter any query variables in the window below that, then hit the Play button. You should see the results in a new pane on the right.
  Here’s a query example:
  Here’s a mutation example:
  Now, let’s create our HTML template in our
  src/components/PostList.vue that will display a list of posts in a neat way. We’ll also add the axios code that will pull in
  posts data from our GraphCMS database:
  <template> <section class="post-list"> <h1>{{ category }} Articles</h1> <hr/> <b-row <b-col <div class="lds-dual-ring"></div> </b-col> </b-row> <div v- <b-card <p class="card-text"> {{ post.content }} </p> <router-link Read Post </router-link> </b-card> </div> </section> </template> <script> import { ENDPOINT, apiClient, POSTS_BY_CATEGORY_QUERY } from "../graphcms.js"; export default { name: "PostList", data() { return { category: "", loading: false, posts: [] }; }, methods: { async fetchPosts() { try { this.loading = true; const response = await apiClient.post(ENDPOINT, { query: POSTS_BY_CATEGORY_QUERY, variables: { category: this.category } }); const body = await response.data.data; this.posts = await body.category.posts; this.loading = false; } catch (error) { console.log(error); } } }, created() { this.category = this.$route.name; this.fetchPosts(); }, watch: { $route() { this.category = this.$route.name; this.posts = []; this.fetchPosts(); } } }; </script> <style> h1{ margin-top: 25px !important; } .lds-dual-ring { display: inline-block; width: 64px; height: 64px; } .lds-dual-ring:after { content: " "; display: block; width: 46px; height: 46px; margin: 1px; border-radius: 50%; border: 5px solid #ccc; border-color: #ccc transparent #ccc transparent; animation: lds-dual-ring 1.2s linear infinite; } @keyframes lds-dual-ring { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style>
  Let’s quickly go through the code’s main features:
  fetchPosts()function is called from within the
  createdlifecycle hook.
  fetchPosts()function is called each time.
  After making those changes, you should now have the following view:
  Make sure the top main navigation is working as expected. Let’s now work on the
  Post component. It will have its own
  fetchPost() function, where it will query by
  slug. If you’re wondering where the
  slug parameter is coming from, let me remind you of this bit of code we put in
  router.js:
  //... { path: '/post/:slug', name: 'Post', component: Post }, //...
  This states that anything that comes after
  /post/ in the URL is available to us in the component as
  this.$route.params.slug.
  The
  post component is a parent of the
  CommentForm and
  CommentList components. The
  CommentList component from the Posts record. Let’s insert code for
  src/components/CommentList.vue now:
  <template> <section class="comment-list"> <hr/> <h4 class="text-muted">Comments</h4> <b-card <p class="card-text text-muted">{{ comment.message }} </p> </b-card> <p v-No comments posted yet!</p> </section> </template> <script> export default { name: "CommentsList", props: ["comments"] }; </script>
  Unless you’ve manually entered comments via the GraphCMS dashboard, don’t expect to see any results just yet. Let’s add code to
  src/components/CommentForm.vue that will enable users to add comments to a blog post:
  <template> <section class="comment-form"> <h4 class="text-muted">Comment Form</h4> <b-form @submit. <b-form-group <b-form-input <b-form-textareaSubmit</b-button> </b-form> </section> </template> <script> import { apiClient, ENDPOINT, CREATE_COMMENT_MUTATION } from "../graphcms.js"; export default { name: "CommentForm", props: ["post"], data() { return { name: "", message: "" }; }, methods: { async onSubmit() { const formattedComment = { name: this.name, message: this.message, post: { id: this.post.id } }; try { const response = await apiClient.post(ENDPOINT, { query: CREATE_COMMENT_MUTATION, variables: formattedComment }); const body = await response.data.data; const newComment = body.createComment; this.post.comments.push(newComment); this.name = ""; this.message = ""; } catch (error) { console.log(error); } } } }; </script> <style> .comment-form { margin-top: 35px; } </style>
  We now have a basic comment form capable of submitting a new comment to our GraphQL back-end system. Once the new comment is saved, we’ll take the returned object and add it to the
  post.comments array. This should trigger the
  CommentList component to display the newly added
  Comment.
  Let’s now build the
  src/components/Post.vue component:
  <template> <section class="post"> <b-row <b-col> <div class="lds-dual-ring text-center"></div> </b-col> </b-row> <b-row <b-col> <h1>{{post.title}}</h1> <h4 class="text-muted">{{post.categories.map(cat => cat.name).toString()}}</h4> <hr> <p>{{ post.content }}</p> </b-col> </b-row> <!-- List of comments --> <b-row <b-col> <CommentList : </b-col> </b-row> <!-- Comment form --> <b-row <b-col> <CommentForm : </b-col> </b-row> </section> </template> <script> import { ENDPOINT, apiClient, POST_BY_SLUG_QUERY } from "../graphcms.js"; import CommentList from "@/components/CommentList"; import CommentForm from "@/components/CommentForm"; export default { name: "Post", components: { CommentList, CommentForm }, data() { return { loading: false, slug: "", post: {} }; }, methods: { async fetchPost() { try { this.loading = true; const response = await apiClient.post(ENDPOINT, { query: POST_BY_SLUG_QUERY, variables: { slug: this.slug } }); const body = await response.data.data; this.post = body.post; this.loading = false; } catch (error) { console.log(error); } } }, created() { this.slug = this.$route.params.slug; this.fetchPost(); } }; </script>
  Finally, here’s the code for
  src/views/PostView.vue to tie everything together:
  <template> <div class="post-view"> <Post/> </div> </template> <script> import Post from "@/components/Post.vue"; export default { name: "PostView", components: { Post } }; </script>
  You should now have the following view for Posts. Take note of the
  :slug at the end of the URL
  localhost:8080/post/fashion-post-1:
  In the above example, I’ve added a couple of comments to test out the new feature. Make sure you do the same on yours.
  I hope you’ve seen how easy it is to build a blog website using Vue.js and GraphQL. If you’d been using plain PHP and MySQL, you’d have written much more code. Even with a PHP framework, you still would have written more code for a simple blog application.
  For the sake of this tutorial, I had to keep things as simple as possible. You may note that this blog project is far from even meeting a minimalistic blog setup. There are several things we haven’t tackled, such as error handling, form validation and caching. For the last bit, I recommend Apollo Client, as it has mechanisms for caching GraphQL query results. Then of course there needs to be an author model, and a proper comments system that supports authentication and message approval.
  If you’re up to it, please go ahead and take this simple Vue.js GraphCMS blog even further.
  Further reading:
  ☞ Easy VueJS Drag & Drop tutorial
  ☞ How to Reduce Your Vue.JS Bundle Size With Webpack - Vue.js Developers
  ☞ How to Drag and Drop File Uploads with Vue.js and Axios
  ☞ How to Get Query String Parameters in Vue.js
  ☞ How To Publish Your Vue.js Component On NPM - Vue.js Developers