How to Build Grails 3, MongoDB and Vue.js CRUD Web Application

by Didin J. on Jun 07, 2018 How to Build Grails 3, MongoDB and Vue.js CRUD Web Application

Step by step tutorial on build Grails 3, MongoDB and Vue.js Profile CRUD (Create, Read, Update, Delete) Web Application

Comprehensive step by step by step tutorial on build Grails 3, MongoDB and Vue.js Profile CRUD (Create, Read, Update, Delete) Web Application. Previously we have to show you how to build CRUD Web Application with the famous Angular 5 and React.js. Now, we will use Vue.js as front-end framework that will be working together on the same project with Grails 3. Using this profile will create 2 application, they are Grails 3 as server and Vue.js as Client.

The scenario for this tutorial almost same as previous tutorial using Angular 5 or React.js profile. They are creating, read, update and delete Customer data. CRUD mechanism using RESTful API provided by Grails 3 and visualization using Vue.js as the front end.

The following tools, frameworks, and module are required for this tutorial achievement:

- JDK 8
- Grails 3.3.5
- Node.js (recommended stable version)
- Vue.js
- MongoDB
- Terminal (Linux/Mac) or Command Line (Windows)
- Text Editor or IDE

Before moving to the steps of the tutorial, make sure you have installed above requirements.


1. Create New Grails 3 Application

Open the terminal or command line then go to your Grails projects folder. Type this command to create a new Grails 3 application with the Vue.js profile.

grails create-app grails-vue --profile=vue

Go to the newly created Grails 3 application folder.

cd ./grails-vue

Now you have server and client folder inside this project folder.

drwxr-xr-x  17 didin  staff   544 May 29 06:58 client
drwxr-xr-x   3 didin  staff    96 May 29 06:58 gradle
-rwxr--r--   1 didin  staff  4971 May 29 06:58 gradlew
-rwxr--r--   1 didin  staff  2314 May 29 06:58 gradlew.bat
drwxr-xr-x  10 didin  staff   320 May 29 06:58 server
-rw-r--r--   1 didin  staff    26 May 29 06:58 settings.gradle

You can run server or client only by type this command.

./gradlew server:bootRun
./gradlew client:bootRun

To run both of them together, use this command.

./gradlew bootRun --parallel

Node.js and its dependencies will be downloaded automatically. The vue.js application will run using port 3000 and Grails 3 using port 8080. Now, open the browser then point to `localhost:3000` then you will see this page.

How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - Grails Vue Page

You will get this response on the browser when you change the address to `localhost:8080`.

How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - Server Response


2. Create Grails 3 Domain Class

To save or populate MongoDB data, first, we have to add Gradle dependencies for MongoDB. Open and edit `server/build.gradle` file then comment out all Hibernate and H2 dependencies then add dependencies for MongoDB.

buildscript {
    ...
    dependencies {
        classpath "org.grails:grails-gradle-plugin:$grailsVersion"
        classpath "com.moowork.gradle:gradle-node-plugin:0.13"
        // classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}"
        classpath "org.grails.plugins:views-gradle:1.1.6"
    }
}
...
dependencies {
    ...
    // compile "org.grails.plugins:hibernate5"
    // compile "org.hibernate:hibernate-core:5.1.5.Final"
    compile "org.grails.plugins:views-json"
    compile "org.grails.plugins:views-json-templates"
    console "org.grails:grails-console"
    profile "org.grails.profiles:react"
    runtime "org.glassfish.web:el-impl:2.1.2-b03"
    // runtime "com.h2database:h2"
    runtime "org.apache.tomcat:tomcat-jdbc"
    compile 'org.grails.plugins:mongodb'
    ...
}

Compile Grails 3 application by typing this command inside Server folder.

cd ./server
grails compile

Next, open and edit `server/grails-app/conf/application.yml` then remove or replace in-memory H2 database and Hibernate configuration with this.

