One place for hosting & domains

      Working with Environment Variables in Vue.js


      While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or
      edited it to ensure you have an error-free learning experience. It’s on our list, and we’re working on it!
      You can help us out by using the “report an issue” button at the bottom of the tutorial.

      In this post, we’ll learn how to work with distinct configurations between development and production mode for Vue.js projects that use the CLI’s webpack template.

      In a web app, we most likely have to access a backend API server through a URL. This URL can be something like http://localhost:8080/api while in development, and https://site.com/api in production when the project is deployed. Environment variables allow us for an easy way to change this URL automatically, according to the current state of the project.

      An easy way to use environment variables with Vue and the webpack template is through files with a .env extension. These files become responsible for storing information that’s specific to the environment (development, testing, production,…)

      The majority of this post applies to apps using v2.x of the Vue CLI, but environment variables are just as easy to manage in the Vue CLI v3.

      Using .env Files in Vue

      The simplest way to use .env files in Vue is to create an application that already supports environment files. Let’s use the vue-cli and the webpack template for that.

      With Node 8 or higher installed, run the following, where my-app is your app name:

      $ npx vue-cli init webpack my-app
      

      This command will create an application with several files ready for use. In this post, we’re focusing only on the environment configuration, which can be accessed in the config directory:

      Project file structure

      There are two files in the config directory: dev.env.js and prod.env.js, and you’ll also have a test.env.js file if you’ve configured tests while initiating the project. These files are used in development and production mode, or in other words, when you are running the application through the npm run dev command, the dev.env.js file is used, and when you compile the project for production with the npm run build command, the prod.env.js file is used instead.

      Let’s change the development file to:

      dev.env.js

      'use strict'
      const merge = require('webpack-merge')
      const prodEnv = require('./prod.env')
      
      module.exports = merge(prodEnv, {
        NODE_ENV: '"development"',
        ROOT_API: '"http://localhost/api"'
      })
      

      Our development environment file has an additional variable called ROOT_API, with the value http://localhost/api.

      Now let’s change the production file to:

      prod.env.js

      'use strict'
      module.exports = {
        NODE_ENV: '"production"',
        ROOT_API: '"http://www.site.com/api"'
      }
      

      Here we have the same ROOT_API variable, but with a different value, which should only be used in production mode. Note how string variables need the double quotes inside the single quotes.

      Using the Environment Files in Your Code

      After creating the ROOT_API variable, we can use it anywhere in Vue through the global process.env object:

      process.env.ROOT_API
      

      For example, open the src/components/HelloWorld.vue file and in the <script> tag add the following:

      mounted() {
        console.log(process.env.ROOT_API)
      }
      

      After running npm run dev, you will see the console.log information in the browser dev tools:

      Running the app

      If you run the npm run build command, the dist directory will be created with the application ready to be deployed to a production environment, and the variable ROOT_API will display the value http://www.site.com./api, as specified in prod.env.js.

      Thus, we can work with different variables for each different environment, using the ready-made configuration that the webpack template provides us. If you use another template, make sure you find an equivalent feature or use a library like dotenv to manage your environment variables.

      What About Vue CLI 3?

      If your app is using the new Vue CLI, you’ll want to instead have files like .env and .env.prod at the root of your project and include variables like this:

      .env

      VUE_APP_ROOT_API=http://localhost/api
      

      .env.prod

      VUE_APP_ROOT_API=http://www.site.com/api
      

      The VUE_APP_ prefix is important here, and variables without that prefix won’t be available in your app.



      Source link

      Building and Using VueJS Components – A Tutorial


      Updated by Linode Contributed by Pavel Petrov

      How to Build and Use VueJS Components

      What are VueJS Components

      In VueJS, components are a way to create custom VueJS instances which can easily be reused in your code. In order to properly explain what VueJS components are we will build a very simple rating-counter component.

      This guide is written for new VueJS users and will explain what are components are, how to build them, and how to use them. Basic knowledge of JavaScript and VueJS is important for following through with the tutorial.

      In this guide you learn how to:

      Note

      Prepare the Vue App

      In your text editor on your computer, create a new file called ratingcounter.html. Then, paste in the content from this snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
      </div>
      
      <script>
      var app = new Vue({
        el: '#app'
      })
      </script>

      As this will be a simple VueJS application, the first thing we have to do is include the VueJS library in our document.

      • The easiest way that is done is by going to the Installation page at vuejs.org and copying the script tag specified under CDN.

      • As we are in the development stage of the application, we will use the development version of the VueJS library. This line is copied into line 1 of the HTML snippet.

      After the VueJS library is included, a div with id set to app is created on lines 3-4. On lines 6-10, a barebones VueJS app is created and linked to this element.

      So far this new app does nothing at all, but we will use it for the skeleton of our example application.

      Creating your First Component

      The component we’ll be developing is a simple reusable rating counter that will illustrate how VueJS components work. We’ll explain each part of the component along the way. Let’s get started:

      Define the Component

      In your ratingcounter.html, update the second <script> section (currently on lines 6-10 of your file) to include the Vue.component() function from lines 7-18 of this snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
      </div>
      
      <script>
      Vue.component('rating-counter', {
          data() {
              return {
                  count: 0
              }
          },
          template:   `<div>
                          <button v-on:click="count--">Thumbs Down</button>
                          {{ count }}
                          <button v-on:click="count++">Thumbs Up</button>
                      </div>`
      })
      
      var app = new Vue({
        el: '#app'
      })
      </script>

      Let’s go through each part of the component:

      • The first parameter of the Vue.component() function is the component’s name, in this case rating-counter (line 7). You will be referring to this component by its name in order to render it (described in the next section: Use the Component).

      • The second argument to the Vue.component() function is the component’s options (lines 8-18). We only use two options: the data function and the template.

      • The component’s data must be a function. This is because each instance of the component must have a separate and independent copy of the data object. Otherwise, each time we reuse a component it would inherit the data from the other instances of the component.

      • The template contains the HTML this component will render:

        • VueJS uses mustache tags to render strings. In our example, {{ count }} (line 19) will render the count from the component’s data.

          What is interesting is that as VueJS is reactive, when you change your variable the view automatically updates it, and you don’t have to add any code to make this work.

        • Another thing you probably noticed in the template is the v-on:click attribute of the buttons (lines 14 and 16). Similar to jQuery’s .on(‘click’, func), you can use this feature to attach a function to the on-click event of the element.

          This can either be pointed to a function, or you can use JavaScript operators. This example uses the increment ++ and decrement -- operators directly in the attribute itself.

      At this point, we’ve built our first component, but it won’t be visible yet if you load ratingcounter.html in your browser.

      Use the Component

      Let’s try the new component out. In your ratingcounter.html, update the app div as follows:

      ratingcounter.html
      1
      2
      3
      4
      5
      
      <div id="app">
         <rating-counter></rating-counter>
         <rating-counter></rating-counter>
         <rating-counter></rating-counter>
      </div>

      This will render three rating counters which work independently from one another:

      First component

      Awesome. Let’s say however that we need to pass arguments from the parent application to the component. The option for that is called props.

      Using Component Props

      Props are VueJS’s method for adding custom properties to your component. To demonstrate this concept, let’s modify our component a little bit:

      1. In ratingcounter.html, update your Vue.component() declaration as follows:

        ratingcounter
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        
        Vue.component('rating-counter', {
            props: ['title'],
            data() {
                return {
                    count: 0
                }
            },
            template:   `<div>
                            <h1>{{ title }}</h1>
                            <button v-on:click="count--">Thumbs Down</button>
                            {{ count }}
                            <button v-on:click="count++">Thumbs Up</button>
                        </div>`
        })

        The props option has been added on line 2 of this snippet, and you can access its values from the template with the mustache syntax, just like a regular data parameter. In line 9 of this snippet, the reference to {{ title }} has been added to the template.

      2. How can you actually pass data to a prop? Just pass title as an attribute to your component’s opening tag. Whatever you pass there would be accessible by your component.

        In your ratingcounter.html, update the app div as follows:

        ratingcounter.html
        1
        2
        3
        4
        5
        
        <div id="app">
           <rating-counter title="Rating 1"></rating-counter>
           <rating-counter title="Rating 2"></rating-counter>
           <rating-counter title="Rating 3"></rating-counter>
        </div>
      3. If you reload the file in your browser, you’ll now see a title rendered for each component instance. Voila:

        Component props

      Sharing Data Between Components and the Parent App

      Just as a Vue component can keep track of data used in that component, the Vue app itself can also maintain its own data object. This section will show how props can also be used to share that data with your components.

      Replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
         {{ parentHeader.label }}{{ parentHeader.globalCount }}
         <hr />
         <rating-counter title="Rating 1" v-bind:parent="parentHeader"></rating-counter>
         <rating-counter title="Rating 2" v-bind:parent="parentHeader"></rating-counter>
         <rating-counter title="Rating 3" v-bind:parent="parentHeader"></rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          props: ['title', 'parent'],
          data() {
              return {
                  count: 0
              }
          },
          template:   `<div>
                          <h1>{{ title }}</h1>
                          <button v-on:click="count--;parent.globalCount--;">Thumbs Down</button>
                          {{ count }}
                          <button v-on:click="count++;parent.globalCount++;">Thumbs Up</button>
                      </div>`
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      Load the file in your browser and start clicking the buttons. You’ll now see a label at the top of the page that counts up the total from each of your components:

      Component with bound data properties

      Let’s break down the updated parts of the file:

      • The parent app’s data is set on lines 29-34. The app now keeps track of an object called parentHeader.

      • The data from this object is rendered on line 4.

      • On line 13, we’ve added another prop to the component, called parent.

      • On lines 6-8, the value for this prop is assigned with the v-bind:parent attribute. By using the v-bind syntax, you’re telling VueJS to bind the parent attribute of the component to whichever data property you supply, in this case the parentHeader object.

      • On lines 21 and 23, the on-click actions for each button will increment or decrement the globalCount property of the parent prop, which corresponds to the globalCount property of the parentHeader object in your app’s data.

      • Because props are reactive, changing this data from the component will cascade the changes back to the parent, and to all other components that reference it.

      Using Slots

      Slots are another very clever way to pass data from parent to components in VueJS. Instead of using attributes as you did before, you can pass data within the component’s opening and closing HTML tags. Let’s take a look at the below example:

      Replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
         {{ parentHeader.label }}{{ parentHeader.globalCount }}
         <hr />
         <rating-counter v-bind:parent="parentHeader"><h1>Rating 1</h1></rating-counter>
         <rating-counter v-bind:parent="parentHeader"><h1>Rating 2</h1></rating-counter>
         <rating-counter v-bind:parent="parentHeader"><h1>Rating 3</h1></rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          props: ['parent'],
          data() {
              return {
                  count: 0
              }
          },
          template:   `<div>
                          <slot></slot>
                          <button v-on:click="count--;parent.globalCount--;">Thumbs Down</button>
                          {{ count }}
                          <button v-on:click="count++;parent.globalCount++;">Thumbs Up</button>
                      </div>`
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      When loaded in a browser, the page should appear identical to the example in the previous section.

      On lines 6-8, notice how instead of passing the title prop with an argument, we pass it within the component’s open and close tags:

      <rating-counter v-bind:parent="parentHeader"><h1>Rating 1</h1></rating-counter>

      On line 20, the slot is referenced with the <slot></slot> syntax. As shown in this example, slots support HTML. As well, they have access to the parent’s scope (not demonstrated here), and they even support nesting more components.

      About slot scope

      Slot scope is an important concept to grasp when working with slots. Even though the content you are passing from the parent is intended for the component, you are still within the context of the parent.

      For example, trying to access the count data of the rating-counter component like this would fail:

      <rating-counter v-bind:parent="parentHeader">
          <h1>{{ count }}</h1>
      </rating-counter>

      However, as you are within the scope of the parent app, you can access the parentHeader object (or any other app data):

      <rating-counter v-bind:parent="parentHeader">
          <h1>{{ parentHeader.label }}</h1>
      </rating-counter>

      Using the parentHeader.label string here wouldn’t make much sense anyway, so this would only serve to demonstrate the scope concept.

      Nesting Slots

      The most important feature of slots might be the ability to use components within components. This is especially useful when creating structure for your apps.

      Replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
          {{ parentHeader.label }}{{ parentHeader.globalCount }}
          <hr />
      
          <rating-counter v-bind:parent="parentHeader">
              <rating-title>Rating 1</rating-title>
          </rating-counter>
      
          <rating-counter v-bind:parent="parentHeader">
              <rating-title>Rating 2</rating-title>
          </rating-counter>
      
          <rating-counter v-bind:parent="parentHeader">
              <rating-title></rating-title>
          </rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          props: ['parent'],
          data() {
              return {
                  count: 0
              }
          },
          template:   `<div>
                          <slot></slot>
                          <button v-on:click="count--;parent.globalCount--;">Thumbs Down</button>
                          {{ count }}
                          <button v-on:click="count++;parent.globalCount++;">Thumbs Up</button>
                      </div>`
      })
      
      Vue.component('rating-title', {
          template:   `<div>
                          <h1>
                              <slot>Default Rating Title</slot>
                          </h1>
                      </div>`
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      We’ve created another component called rating-title to illustrate slot nesting. This component will wrap a title that you set inside a pair of <h1> tags:

      Components with nested slots

      Let’s explore the code for this component:

      • The template for the new component is defined on lines 37-41. The <slot> tag has been added to this template, but this time the slot is not empty.

      • You can specify the default value for slots by adding it between the open and close slot tags: <slot>Default Rating Title</slot>

      • You can see how this default value is referenced for the new rating-title component on line 16. Whenever nothing is included between the <rating-title></rating-title> tags, the default value is used.

      • Compare this with how the component is used on lines 8 and 12. Because a title is included between the component’s tags, the default text is is not rendered.

      Named Slots

      To allow even more structure, you can use multiple slots in a template by using slot naming. Lets overengineer our simple example a little bit to see how it works.

      Replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
          {{ parentHeader.label }}{{ parentHeader.globalCount }}
          <hr />
      
         <rating-counter v-bind:parent="parentHeader">
             <template v-slot:title>
                 <rating-title></rating-title>
             </template>
             <template v-slot:subtitle>
                 Subtitle
             </template>
         </rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          props: ['parent'],
          data() {
              return {
                  count: 0
              }
          },
          template:   `<div>
                          <slot name="title"></slot>
                          <h2>
                              <slot name="subtitle"></slot>
                          </h2>
                          <button v-on:click="count--;parent.globalCount--;">Thumbs Down</button>
                          {{ count }}
                          <button v-on:click="count++;parent.globalCount++;">Thumbs Up</button>
                      </div>`
      })
      
      Vue.component('rating-title', {
          template:   `<div>
                          <h1>
                              <slot>Default Rating Title</slot>
                          </h1>
                      </div>`
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      When loaded in a browser, the file will look like:

      Components with named slots

      • You can see that within our rating-counter component (on lines 26-29) there are now two slots. This time they have name attributes as well: title and subtitle.

      • This takes care of the creation of the named slots, but how do we use them? You can reference a named slot by using the <template v-slot:slotname></template> syntax within your parent component. The content inside the <template> tags will then be inserted where the <slot> with the same name appears in your component’s template.

      Using Component Events

      Events in components are essential for component-to-parent communication in VueJS. Whenever a component needs to communicate back to its parent, this is the safe way of doing that.

      For an example of how this works, replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
          {{ parentHeader.label+" "+parentHeader.globalCount }}
          <hr />
      
          <rating-counter v-bind:parent="parentHeader" v-on:increment="parentHeader.globalCount++"></rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          template:   `<div>
                          <br><button v-on:click="$emit('increment');">Thumbs Up</button>
                      </div>`
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      When loaded in a browser, the file will look like:

      Component with an event

      The example is similar to the previous examples, but we’ve only left what is essential to the events:

      • In the v-on:click event of our component (line 13), you can see that this time we’re using VueJS’s $emit method instead of changing variables manually. The $emit() method takes a custom event name as an argument (increment in this example).

      • This fires an empty increment event, which our parent is subscribed to on line 7. Notice the v-on:increment="parentHeader.globalCount++" attribute; this is our event subscriber.

      • The subscriber can call a method of the VueJS app, or in this example, just directly use the increment JavaScript ++ operator to increase the counter.

      • Because the component no longer directly manipulates the parent’s globalCount data, the parent prop for the component can be removed.

      Passing a Parameter with Events

      How about if you want to pass a parameter with the event? We’ve got you covered.

      For an example of how this works, replace the contents of your ratingcounter.html with the following snippet:

      ratingcounter.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      
      <div id="app">
          {{ parentHeader.label+" "+parentHeader.globalCount }}
          <hr />
      
          <rating-counter v-bind:parent="parentHeader" v-on:increment="parentHeader.globalCount+=$event"></rating-counter>
      </div>
      
      <script>
      Vue.component('rating-counter', {
          template:   `<div>
                          <button v-on:click="$emit('increment', -1);">Thumbs Down</button>
                          <button v-on:click="$emit('increment', 1);">Thumbs Up</button>
                      </div>`,
      
      })
      
      new Vue({
          el: '#app',
          data: {
              parentHeader: {
                  label: "Counter is at: ",
                  globalCount: 0
              }
          }
      })
      </script>

      When loaded in a browser, the file will look like:

      Component with an event and parameter

      • This example introduces a second parameter of the $emit() function (lines 13-14).

      • Instead of simply incrementing by one, our parent event subscriber can make use of this argument with the $event variable (line 7).

      • The template for the component now has a Thumbs Down button again, and the argument for this button’s event is -1 (line 13).

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      How To Build a Blog with Nest.js, MongoDB, and Vue.js


      The author selected Software in the Public Interest Inc to receive a donation as part of the Write for DOnations program.

      Introduction

      Nest.js is a scalable, server-side JavaScript framework built with TypeScript that still preserves compatibility with JavaScript, which makes it an effective tool for building efficient and reliable back-end applications. It has a modular architecture that provides a mature, structural design pattern to the Node.js development world.

      Vue.js is a front-end JavaScript framework for building user interfaces. It has a simple, yet very powerful API along with great performance. Vue.js is capable of powering the front-end layer and logic of any web application irrespective of the size. The ease of integrating it with other libraries or existing projects makes it a perfect choice for most modern web applications.

      In this tutorial, you’ll build a Nest.js application to get yourself familiar with its building blocks as well as the fundamental principles of building modern web applications. You’ll approach this project by separating the application into two different sections: the frontend and the backend. Firstly, you’ll concentrate on the RESTful back-end API built with Nest.js. You’ll then focus on the frontend, which you will build with Vue.js. Both applications will run on different ports and will function as separate domains.

      You’ll build a blog application with which users can create and save a new post, view the saved posts on the homepage, and carry out other processes such as editing and deleting posts. Furthermore, you’ll connect your application and persist its data with MongoDB, which is a schema-less NoSQL database that can receive and store JSON documents. This tutorial focuses on building your application in a development environment. For a production environment, you should also consider user authentication for your application.

      Prerequisites

      To complete this tutorial, you will need:

      Note: This tutorial uses a macOS machine for development. If you’re using another operating system, you may need to use sudo for npm commands throughout the tutorial.

      Step 1 — Installing Nest.js and Other Dependencies

      In this section, you will get started with Nest.js by installing the application and its required dependencies on your local machine. You can easily install Nest.js by either using the CLI that Nest.js provides, or, by installing the starter project from GitHub. For the purpose of this tutorial, you’ll use the CLI to set up the application. To begin, run the following command from the terminal to have it installed globally on your machine:

      You will see output similar to the following:

      Output

      @nestjs/cli@5.8.0 added 220 packages from 163 contributors in 49.104s

      To confirm your installation of the Nest CLI, run this command from your terminal:

      You'll see output showing the current version installed on your machine:

      Output

      5.8.0

      You'll make use of the nest command to manage your project and use it to generate relevant files — like the controller, modules, and providers.

      To begin the project for this tutorial, use the nest command to craft a new Nest.js project named blog-backend by running the following command from your terminal:

      Immediately after running the command, nest will prompt you to provide some basic information like the description, version, and author. Go ahead and provide the appropriate details. Hit ENTER on your computer to proceed after responding to each prompt.

      Next, you'll choose a package manager. For the purpose of this tutorial, select npm and hit ENTER to start installing Nest.js.

      Alt Creating a Nest project

      This will generate a new Nest.js project in a blog-backend folder within your local development folder.

      Next, navigate to the new project’s folder from your terminal:

      Run the following command to install other server dependencies:

      • npm install --save @nestjs/mongoose mongoose

      You've installed @nestjs/mongoose, which is a Nest.js dedicated package for an object modelling tool for MongoDB, and mongoose, which is a package for Mongoose.

      Now you'll start the application using the following command:

      Now, if you navigate to http://localhost:3000 from your favorite browser, you will see your application running.

      Alt Welcome page of the fresh installation of Nest.js application

      You've successfully generated the project by leveraging the availability of the Nest CLI command. Afterward, you proceeded to run the application and accessed it on the default port 3000 on your local machine. In the next section, you'll take the application further by setting up the configuration for the database connection.

      Step 2 — Configuring and Connecting with the Database

      In this step, you'll configure and integrate MongoDB into your Nest.js application. You'll use MongoDB to store data for your application. MongoDB stores its data in documents as field : value pairs. To access this data structure, you'll use Mongoose, which is an object document modeling (ODM) that allows you to define schemas representing the types of data that a MongoDB database stores.

      To start MongoDB, open a separate terminal window so that the application can keep running, and then execute the following command:

      This will start the MongoDB service and run the database in the background of your machine.

      Open the project blog-backend in your text editor and navigate to ./src/app.module.ts. You can set up a connection to the database by including the installed MongooseModule within the root ApplicationModule. To achieve this, update the content in app.module.ts with the following highlighted lines:

      ~/blog-backend/src/app.module.ts

      import { Module } from '@nestjs/common';
      import { AppController } from './app.controller';
      import { AppService } from './app.service';
      import { MongooseModule } from '@nestjs/mongoose';
      
      @Module({
        imports: [
          MongooseModule.forRoot('mongodb://localhost/nest-blog', { useNewUrlParser: true }),
        ],
        controllers: [AppController],
        providers: [AppService],
      })
      export class AppModule { }
      

      In this file, you use the forRoot() method to supply the connection to the database. Save and close the file when you are finished editing.

      With this in place, you have set up the database connection by using the Mongoose module for MongoDB. In the next section, you will create a database schema using the Mongoose library, a TypeScript interface, and a data transfer object (DTO) schema.

      Step 3 — Creating a Database Schema, Interfaces, and DTO

      In this step, you will create a schema, interface, and a data transfer object for your database using Mongoose. Mongoose helps to manage relationships between data and provides schema validation for data types. To help define the structure and datatype of the data in your application's database, you'll create files that determine the following:

      • database schema: This is an organization of data as a blueprint for defining the structure and the types of data that the database needs to store.

      • interfaces: TypeScript interfaces are used for type-checking. It can be used to define the types of data that should be passed for an application.

      • data transfer object: This is an object that defines how data will be sent over the network and carries the data between processes.

      To begin, go back to your terminal where the application is currently running and stop the process with CTRL + C, then navigate to the ./src/ folder:

      Then, create a directory named blog, and a schemas folder within that:

      In the schemas folder, create a new file called blog.schema.ts and open it using your text editor. Then, add the following content:

      ~/blog-backend/src/blog/schemas/blog.schema.ts

      import * as mongoose from 'mongoose';
      
      export const BlogSchema = new mongoose.Schema({
          title: String,
          description: String,
          body: String,
          author: String,
          date_posted: String
      })
      

      Here, you have used Mongoose to define the type of data that you will store in the database. You've specified that all the fields will store and only accept string values. Save and close the file when you are finished editing.

      Now, with the database schema determined, you can move on to creating the interfaces.

      To begin, navigate back into the blog folder:

      • cd ~/blog-backend/src/blog/

      Create a new folder named interfaces and move into it:

      In the interfaces folder, create a new file called post.interface.ts and open it using your text editor. Add the following content to define the types of data for a Post:

      ~/blog-backend/src/blog/interfaces/post.interface.ts

      import { Document } from 'mongoose';
      
      export interface Post extends Document {
          readonly title: string;
          readonly description: string;
          readonly body: string;
          readonly author: string;
          readonly date_posted: string
      }
      

      In this file, you have successfully defined the types of data for a Post type as string values. Save and exit the file.

      Since your application will carry out the functionality of posting data to the database, you will create a data transfer object that will define how data will be sent over the network.

      To achieve this, create a folder dto inside the ./src/blog folder. Within the newly created folder, create another file named create-post.dto.ts

      Navigate back into the blog folder:

      • cd ~/blog-backend/src/blog/

      Then create a folder named dto and move into it:

      In the dto folder, create a new file called create-post.dto.ts and open it using your text editor to add the following content:

      ~/blog-backend/src/blog/dto/create-post.dto.ts

      export class CreatePostDTO {
          readonly title: string;
          readonly description: string;
          readonly body: string;
          readonly author: string;
          readonly date_posted: string
      }
      

      You've marked each of the individual properties in the CreatePostDTO class to have a data type of string and as readonly to avoid unnecessary mutation. Save and exit the file when you are finished editing.

      In this step, you have created a database schema for the database, an interface, and a data transfer object for the data your database will store. Next, you'll generate a module, controller, and service for your blog.

      Step 4 — Creating the Module, Controller, and Service for the Blog

      In this step, you're going to improve on the existing structure of the application by creating a module for your blog. This module will organize the file structure of your application. Next, you'll create a controller to handle routes and process HTTP requests from the client. To wrap things up, you'll set up a service to handle all the business logic that is too complex for the controller of the application to process.

      Generating a Module

      Similarly to the Angular front-end web framework, Nest.js uses a modular syntax. Nest.js applications have a modular design; it comes installed with a single root module, which is often sufficient for a small application. But when an application starts to grow, Nest.js recommends a multiple-module organization, splitting the code into related features.

      A module in Nest.js is identified by the @Module() decorator and takes in an object with properties such as controllers and providers. Each of these properties takes an array of controllers and providers respectively.

      You will generate a new module for this blog application in order to keep the structure more organized. To begin, still in the ~/blog-backend folder, execute the following command:

      • nest generate module blog

      You will see output similar to the following:

      Output

      CREATE /src/blog/blog.module.ts UPDATE /src/app.module.ts

      The command generated a new module named blog.module.ts for the application and imported the newly created module into the root module for the application. This will allow Nest.js to be aware of another module besides the root module.

      In this file, you will see the following code:

      ~/blog-backend/src/blog/blog.module.ts

      import { Module } from '@nestjs/common';
      
      @Module({})
      export class BlogModule {}
      

      You will update this BlogModule with the required properties later in the tutorial. Save and exit the file.

      Generating a Service

      A service, which can also be called a provider in Nest.js, was designed to remove logic from controllers, which are meant to only handle HTTP requests and redirect more complex tasks to services. Services are plain JavaScript classes with an @Injectable() decorator on top of them. To generate a new service, run the following command from the terminal while you are still within the project directory:

      • nest generate service blog

      You will see output similar to the following:

      Output

      CREATE /src/blog/blog.service.spec.ts (445 bytes) CREATE /src/blog/blog.service.ts (88 bytes) UPDATE /src/blog/blog.module.ts (529 bytes)

      The nest command used here has created a blog.service.spec.ts file, which you can use for testing. It has also created a new blog.service.ts file, which will hold all the logic for this application and handle adding and retrieving documents to the MongoDB database. Also, it automatically imported the newly created service and added to blog.module.ts.

      The service handles all the logic within the application, is responsible for interacting with the database, and returns the appropriate responses back to the controller. To accomplish this, open the blog.service.ts file in your text editor and replace the contents with the following:

      ~/blog-backend/src/blog/blog.service.ts

      import { Injectable } from '@nestjs/common';
      import { Model } from 'mongoose';
      import { InjectModel } from '@nestjs/mongoose';
      import { Post } from './interfaces/post.interface';
      import { CreatePostDTO } from './dto/create-post.dto';
      
      @Injectable()
      export class BlogService {
      
          constructor(@InjectModel('Post') private readonly postModel: Model<Post>) { }
      
          async getPosts(): Promise<Post[]> {
              const posts = await this.postModel.find().exec();
              return posts;
          }
      
          async getPost(postID): Promise<Post> {
              const post = await this.postModel
                  .findById(postID)
                  .exec();
              return post;
          }
      
          async addPost(createPostDTO: CreatePostDTO): Promise<Post> {
              const newPost = await this.postModel(createPostDTO);
              return newPost.save();
          }
      
          async editPost(postID, createPostDTO: CreatePostDTO): Promise<Post> {
              const editedPost = await this.postModel
                  .findByIdAndUpdate(postID, createPostDTO, { new: true });
              return editedPost;
          }
      
          async deletePost(postID): Promise<any> {
              const deletedPost = await this.postModel
                  .findByIdAndRemove(postID);
              return deletedPost;
          }
      
      }
      
      

      In this file, you first imported the required module from @nestjs/common, mongoose, and @nestjs/mongoose. You also imported an interface named Post and a data transfer object CreatePostDTO.

      In the constructor, you added @InjectModel('Post'), which will inject the Post model into this BlogService class. You will now be able to use this injected model to retrieve all posts, fetch a single post, and carry out other database-related activities.

      Next, you created the following methods:

      • getPosts(): to fetch all posts from the database.
      • getPost(): to retrieve a single post from the database.
      • addPost(): to add a new post.
      • editPost(): to update a single post.
      • deletePost(): to delete a particular post.

      Save and exit the file when you are finished.

      You have finished setting up and creating several methods that will handle proper interaction with the MongoDB database from the back-end API. Now, you will create the required routes that will handle HTTP calls from a front-end client.

      Generating a Controller

      In Nest. js, controllers are responsible for handling any incoming requests from the client side of an application and returning the appropriate response. Similarly to most other web frameworks, it is important for the application to listen for a request and respond to it.

      To cater to all the HTTP requests for your blog application, you will leverage the nest command to generate a new controller file. Ensure that you are still in the project directory, blog-backend, and run the following command:

      • nest generate controller blog

      You will see output similar to:

      Output

      CREATE /src/blog/blog.controller.spec.ts (474 bytes) CREATE /src/blog/blog.controller.ts (97 bytes) UPDATE /src/blog/blog.module.ts (483 bytes)

      The output indicates that this command created two new files within the src/blog directory. They are blog.controller.spec.ts and blog.controller.ts. The former is a file that you can use to write automated testing for the newly created controller. The latter is the controller file itself. Controllers in Nest.js are TypeScript files decorated with @Controller metadata. The command also imported the newly created controller and added to the blog module.

      Next, open the blog.controller.ts file with your text editor and update it with the following content:

      ~/blog-backend/src/blog/blog.controller.ts

      import { Controller, Get, Res, HttpStatus, Param, NotFoundException, Post, Body, Query, Put, Delete } from '@nestjs/common';
      import { BlogService } from './blog.service';
      import { CreatePostDTO } from './dto/create-post.dto';
      import { ValidateObjectId } from '../shared/pipes/validate-object-id.pipes';
      
      
      @Controller('blog')
      export class BlogController {
      
          constructor(private blogService: BlogService) { }
      
          @Get('posts')
          async getPosts(@Res() res) {
              const posts = await this.blogService.getPosts();
              return res.status(HttpStatus.OK).json(posts);
          }
      
          @Get('post/:postID')
          async getPost(@Res() res, @Param('postID', new ValidateObjectId()) postID) {
              const post = await this.blogService.getPost(postID);
              if (!post) throw new NotFoundException('Post does not exist!');
              return res.status(HttpStatus.OK).json(post);
      
          }
      
          @Post('/post')
          async addPost(@Res() res, @Body() createPostDTO: CreatePostDTO) {
              const newPost = await this.blogService.addPost(createPostDTO);
              return res.status(HttpStatus.OK).json({
                  message: "Post has been submitted successfully!",
                  post: newPost
              })
          }
      }
      

      In this file, you first imported the necessary modules to handle HTTP requests from @nestjs/common module. Then, you imported three new modules which are: BlogService, CreatePostDTO, and ValidateObjectId. After that, you injected the BlogService into the controller via a constructor in order to gain access and make use of the functions that are already defined within the BlogService file. This is a pattern regarded as dependency injection used in Nest.js to increase efficiency and enhance the modularity of the application.

      Finally, you created the following asynchronous methods:

      • getPosts(): This method will carry out the functionality of receiving an HTTP GET request from the client to fetch all posts from the database and then return the appropriate response. It is decorated with a @Get('posts').

      • getPost(): This takes a postID as a parameter and fetches a single post from the database. In addition to the postID parameter passed to this method, you realized the addition of an extra method named ValidateObjectId(). This method implements the PipeTransform interface from Nest.js. Its purpose is to validate and ensure that the postID parameter can be found in the database. You will define this method in the next section.

      • addPost(): This method will handle a POST HTTP request to add a new post to the database.

      To be able to edit and delete a particular post, you will need to add two more methods to the blog.controller.ts file. To do that, include the following editPost() and deletePost() methods directly after the addPost() method you previously added to blog.controller.ts:

      ~/blog-backend/src/blog/blog.controller.ts

      
      ...
      @Controller('blog')
      export class BlogController {
          ...
          @Put('/edit')
          async editPost(
              @Res() res,
              @Query('postID', new ValidateObjectId()) postID,
              @Body() createPostDTO: CreatePostDTO
          ) {
              const editedPost = await this.blogService.editPost(postID, createPostDTO);
              if (!editedPost) throw new NotFoundException('Post does not exist!');
              return res.status(HttpStatus.OK).json({
                  message: 'Post has been successfully updated',
                  post: editedPost
              })
          }
      
      
          @Delete('/delete')
          async deletePost(@Res() res, @Query('postID', new ValidateObjectId()) postID) {
              const deletedPost = await this.blogService.deletePost(postID);
              if (!deletedPost) throw new NotFoundException('Post does not exist!');
              return res.status(HttpStatus.OK).json({
                  message: 'Post has been deleted!',
                  post: deletedPost
              })
          }
      }
      

      Here you have added:

      • editPost(): This method accepts a query parameter of postID and will carry out the functionality of updating a single post. It also made use of the ValidateObjectId method to provide proper validation for the post that you need to edit.

      • deletePost(): This method will accept a query parameter of postID and will delete a particular post from the database.

      Similarly to the BlogController, each of the asynchronous methods you have defined here has a metadata decorator and takes in a prefix that Nest.js uses as a routing mechanism. It controls which controller receives which requests and points to the methods that should process the request and return a response respectively.

      For example, the BlogController that you have created in this section has a prefix of blog and a method named getPosts() that takes in a prefix of posts. This means that any GET request sent to an endpoint of blog/posts (http:localhost:3000/blog/posts) will be handled by the getPosts()method. This example is similar to how other methods will handle HTTP requests.

      Save and exit the file.

      For the complete blog.controller.ts file, visit the DO Community repository for this application.

      In this section, you have created a module to keep the application more organized. You also created a service to handle the business logic for the application by interacting with the database and returning the appropriate response. Finally, you generated a controller and created the required methods to handle HTTP requests such as GET, POST, PUT, and DELETE from the client side. In the next step, you'll complete your back-end setup.

      You can identify each post in your blog application by a unique ID, also known as PostID. This means that fetching a post will require you to pass this ID as a query parameter. To validate this postID parameter and ensure that the post is available in the database, you need to create a reusable function that can be initialized from any method within the BlogController.

      To configure this, navigate to the ./src/blog folder:

      Then, create a new folder named shared:

      In the pipes folder, using your text editor, create a new file called validate-object-id.pipes.ts and open it. Add the following content to define the accepted postID data:

      ~/blog-backend/src/blog/shared/pipes/validate-object-id.pipes.ts

      import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
      import * as mongoose from 'mongoose';
      
      @Injectable()
      export class ValidateObjectId implements PipeTransform<string> {
          async transform(value: string, metadata: ArgumentMetadata) {
              const isValid = mongoose.Types.ObjectId.isValid(value);
              if (!isValid) throw new BadRequestException('Invalid ID!');
              return value;
          }
      }
      

      The ValidateObjectId() class implements the PipeTransform method from the @nestjs/common module. It has a single method named transform() that takes in value as a parameter — postID in this case. With the method above, any HTTP request from the frontend of this application with a postID that can’t be found in the database will be regarded as invalid. Save and close the file.

      After creating both the service and controller, you need to set up the Post model that is based on the BlogSchema. This configuration could be set up within the root ApplicationModule, but in this instance building the model in BlogModule will maintain your application's organization. Open the ./src/blog/blog.module.ts and update it with the following highlighted lines:

      ~/blog-backend/src/blog/blog.module.ts

      import { Module } from '@nestjs/common';
      import { BlogController } from './blog.controller';
      import { BlogService } from './blog.service';
      import { MongooseModule } from '@nestjs/mongoose';
      import { BlogSchema } from './schemas/blog.schema';
      
      @Module({
        imports: [
          MongooseModule.forFeature([{ name: 'Post', schema: BlogSchema }])
       ],
        controllers: [BlogController],
        providers: [BlogService]
      })
      export class BlogModule { }
      

      This module uses the MongooseModule.forFeature() method to define which models should be registered in the module. Without this, injecting the PostModel within the BlogService using @injectModel() decorator wouldn't work. Save and close the file when you have finished adding the content.

      In this step, you've created the complete backend RESTful API with Nest.js and integrated it with MongoDB. In the next section, you'll configure the server to allow HTTP requests from another server, because your frontend application will be running on a different port.

      Step 6 — Enabling CORS

      An HTTP request from one domain to another is often blocked by default, except when specified by the server to allow it. For your front-end application to make a request to the back-end server, you must enable Cross-origin resource sharing (CORS), which is a technique that allows requests for restricted resources on a web page.

      In Nest.js to enable CORS, you need to add a single method to your main.ts file. Open this file in your text editor, which is located at ./src/main.ts, and update it with the following highlighted content:

      ~/blog-backend/src/main.ts

      import { NestFactory } from '@nestjs/core';
      import { AppModule } from './app.module';
      
      async function bootstrap() {
        const app = await NestFactory.create(AppModule);
        app.enableCors();
        await app.listen(3000);
      }
      bootstrap();
      
      

      Save and exit the file.

      Now that you have completed the back-end setup, you'll shift your focus to the frontend and use Vue.js to consume the APIs built so far.

      Step 7 — Creating the Vue.js Frontend

      In this section, you are going to create your front-end application with Vue.js. Vue CLI is a standard tool that allows you to quickly generate and install a new Vue.js project without much hassle.

      To begin, you first need to install the Vue CLI globally on your machine. Open another terminal, and instead of working from the blog-backend folder, navigate to your local project's development folder and run:

      Once the installation process is complete, you'll make use of the vue command to create a new Vue.js project:

      You'll see a short prompt after you've entered this command. Choose the manually select features option, and then select the features you'll need for this project by pressing SPACE on your computer to highlight multiple features. You'll select Babel, Router, and Linter / Formatter.

      Alt Vue project CLI set up

      For the next instructions, type y to use history mode for a router; this will ensure that history mode is enabled within the router file, which will automatically generate for this project. In addition, select ESLint with error prevention only to pick a linter/formatter configuration. Next, select Lint on save for additional Lint features. Then select to save your configuration in a dedicated config file for future projects. Type a name for your preset, like vueconfig.

      Alt Vue.js final CLI set up

      Vue.js will then start creating the application and all its required dependencies in a directory named blog-frontend.

      Once the installation process is complete, navigate inside the Vue.js application:

      Then, start the development server with:

      Your application will be running on http://localhost:8080.

      Alt Vue.js home view

      Since you'll be performing HTTP requests within this application, you'll need to install Axios, which is a promise-based HTTP client for the browser. You'll use Axios here to perform HTTP requests from the different components within the application. Stop the front-end application by hitting CTRL + C from the terminal on your computer and then run the following command:

      Your front-end application will be making an API call to the back-end API on a particular domain from different components within the application. In order to ensure proper structure for this application, you can create a helper file and define the server baseURL.

      To begin, from you terminal still within blog-frontend, navigate to the ./src/ folder:

      Create another folder named utils:

      In the utils folder, using your text editor, create a new file called helper.js and open it. Add the following content to define the baseURL for the back-end Nest.js project:

      ~blog-frontend/src/utils/helper.js

      export const server = {
      
      baseURL: 'http://localhost:3000'
      
      }
      

      By defining a baseURL, you'll be able to call it from anywhere within you Vue.js component files. In the event that you need to change the URL, it will be an easier process to update the baseURL in this file rather than across your application.

      In this section, you installed the Vue CLI, a tool for creating a new Vue.js application. You used this tool to craft the blog-frontend application. In addition, you ran the application and installed a library named Axios, which you will use whenever there is an HTTP call within the app. Next, you will create components for the application.

      Step 8 — Creating Reusable Components

      Now you're going to create reusable components for your application, which is the standard structure for Vue.js applications. The component system in Vue.js makes it possible for developers to build a single, independent unit of an interface that can have its own state, markup, and style. This makes it appropriate for components in Vue.js to be reusable.

      Every Vue.js component contains three different sections:

      • <template>: contains the HTML contents

      • <script>: holds all the basic frontend logic and defines the functions

      • <style>: the stylesheet for each separate component

      First, you'll start by creating a component to create a new post. To do that, create a new folder named post within the ./src/components folder, which will house the necessary reusable components for posts. Then using your text editor, inside the newly created post folder, create another file and name it Create.vue. Open the new file and add the following code, which contains the necessary input fields for submitting a post:

      ~blog-frontend/src/components/post/Create.vue

      <template>
         <div>
              <div class="col-md-12 form-wrapper">
                <h2> Create Post </h2>
                <form id="create-post-form" @submit.prevent="createPost">
                     <div class="form-group col-md-12">
                      <label for="title"> Title </label>
                      <input type="text" id="title" v-model="title" name="title" class="form-control" placeholder="Enter title">
                     </div>
                    <div class="form-group col-md-12">
                        <label for="description"> Description </label>
                        <input type="text" id="description" v-model="description" name="description" class="form-control" placeholder="Enter Description">
                    </div>
                    <div class="form-group col-md-12">
                        <label for="body"> Write Content </label>
                        <textarea id="body" cols="30" rows="5" v-model="body" class="form-control"></textarea>
                    </div>
                    <div class="form-group col-md-12">
                        <label for="author"> Author </label>
                        <input type="text" id="author" v-model="author" name="author" class="form-control">
                    </div>
      
                    <div class="form-group col-md-4 pull-right">
                        <button class="btn btn-success" type="submit"> Create Post </button>
                    </div>          
                </form>
              </div>
          </div>
      </template>
      

      This is the <template> section of the CreatePost component. It contains the HTML input elements required to create a new post. Each of the input fields has a v-model directive as an input attribute. This is to ensure two-way data bindings on each of the form input to make it easy for Vue.js to obtain the user's input.

      Next, add the <script> section to the same file directly following the preceding content:

      ~blog-frontend/src/components/post/Create.vue

      ...
      <script>
      import axios from "axios";
      import { server } from "../../utils/helper";
      import router from "../../router";
      export default {
        data() {
          return {
            title: "",
            description: "",
            body: "",
            author: "",
            date_posted: ""
          };
        },
        created() {
          this.date_posted = new Date().toLocaleDateString();
        },
        methods: {
          createPost() {
            let postData = {
              title: this.title,
              description: this.description,
              body: this.body,
              author: this.author,
              date_posted: this.date_posted
            };
            this.__submitToServer(postData);
          },
          __submitToServer(data) {
            axios.post(`${server.baseURL}/blog/post`, data).then(data => {
              router.push({ name: "home" });
            });
          }
        }
      };
      </script>
      
      

      Here you've added a method named createPost() to create a new post and submit it to the server using Axios. Once a user creates a new post, the application will redirect back to the homepage where users can view the list of created posts.

      You will configure vue-router to implement the redirection later in this tutorial.

      Save and close the file when you are finished editing. For the complete Create.vue file, visit the DO Community repository for this application.

      Now, you need to create another component for editing a particular post. Navigate to ./src/components/post folder and create another file and name it Edit.vue. Add the following code that contains the <template> section to it:

      ~blog-frontend/src/components/post/Edit.vue

      <template>
      <div>
            <h4 class="text-center mt-20">
             <small>
               <button class="btn btn-success" v-on:click="navigate()"> View All Posts </button>
             </small>
          </h4>
              <div class="col-md-12 form-wrapper">
                <h2> Edit Post </h2>
                <form id="edit-post-form" @submit.prevent="editPost">
                  <div class="form-group col-md-12">
                      <label for="title"> Title </label>
                      <input type="text" id="title" v-model="post.title" name="title" class="form-control" placeholder="Enter title">
                  </div>
                  <div class="form-group col-md-12">
                      <label for="description"> Description </label>
                      <input type="text" id="description" v-model="post.description" name="description" class="form-control" placeholder="Enter Description">
                  </div>
                  <div class="form-group col-md-12">
                      <label for="body"> Write Content </label>
                      <textarea id="body" cols="30" rows="5" v-model="post.body" class="form-control"></textarea>
                  </div>
                  <div class="form-group col-md-12">
                      <label for="author"> Author </label>
                      <input type="text" id="author" v-model="post.author" name="author" class="form-control">
                  </div>
      
                  <div class="form-group col-md-4 pull-right">
                      <button class="btn btn-success" type="submit"> Edit Post </button>
                  </div>
                </form>
              </div>
          </div>
      </template>
      
      

      This template section holds similar content as the CreatePost() component; the only difference is that it contains the details of the particular post that needs to be edited.

      Next, add the<script> section directly following the </template> section in Edit.vue:

      ~blog-frontend/src/components/post/Edit.vue

      ...
      <script>
      import { server } from "../../utils/helper";
      import axios from "axios";
      import router from "../../router";
      export default {
        data() {
          return {
            id: 0,
            post: {}
          };
        },
        created() {
          this.id = this.$route.params.id;
          this.getPost();
        },
        methods: {
          editPost() {
            let postData = {
              title: this.post.title,
              description: this.post.description,
              body: this.post.body,
              author: this.post.author,
              date_posted: this.post.date_posted
            };
      
            axios
              .put(`${server.baseURL}/blog/edit?postID=${this.id}`, postData)
              .then(data => {
                router.push({ name: "home" });
              });
          },
          getPost() {
            axios
              .get(`${server.baseURL}/blog/post/${this.id}`)
              .then(data => (this.post = data.data));
          },
          navigate() {
            router.go(-1);
          }
        }
      };
      </script>
      
      

      Here, you obtained the route parameter id to identify a particular post. You then created a method named getPost() to retrieve the details of this post from the database and updated the page with it. Finally, you created an editPost() method to submit the edited post back to the back-end server with a PUT HTTP request.

      Save and exit the file. For the complete Edit.vue file, visit the DO Community repository for this application.

      Now, you'll create a new component within the ./src/components/post folder and name it Post.vue. This will allow you to view the details of a particular post from the homepage. Add the following content to Post.vue:

      ~blog-frontend/src/components/post/Post.vue

      <template>
          <div class="text-center">
              <div class="col-sm-12">
            <h4 style="margin-top: 30px;"><small><button class="btn btn-success" v-on:click="navigate()"> View All Posts </button></small></h4>
            <hr>
            <h2>{{ post.title }}</h2>
            <h5><span class="glyphicon glyphicon-time"></span> Post by {{post.author}}, {{post.date_posted}}.</h5>
            <p> {{ post.body }} </p>
      
          </div>
          </div>
      </template>
      

      This code renders the details of a post that includes, title, author, and the post body.

      Now, directly following </template>, add the following code to the file:

      ~blog-frontend/src/components/post/Post.vue

      ...
      <script>
      import { server } from "../../utils/helper";
      import axios from "axios";
      import router from "../../router";
      export default {
        data() {
          return {
            id: 0,
            post: {}
          };
        },
        created() {
          this.id = this.$route.params.id;
          this.getPost();
        },
        methods: {
          getPost() {
            axios
              .get(`${server.baseURL}/blog/post/${this.id}`)
              .then(data => (this.post = data.data));
          },
          navigate() {
            router.go(-1);
          }
        }
      };
      </script>
      

      Similar to the <script> section of the edit post component, you obtained the route parameter id and used it to retrieve the details of a particular post.

      Save and close the file when you are finished adding the content. For the complete Post.vue file, visit the DO Community repository for this application.

      Next, to display all the created posts to users, you will create a new component. If you navigate to the views folder in src/views, you will see a Home.vue component — if this file is not present, use your text editor to create it, add the following code:

      ~blog-frontend/src/views/Home.vue

      <template>
          <div>
      
            <div class="text-center">
              <h1>Nest Blog Tutorial</h1>
             <p> This is the description of the blog built with Nest.js, Vue.js and MongoDB</p>
      
             <div v-if="posts.length === 0">
                  <h2> No post found at the moment </h2>
              </div>
            </div>
      
              <div class="row">
                 <div class="col-md-4" v-for="post in posts" :key="post._id">
                    <div class="card mb-4 shadow-sm">
                      <div class="card-body">
                         <h2 class="card-img-top">{{ post.title }}</h2>
                        <p class="card-text">{{ post.body }}</p>
                        <div class="d-flex justify-content-between align-items-center">
                          <div class="btn-group" style="margin-bottom: 20px;">
                            <router-link :to="{name: 'Post', params: {id: post._id}}" class="btn btn-sm btn-outline-secondary">View Post </router-link>
                             <router-link :to="{name: 'Edit', params: {id: post._id}}" class="btn btn-sm btn-outline-secondary">Edit Post </router-link>
                             <button class="btn btn-sm btn-outline-secondary" v-on:click="deletePost(post._id)">Delete Post</button>
                          </div>
                        </div>
      
                        <div class="card-footer">
                          <small class="text-muted">Posted on: {{ post.date_posted}}</small><br/>
                          <small class="text-muted">by: {{ post.author}}</small>
                        </div>
      
                      </div>
                    </div>
                  </div>
            </div>
          </div>
      </template>
      
      

      Here, within the <template> section, you used the <router-link> to create a link for editing as well as for viewing a post by passing the post._id as a query parameter. You also used the v-if directive to conditionally render the post for users. If there is no post from the database, a user will only see this text: No post found at the moment.

      Save and exit the file. For the complete Home.vue file, visit the DO Community repository for this application.

      Now, directly following the </template> section in Home.vue, add the following </script> section:

      ~blog-frontend/src/views/Home.vue

      ...
      <script>
      // @ is an alias to /src
      import { server } from "@/utils/helper";
      import axios from "axios";
      
      export default {
        data() {
          return {
            posts: []
          };
        },
        created() {
          this.fetchPosts();
        },
        methods: {
          fetchPosts() {
            axios
              .get(`${server.baseURL}/blog/posts`)
              .then(data => (this.posts = data.data));
          },
          deletePost(id) {
            axios.delete(`${server.baseURL}/blog/delete?postID=${id}`).then(data => {
              console.log(data);
              window.location.reload();
            });
          }
        }
      };
      </script>
      
      

      Within the <script> section of this file, you created a method named fetchPosts() to fetch all posts from the database, and you updated the page with the data returned from the server.

      Now, you'll update the App component of the front-end application in order to create links to the Home and Create components. Open src/App.vue and update it with the following:

      ~blog-frontend/src/App.vue

      <template>
        <div id="app">
          <div id="nav">
            <router-link to="/">Home</router-link> |
            <router-link to="/create">Create</router-link>
          </div>
          <router-view/>
        </div>
      </template>
      
      <style>
      #app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        color: #2c3e50;
      }
      #nav {
        padding: 30px;
        text-align: center;
      }
      
      #nav a {
        font-weight: bold;
        color: #2c3e50;
      }
      
      #nav a.router-link-exact-active {
        color: #42b983;
      }
      </style>
      

      Apart from including the links to both Home and Create components, you also included the <Style> section, which is the stylesheet for this component and holds the definition of styles for some of the elements on the page. Save and exit the file.

      You have created all the required components for your application in this step. Next, you will configure the router file.

      Step 9 — Setting Up Routing

      After creating all the necessary reusable components, you can now properly configure the router file by updating its content with links to all the components you've created. This will ensure that all endpoints within the front-end application are mapped to a particular component for appropriate action. Navigate to ./src/router.js and replace its content with the following:

      ~blog-frontend/src/router.js

      import Vue from 'vue'
      import Router from 'vue-router'
      import HomeComponent from '@/views/Home';
      import EditComponent from '@/components/post/Edit';
      import CreateComponent from '@/components/post/Create';
      import PostComponent from '@/components/post/Post';
      
      Vue.use(Router)
      
      export default new Router({
        mode: 'history',
        routes: [
          { path: '/', redirect: { name: 'home' } },
          { path: '/home', name: 'home', component: HomeComponent },
          { path: '/create', name: 'Create', component: CreateComponent },
          { path: '/edit/:id', name: 'Edit', component: EditComponent },
          { path: '/post/:id', name: 'Post', component: PostComponent }
        ]
      });
      

      You imported Router from the vue-router module and instantiated it by passing the mode and routes parameters. The default mode for vue-router is a hash mode, which uses the URL hash to simulate a full URL so that the page won't be reloaded when the URL changes. In order to make the hash unnecessary, you have used history mode here to achieve URL navigation without a page reload. Finally, within the routes option, you specified the path for the endpoint — a name for the route and the component that should be rendered when the route is called within the application. Save and exit the file.

      Now that you have set up routing to the application, you need to include the Bootstrap file to help with pre-built styling for the user interface of the application. To achieve that, open ./public/index.html file in your text editor and include the CDN file for Bootstrap by adding the following content to the file:

      ~blog-frontend/public/index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
        ...
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
        <title>blog-frontend</title>
      </head>
      <body>
         ...
      </body>
      </html>
      

      Save and exit the file, and then restart the application with npm run serve for your blog-frontend, if it is not currently running.

      Note: Ensure that both the back-end server and the MongoDB instance are running as well. If otherwise, navigate to the blog-backend from another terminal and run npm run start. Also, start the MongoDB service by running sudo mongod from a new terminal as well.

      Navigate to your application at: http://localhost:8080. Now you can test your blog by creating and editing posts.

      Alt Create a new post

      Click on Create on your application to see the Create Post screen, which relates to and renders the CreateComponent file. Enter values into the input fields and click on the Create Post button to submit a post. Once you are done, the application will redirect you back to the homepage.

      The homepage of the application renders the HomeComponent. This component has a method that sends an HTTP call to fetch all posts from the database and displays them to users.

      Alt View all posts from the database

      Clicking on the Edit Post button for a particular post will take you to an edit page where you can incorporate any changes and save your post.

      Alt Edit a new post

      In this section, you configured and set up routing for the application. With this in place, your blog application is ready.

      Conclusion

      In this tutorial, you have explored a new way of structuring a Node.js application by using Nest.js. You created a simple blog application using Nest.js to build the back-end RESTful API and used Vue.js to handle all the front-end logic. Furthermore, you also integrated MongoDB as a database for your Nest.js application.

      To learn more about how to add authentication to your application, you can make use of Passport.js, a popular Node.js authentication library. You can learn about Passport.js integration in the Nest.js documentation.

      You can find the complete source code for this project here on GitHub. For more information about Nest.js, you can visit the official documentation.



      Source link