{"data":{"site":{"siteMetadata":{"title":"WebriQ Blog","author":"Kyle Mathews"}},"markdownRemark":{"id":"51003c6b-b9cb-50c2-aaa2-2afc7856c3d6","excerpt":"Vue.js is a progressive framework for building user interfaces, is gaining in popularity among developers. But why yet another JavaScript framework? Vue has…","html":"<p>Vue.js is a progressive framework for building user interfaces, is gaining in popularity among developers. But why yet another JavaScript framework? Vue has learned from the experiences of Angular and React and many see it as simpler to implement and understand.</p>\n<p>Vue.js is lightweight and easily adoptable. It’s reactive and component-based, allowing you to create pluggable components you can add to any project. Most importantly for this tutorial, Vue and its hallmark incremental adoptability allows you to try Vue without putting your existing code base at risk.</p>\n<p>Vue works well with serverless application architectures. Serverless architectures are becoming the preferred architecture for many developers because it allows them to create and fine-tune products efficiently without having to bear the burdens (server maintenance, outages, and scaling bottlenecks) of traditional server-based architecture. Sarah Drasner recently wrote an entire series on how to <a href=\"https://css-tricks.com/creating-vue-js-serverless-checkout-part-one/\">create a serverless checkout flow powered by Vue</a> and is a good example of this in practice.</p>\n<p>In this tutorial, you’ll learn how to build a marketing website as a serverless Vue.js application using ButterCMS. ButterCMS is a <a href=\"https://buttercms.com/api-first-cms/\">headless CMS and blogging platform</a> that lets you build CMS-powered apps using any programming language, including Vue. There are other options for going with a <a href=\"https://css-tricks.com/tag/headless-cms/\">headless CMS</a>, but I happen to develop for ButterCMS and know it extremely well so that’s what we’ll be using in our examples.</p>\n<p>This tutorial will show you how to add performant content APIs to your Vue.js application. These APIs are easy to navigate even for the non-technical members of your team, enabling you to enjoy agile content management without having to spin up and maintain your own CMS infrastructure.</p>\n<p>Specifically, we’ll examine code samples for three content types we might find on a marketing website: customer case studies, frequently asked questions, and blog posts.</p>\n<p>Note that the designs in the screenshots we use throughout this post will likely differ from what you build and are styled with light CSS for demonstration. Your real design would use the global styling from your app making the pages look consistent with the rest of your site.</p>\n<h3>Getting Started</h3>\n<p>We’re using ButterCMS as our content management system, so let’s install it:</p>\n<p><code class=\"language-text\">npm install buttercms --save</code>Once installed, you can proceed with the following examples.</p>\n<h3>Example 1: Customer Case Studies</h3>\n<p>Let’s start by making it possible for any non-technical person on your team to add customer case studies to the site. In this case, we’ll create a page that can hold all of the published case studies that promote the product or service we’re selling which, when clicked, open up the page for that case study.</p>\n<h4>Step 1: Setup Customer Case Study Page Type</h4>\n<p>Using the dashboard on ButterCMS, you can create a “page type” entitled “Customer Case Study” and define the content fields. Once you’ve done this, you can create your first page. Specify the name and URL of the page using the ButterCMS dashboard and complete the populate the content fields we just defined.</p>\n<p>Once this is all done, the ButterCMS API will return your defined page in JSON format. It should look something like this:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"></code></pre></div>\n<p>{\n“data”: {\n“slug”: “acme-co”,\n“fields”: {\n“facebook<em>open</em>graph<em>title”: “Acme Co loves ButterCMS”,\n“seo</em>title”: “Acme Co Customer Case Study”,\n“headline”: “Acme Co saved 200% on Anvil costs with ButterCMS”,\n“testimonial”: “We’ve been able to make anvils faster than ever before! - Chief Anvil Maker\\r\\n”,\n“customer_logo”: ”<a href=\"https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV%22\">https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV”</a>,\n}\n}\n}</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">```#### Step 2: Integrating Your App\n\nNext, open your code editor and create a file called `buttercms.js` in your `/src` directory.\n\nIf you don’t have an existing project, create one by entering the following:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">vue init webpack buttercms-project\ncd buttercms-project\nnpm i\nnpm i -S buttercms\nnpm run dev</code></pre></div>\n<p>`<code class=\"language-text\"></code>Then, in <code class=\"language-text\">src/buttercms.js</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"></code></pre></div>\n<p>import Butter from ‘buttercms’;\nconst butter = Butter(‘your<em>api</em>token’);</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">```Now, update the routes in your app. This is done in `router/index.js`:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import Vue from &#39;vue&#39;\nimport Router from &#39;vue-router&#39;\nimport CustomersHome from &#39;@/components/CustomersHome&#39;\nimport CustomerPage from &#39;@/components/CustomerPage&#39;\n    \nVue.use(Router)\n\nexport default new Router({\n  mode: &#39;history&#39;,\n  routes: [\n    {\n      path: &#39;/customers/&#39;,\n      name: &#39;customers-home&#39;,\n      component: CustomersHome\n    },\n    {\n      path: &#39;/customers/:slug&#39;,\n      name: &#39;customer-page&#39;,\n      component: CustomerPage\n    }\n  ]\n})</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"it's worth checking out hassan djirdeh's recent post [\"let's build a custom vue router\"](https://css-tricks.com/build-a-custom-vue-router) for a deep-dive on using vue's routing library and methods for creating custom routes.\"><pre class=\"language-it's worth checking out hassan djirdeh's recent post [\"let's build a custom vue router\"](https://css-tricks.com/build-a-custom-vue-router) for a deep-dive on using vue's routing library and methods for creating custom routes.\"><code class=\"language-it's worth checking out hassan djirdeh's recent post [\"let's build a custom vue router\"](https://css-tricks.com/build-a-custom-vue-router) for a deep-dive on using vue's routing library and methods for creating custom routes.\">You have content in a data file, which is great, but now you need a page that uses the content. You&#39;re going to define a `getpages()` method that fetches all of the case study pages so you can render them together on a single landing page to create an index of them all. This will be a &quot;homepage&quot; for all of the published case studies.\n\nIn `components/CustomersHome.vue` you add:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  // import ButterCMS from \n  import { butter } from &#39;@/buttercms&#39;\n  export default {\n    name: &#39;customers-home&#39;,\n    data() {\n      return {\n        page_title: &#39;Customers&#39;,\n        // Create array to hold the pages from ButterCMS API\n        pages: []\n      }\n    },\n    methods: {\n      // Get List of Customer Pages\n      getPages() {\n        butter.page.list(&#39;customer_case_study&#39;)\n          .then((res) =&gt; {\n            // console.log(res.data.data) // Check the results in the console\n            this.pages = res.data.data\n          })\n      }\n    },\n    created() {\n      // Fire on page creation\n      this.getPages()\n    }\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"...and to display the results, one by one:\"><pre class=\"language-...and to display the results, one by one:\"><code class=\"language-...and to display the results, one by one:\"></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  \n    {{ page_title }}\n    \n      \n        \n          \n          {{ page.fields.headline }}\n        \n      \n    \n  </code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"here's an example of something close to what you have so far after publishing one case study:\"><pre class=\"language-here's an example of something close to what you have so far after publishing one case study:\"><code class=\"language-here's an example of something close to what you have so far after publishing one case study:\">Now, you&#39;re going to set up the page we get when clicking on a case study from the homepage. To do so, in `components/CustomerPage.vue` we define a `getPage()` method to get a particular customer page based on its slug:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  import { butter } from &#39;@/buttercms&#39;\n  export default {\n    name: &#39;customer-page&#39;,\n    data() {\n      return {\n        slug: this.$route.params.slug,\n        page: {\n          slug: &#39;&#39;,\n          fields: {}\n        }\n      }\n    },\n    methods: {\n      getPage() {\n        butter.page.retrieve(&#39;customer_case_study&#39;, this.slug)\n          .then((res) =&gt; {\n            console.log(res.data.data)\n            this.page = res.data.data\n          }).catch((res) =&gt; {\n            console.log(res)\n          })\n      }\n    },\n    created() {\n      this.getPage()\n    }\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"and, just like you did for the case studies homepage, you need to display the content by defining a template and calling the content fields you want to show:\"><pre class=\"language-and, just like you did for the case studies homepage, you need to display the content by defining a template and calling the content fields you want to show:\"><code class=\"language-and, just like you did for the case studies homepage, you need to display the content by defining a template and calling the content fields you want to show:\"></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  \n    \n      \n    \n    {{ page.fields.headline }}\n    Testimonials\n    \n    \n  </code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"this should give you something like this:\"><pre class=\"language-this should give you something like this:\"><code class=\"language-this should give you something like this:\">Success! Now you can go directly to a page that lists all published case studies and click on any of them to be taken to the detail page for a specific case study post.\n\n### Example 2: Frequently Asked Questions\n\nNow lets&#39;s walk through how to create a Frequently Asked Questions (FAQ) page for the app. We’ll be using ButterCMS &quot;Content Fields” for this. Content fields are simply global pieces of content that can be managed by your team. This content can span multiple pages and each content field has a unique ID for querying, as you’ll see below.\n\n#### Step 1: Setup Content Fields\n\nFirst, you’ll need to set up some custom content fields. Using the dashboard, you can set up a workspace to organize content fields. Workspaces will allow content editors to curate content without affecting development or the API.\n\nOnce you&#39;re in a workspace, click the button to create a new content field. Choose the &quot;Object&quot; type and use &quot;FAQ Headline&quot; as the name of the field. It will have an API slug of `faq_headline`.\n\nAfter saving, add another field but this time choose the &quot;Collection&quot; type and use &quot;FAQ Items&quot; as the name of the field. This one will have an `faq_items` API slug. On the next screen, set up two properties for items in the collection and go back to your workspace to update your heading and add some FAQ posts.\n\n#### Step 2: Integrating Your App\n\nNow that you’ve created dynamic content using content fields, it’s time to display it in the app. To do this, you’ll fetch the fields with an API call and reference them in your view. First, set up a route to your FAQ page:\n\nLet&#39;s add FAQ routes in `router/index.js`:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import Vue from &#39;vue&#39;\nimport Router from &#39;vue-router&#39;\n\nimport FAQ from &#39;@/components/FAQ&#39;\n\nVue.use(Router)\n\nexport default new Router({\n  mode: &#39;history&#39;,\n  routes: [\n    {\n      path: &#39;/faq&#39;,\n      name: &#39;faq&#39;,\n      component: FAQ\n    }\n  ]\n})</code></pre></div>\n<p>`<code class=\"language-text\"></code>Then create <code class=\"language-text\">components/FAQ.vue</code> with a call to get the FAQ content from the API:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"></code></pre></div>\n<p>  import { butter } from ’@/buttercms’\nexport default {\nname: ‘faq’,\ndata() {\nreturn {\npage<em>title: ‘FAQ’,\nfaq</em>items: []\n}\n},\nmethods: {\ngetFaqs() {\nbutter.content.retrieve([‘faq<em>headline’, ‘faq</em>items’])\n.then((res) => {\nconsole.log(res.data.data)\nthis.page<em>title = res.data.data.faq</em>headline\nthis.faq<em>items = res.data.data.faq</em>items\n})\n}\n},\ncreated() {\nthis.getFaqs()\n}\n}</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">```Notice that we predefined `page_title` as `FAQ` and then updated it with the API call to the FAQ content fields.\n\nDefine the ``:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  \n    {{ page_title }}\n    \n      {{ faq.question }}\n      {{ faq.answer }}\n    \n  </code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"your displayed result should look something like this:\"><pre class=\"language-your displayed result should look something like this:\"><code class=\"language-your displayed result should look something like this:\">Now anyone on your team can update the values from the ButterCMS dashboard and the corresponding content in your app will automatically update.\n\n### Example 3: Blog Posts\n\nLast, we’ll tackle a blog engine for the app.\n\n#### Step 1: Displaying Posts\n\nWe’ll start out by creating a blog route using `vue-router`. To display posts we create a simple `/blog` route in our app and fetch blog posts, as well as a `/blog/:slug` route to handle individual posts.\n\nIn `router/index.js`:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">import Vue from &#39;vue&#39;\nimport Router from &#39;vue-router&#39;\nimport BlogHome from &#39;@/components/BlogHome&#39;\nimport BlogPost from &#39;@/components/BlogPost&#39;\n\nVue.use(Router)\n\nexport default new Router({\n  mode: &#39;history&#39;,\n  routes: [\n    {\n      path: &#39;/blog/&#39;,\n      name: &#39;blog-home&#39;,\n      component: BlogHome\n    },\n    {\n      path: &#39;/blog/:slug&#39;,\n      name: &#39;blog-post&#39;,\n      component: BlogPost\n    }\n  ]\n})</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"#### step 2: creating the blog homepage\"><pre class=\"language-#### step 2: creating the blog homepage\"><code class=\"language-#### step 2: creating the blog homepage\">To create your blog homepage that displays the most recently published posts, you’ll create a Vue component for the blog home in a new `components/BlogHome.vue` file:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  import { butter } from &#39;@/buttercms&#39;\n  export default {\n    name: &#39;blog-home&#39;,\n    data() {\n      return {\n        page_title: &#39;Blog&#39;,\n        posts: []\n      }\n    },\n    methods: {\n      getPosts() {\n        butter.post.list({\n          page: 1,\n          page_size: 10\n        }).then((res) =&gt; {\n          // console.log(res.data)\n          this.posts = res.data.data\n        })\n      }\n    },\n    created() {\n      this.getPosts()\n    }\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"if you have been following along with the previous examples, then you may start to see a pattern here and know that you need to display the content by defining the template and calling the fields in the same component file:\"><pre class=\"language-if you have been following along with the previous examples, then you may start to see a pattern here and know that you need to display the content by defining the template and calling the fields in the same component file:\"><code class=\"language-if you have been following along with the previous examples, then you may start to see a pattern here and know that you need to display the content by defining the template and calling the fields in the same component file:\"></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  \n      {{ page_title }}\n      \n      \n        \n          \n            \n              \n              \n              \n              \n            \n            {{ post.title }}\n            {{ post.summary }}\n          \n        \n      \n  </code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"assuming your fields match the example, your blog homepage should look something like this:\"><pre class=\"language-assuming your fields match the example, your blog homepage should look something like this:\"><code class=\"language-assuming your fields match the example, your blog homepage should look something like this:\">#### Step 3: Creating a Blog Post\n\nNext, create a new `components/BlogPost.vue` file which will be your view for a single post:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  import { butter } from &#39;@/buttercms&#39;\n  export default {\n    name: &#39;blog-post&#39;,\n    data() {\n      return {\n        post: {}\n      }\n    },\n    methods: {\n      getPost() {\n        butter.post.retrieve(this.$route.params.slug)\n          .then((res) =&gt; {\n            // console.log(res.data)\n            this.post = res.data\n          }).catch((res) =&gt; {\n            console.log(res)\n          })\n      }\n    },\n    created() {\n      this.getPost()\n    }\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"you may have guessed it, but now you need to define the template and make a call to the blog post content fields:\"><pre class=\"language-you may have guessed it, but now you need to define the template and make a call to the blog post content fields:\"><code class=\"language-you may have guessed it, but now you need to define the template and make a call to the blog post content fields:\"></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  \n    {{ post.data.title }}\n    {{ post.data.author.first_name }} {{ post.data.author.last_name }}\n    \n\n    \n      {{ post.meta.previous_post.title }}\n    \n    \n      {{ post.meta.next_post.title }}\n    \n  </code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"again, assuming you're using similar fields as the example, this is what you might expect to see:\"><pre class=\"language-again, assuming you're using similar fields as the example, this is what you might expect to see:\"><code class=\"language-again, assuming you're using similar fields as the example, this is what you might expect to see:\">#### Step 4: Handling Blog Post Routes\n\nAt this point, your app is pulling all blog posts, allowing you to navigate to individual posts. But, you will notice the next/previous buttons in the browser aren’t working. Why? When using routes with `params`, the same component instance will be reused when the user navigates from `/blog/foo` to `/blog/bar`.\n\nSince both routes render the same component, this is more efficient than destroying the old instance and creating a new one. But, this also means that the lifecycle hooks of the component will not be called.\n\nThere is a fix for this. We need to watch the `$route` object and call `getPost()` when the route changes. To do this, update the script section in `components/BlogPost.vue`:</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">  import { butter } from &#39;@/buttercms&#39;\n  export default {\n    name: &#39;blog-post&#39;,\n    data() {\n      return {\n        post: {}\n      }\n    },\n    methods: {\n      getPost() {\n        butter.post.retrieve(this.$route.params.slug)\n          .then((res) =&gt; {\n            // console.log(res.data)\n            this.post = res.data\n          }).catch((res) =&gt; {\n            console.log(res)\n          })\n      }\n    },\n    watch: {\n      $route(to, from) {\n        this.getPost()\n      }\n    },\n    created() {\n      this.getPost()\n    }\n  }</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"at this point, your app has a working blog that can be updated easily from the cms dashboard.\"><pre class=\"language-at this point, your app has a working blog that can be updated easily from the cms dashboard.\"><code class=\"language-at this point, your app has a working blog that can be updated easily from the cms dashboard.\">You can also use APIs to filter and feature content on your blog with categories, tags, and authors. In fact, there’s a lot you can do with an API in terms of managing different aspects of your blog, including RSS, Atom feeds, sitemap markup, and content styling with CSS.\n\n### API or GIT Based Approach\n\nA Git-based approach to managing content\n\nThe premise is that you can leverage modern frontend tooling to create lighter, faster, more secure sites. And FYI, this isn&#39;t fringe developer extravaganza. Entire organizations like Smashing Magazine, Sequoia Capital &amp; Mailchimp have migrated to frontend tooling. \nOne of the first implications here is decoupling the building &amp; hosting of your site. You can &quot;pre-bake&quot; and pre-build assets and then serve them on Content Delivery Networks. This effectively frees your content from the database/templates imperatives of traditional CMS, like Wordpress and Drupal.\nHowever, the content itself still needs to live somewhere. That&#39;s where GIT comes in.\nTo create your site, you can use a static site generator like Spike, the one implemented as standard by WebriQ or a JS framework like Ember, Angular, React, or Vue. It doesn&#39;t matter. The point is you store the site&#39;s content in a GitHub repository, where most of your pages can be simple Markdown files. Then, you deploy &amp; host that static content on a content delivery delivery network of your choice. \nWebriQ CMS is a Git-based, open source Ember CMS. \nThe CMS is part of an [APP](https://app.webriq.com) to build, manage, update and host sites build with unique front end tools. With WebriQ CMS your content lives in Github and the CMS leverages Github&#39;s API to interact directly with your content repository. The WebriQ API allows the CMS to interact directly with the Github repository. It loops editors directly in to Github workflow. \nYou can see our Git based CMS in action on [WEBRIQ CMS DEMO](https://app.webriq.com/help/page/demo/howto)\n\n </code></pre></div>","frontmatter":{"title":"Building a Serverless CMS Powered by Vue.js","date":"March 09, 2018"}}},"pageContext":{"slug":"/building-a-serverless-cms-powered-by-vuejs/","previous":{"fields":{"slug":"/git-based-cms-system/"},"frontmatter":{"title":"Serverless technology is bringing CMS out of the past"}},"next":{"fields":{"slug":"/wtf-is-the-jam-stack/"},"frontmatter":{"title":"WTF is the JAM Stack"}}}}