...
environments:
    development:
        grails:
            mongodb:
                host: "localhost"
                port: 27017
                username: ""
                password: ""
                databaseName: "grails-vue"
    production:
        grails:
            mongodb:
                host: "localhost"
                port: 27017
                username: ""
                password: ""
                databaseName: "grails-vue"

Now, we are ready to create a domain class for Customer data. In the terminal or command line inside Server folder type this command to enter Grails 3 interactive console.

grails

Create a new Grails 3 domain class by typing this command.

create-domain-class grails.vue.Customer

Open and edit `server/grails-app/domain/grails/vue/Customer.groovy` file then Replace all codes with this.

package grails.vue

import grails.rest.*

@Resource(uri='/customer')
class Customer {

  String name
  String address
  String city
  String postalCode
  String phone

  static constraints = {
      name blank:false
      address blank:false
      city blank:false
      postalCode blank:false
      phone blank:false
  }

  String toString() {
    name
  }
}

That it's, just modify a domain class we have a RESTful API for Customer data CRUD operation.


3. Test CRUD (Create, Read, Update, Delete) RESTful API

Now, it's a time for testing a CRUD (Create, Read, Update, Delete) operation. We will use `CURL` command for this. Open the other terminal or command line tab then run MongoDB server if there's no currently running MongoDB server.

Run again the Server by using this command in previous terminal tab after type `exit` in the Grails 3 interactive console.

cd ../
./gradlew server:bootRun

Open another terminal or command line tab again then type this command to get Customer data/list.

curl -i -H "Accept: application/json" localhost:8080/customer

The correct response should be like this.

HTTP/1.1 200
X-Application-Context: application:development
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 31 May 2018 00:11:33 GMT

[]

Next, save or post a single data to the Customer endpoint.

curl -i -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","address":"accross the river behind the mountain","city":"the hight mount","postalCode":"11111","phone":"123123123"}' localhost:8080/customer

You will get this response when data saved successfully to MongoDB database.

HTTP/1.1 201
X-Application-Context: application:development
Location: http://localhost:8080/customer/1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 31 May 2018 00:12:16 GMT

{"id":1,"phone":"123123123","address":"accross the river behind the mountain","postalCode":"11111","name":"John Doe","city":"the hight mount"}

Now you have your RESTful API ready to access from the Vue.js front-end application. Don't worry about the CORS for using the different port because it already enabled by Grails 3 default configuration.


4. Create Vue.js Component and Routing

Now, it's time for Vue.js or front end part. First, create or add the component of the customer list, show, edit and create. Create all of those files inside components folder after go to the Client folder.

cd ./client
touch src/components/CustomerList.vue
touch src/components/CreateCustomer.vue
touch src/components/EditCustomer.vue
touch src/components/ShowCustomer.vue

Now, open and edit `client/src/router/index.js` then add the import for all above new components.

import Vue from 'vue'
import Router from 'vue-router'
import CustomerList from '@/components/CustomerList'
import CreateCustomer from '@/components/CreateCustomer'
import ShowCustomer from '@/components/ShowCustomer'
import EditCustomer from '@/components/EditCustomer'

Add the router to each component or page.

export default new Router({
  routes: [
    {
      path: '/',
      name: 'CustomerList',
      component: CustomerList
    },
    {
      path: '/show-customer/:id',
      name: 'ShowCustomer',
      component: ShowCustomer
    },
    {
      path: '/add-customer',
      name: 'CreateCustomer',
      component: CreateCustomer
    },
    {
      path: '/edit-customer/:id',
      name: 'EditCustomer',
      component: EditCustomer
    }
  ]
})


5. Add Module for RESTful API Access and Styling UI

Previously, the file for customer list component is created. For UI or styling, we are using Bootstrap Vue, to install it type this command on the terminal.

npm i [email protected] bootstrap-vue [email protected]

Open and edit `client/src/main.js` then add/replace the imports for Bootstrap-Vue.

import Vue from 'vue'
import App from './App'
import BootstrapVue from 'bootstrap-vue'
import router from './router'
import * as uiv from 'uiv'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import './assets/css/grails.css'
import './assets/css/main.css'

Add this line after `Vue.config`.

Vue.use(BootstrapVue)

Next, we are using Axio for accessing RESTful API that provided by Grails. To install it, in the terminal type this command.

npm install axios --save


6. Modify Component of Customer List

Now, open and edit `client/src/components/CustomerList.vue` then add this lines of codes.

<template>
  <b-row>
    <b-col cols="12">
      <h2>
        Customer List
        <b-link href="#/add-customer">(Add Customer)</b-link>
      </h2>
      <b-table striped hover :items="customers" :fields="fields">
        <template slot="actions" scope="row">
          <b-btn size="sm" @click.stop="details(row.item)">Details</b-btn>
        </template>
      </b-table>
      <ul v-if="errors && errors.length">
        <li v-for="error of errors">
          {{error.message}}
        </li>
      </ul>
    </b-col>
  </b-row>
</template>

<script>

import axios from 'axios'

export default {
  name: 'CustomerList',
  data () {
    return {
      fields: {
        name: { label: 'Name', sortable: true, 'class': 'text-center' },
        city: { label: 'City', sortable: true },
        actions: { label: 'Action', 'class': 'text-center' }
      },
      customers: [],
      errors: []
    }
  },
  created () {
    axios.get(`http://localhost:8080/customer`)
    .then(response => {
      this.customers = response.data
    })
    .catch(e => {
      this.errors.push(e)
    })
  },
  methods: {
    details (customer) {
      this.$router.push({
        name: 'ShowCustomer',
        params: { id: customer.id }
      })
    }
  }
}
</script>

There are template and script in one file. The template block contains HTML tags. Script block contains variables, page lifecycle and methods or functions.


7. Modify Component of Create Customer

Now, open and edit `client/src/components/CreateCustomer.vue` then add this lines of codes.

<template>
  <b-row>
    <b-col cols="12">
      <h2>
        Add Customer
        <b-link href="#/">(Customer List)</b-link>
      </h2>
      <b-form @submit="onSubmit">
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Name">
          <b-form-input id="name" :state="state" v-model.trim="customer.name"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Address">
            <b-form-textarea id="address"
                       v-model="customer.address"
                       placeholder="Enter something"
                       :rows="2"
                       :max-rows="6">{{customer.address}}</b-form-textarea>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer City">
          <b-form-input id="city" :state="state" v-model.trim="customer.city"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Postal Code">
          <b-form-input id="postalCode" :state="state" v-model.trim="customer.postalCode"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Phone">
          <b-form-input id="phone" :state="state" v-model.trim="customer.phone"></b-form-input>
        </b-form-group>
        <b-button type="submit" variant="primary">Save</b-button>
      </b-form>
    </b-col>
  </b-row>
</template>

<script>

import axios from 'axios'

export default {
  name: 'CreateCustomer',
  data () {
    return {
      customer: {}
    }
  },
  methods: {
    onSubmit (evt) {
      evt.preventDefault()
      axios.post(`http://localhost:8080/customer`, this.customer)
      .then(response => {
        this.$router.push({
          name: 'ShowCustomer',
          params: { id: response.data.id }
        })
      })
      .catch(e => {
        this.errors.push(e)
      })
    }
  }
}
</script>

That code contains the template for customer form, the script that contains Vue.js 2 codes for hold customer model and methods for saving customer to RESTful API.


8. Modify Component of Show Customer

Open and edit `client/src/components/ShowCustomer.vue` then add this lines of codes.

<template>
  <b-row>
    <b-col cols="12">
      <h2>
        Edit Customer
        <b-link href="#/">(Customer List)</b-link>
      </h2>
      <b-jumbotron>
        <template slot="header">
          {{customer.title}}
        </template>
        <template slot="lead">
          Name: {{customer.name}}<br>
          Address: {{customer.address}}<br>
          City: {{customer.city}}<br>
          Postal Code: {{customer.postalCode}}<br>
          Phone: {{customer.phone}}<br>
        </template>
        <hr class="my-4">
        <b-btn variant="success" @click.stop="editcustomer(customer.id)">Edit</b-btn>
        <b-btn variant="danger" @click.stop="deletecustomer(customer.id)">Delete</b-btn>
      </b-jumbotron>
    </b-col>
  </b-row>
</template>

<script>

import axios from 'axios'

export default {
  name: 'ShowCustomer',
  data () {
    return {
      customer: []
    }
  },
  created () {
    axios.get(`http://localhost:8080/customer/` + this.$route.params.id)
    .then(response => {
      this.customer = response.data
    })
    .catch(e => {
      this.errors.push(e)
    })
  },
  methods: {
    editcustomer (customerid) {
      this.$router.push({
        name: 'EditCustomer',
        params: { id: customerid }
      })
    },
    deletecustomer (customerid) {
      axios.delete('http://localhost:8080/customer/' + customerid)
      .then((result) => {
        this.$router.push({
          name: 'CustomerList'
        })
      })
      .catch(e => {
        this.errors.push(e)
      })
    }
  }
}
</script>

<style>
  .jumbotron {
    padding: 2rem;
  }
</style>

Delete function also includes this component inside methods block.


9. Modify Component of Edit Customer

For editing customer that chooses from show customer page, open and edit `client/src/components/EditCustomer.vue` then add this lines of codes.

<template>
  <b-row>
    <b-col cols="12">
      <h2>
        Edit Customer
        <router-link :to="{ name: 'ShowCustomer', params: { id: customer.id } }">(Show Customer)</router-link>
      </h2>
      <b-form @submit="onSubmit">
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Name">
          <b-form-input id="name" :state="state" v-model.trim="customer.name"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Address">
            <b-form-textarea id="address"
                       v-model="customer.address"
                       placeholder="Enter something"
                       :rows="2"
                       :max-rows="6">{{customer.address}}</b-form-textarea>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer City">
          <b-form-input id="city" :state="state" v-model.trim="customer.city"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Customer Postal Code">
          <b-form-input id="postalCode" :state="state" v-model.trim="customer.postalCode"></b-form-input>
        </b-form-group>
        <b-form-group id="fieldsetHorizontal"
                  horizontal
                  :label-cols="4"
                  breakpoint="md"
                  label="Enter Phone">
          <b-form-input id="phone" :state="state" v-model.trim="customer.phone"></b-form-input>
        </b-form-group>
        <b-button type="submit" variant="primary">Update</b-button>
      </b-form>
    </b-col>
  </b-row>
</template>

<script>

import axios from 'axios'

export default {
  name: 'EditCustomer',
  data () {
    return {
      customer: {}
    }
  },
  created () {
    axios.get(`http://localhost:8080/customer/` + this.$route.params.id)
    .then(response => {
      this.customer = response.data
    })
    .catch(e => {
      this.errors.push(e)
    })
  },
  methods: {
    onSubmit (evt) {
      evt.preventDefault()
      axios.put(`http://localhost:8080/customer/` + this.$route.params.id, this.customer)
      .then(response => {
        this.$router.push({
          name: 'ShowCustomer',
          params: { id: this.$route.params.id }
        })
      })
      .catch(e => {
        this.errors.push(e)
      })
    }
  }
}
</script>

This component almost same as creating customer component, except for load customer data by id and method for update data using `PUT`.


10. Run The Grails 3, MongoDB and Vue.js Web Application

This time to test all complete the Grails 3, MongoDB and Vue.js Stack configuration. Type this command to run again this web application.

./gradlew bootRun --parallel

And here the application looks like, now you can test all CRUD functionality.

How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - Edit
How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - Show
How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - List
How to Build Grails 3, MongoDB and Vue.js CRUD Web Application - Create

That's it, you can find the full source code on our GitHub.

That just the basic. If you need more deep learning about Groovy and Grails you can take the following cheap course:

  • Mastering Grails. A Comprehensive Grails Course.
  • Groovy Scripting for Developers / Testers
  • Introduction to JVM Languages Clojure, Kotlin, and Groovy
  • Thanks!