{{announcement.body}}
{{announcement.title}}

Using Kotlin, Spring Boot, and Vue.js to Make a CRUD App

DZone 's Guide to

Using Kotlin, Spring Boot, and Vue.js to Make a CRUD App

Learn to build a Vue.js client application and a Spring Boot REST service, using them to demonstrate a CRUD application with Okta for authentication.

· Java Zone ·
Free Resource

Vue.js is a Javascript view library, similar to React and Angular. When paired with a state management library such as MobX, Vue.js is able to function as a full-featured application framework. In addition, Vue.js is adaptable, and can therefore be tailored to fit your needs. Similarly to REact, Vue.js uses a virtual DOM to streamline processing so it can limit rendering on each stage update.

From experience, I find Vue a much easier tool to use compared to React and Angular. It is best when used on small projects that don’t depend on a full web application framework. While this is true, it doesn’t mean that Vue.js should be limited to smaller projects. Vue is being used more often on bigger projects. 

Spring’s streamlined Java application framework is Spring Boot, which makes creating server-side code (with Java/Kotlin) easy. A lot of behind-the-scenes action occurs in Spring Boot and, while this ‘mysterious’ function made Spring harder to use in the past, Spring Boot has been able to find a stability between the customizable and easy to use features, making it much more efficient than its earlier counterpart; Spring. Spring Boot can be used to create a fully functioning resource server from both a plain Java and Kotlin object, from a minimal amount of code.

Today I will teach you how to create a fully-functional, client-server CRUD application using Spring Boot for the resource server, and Vue.js for the client. Using Kotlin for the Spring Boot Server, you will also need to use Okta and Oauth 2.0 to complete the application. 

If you didn’t know, CRUD stands for Create, Read, Update, and Delete. Once these operations can be completed, you have acquired all the basics for a server application.

You are going to be building a todo app. The client-side of the app is based on the original Vue Todo App project, and on a project written by Evan You. Although it has been significantly modified, I understand the importance of giving credit where it is due. 

Prerequisites:

Table of Contents 

Create a Vue + Spring Boot Application

Rather than creating an app from scratch, go ahead and download the example application from this tutorial’s GitHub repository.

Java
 




x


 
1
git clone https://github.com/oktadeveloper/okta-kotlin-spring-boot-vue-example.git


The example project contains two main sub-directories:

  • client: contains the Vue.js client
  • server: contains the Spring Boot Kotlin resource server

First, you’re going to take a look at the resource server and make sure that it’s all working.

Build a Spring Boot Resource Server with Kotlin

The Kotlin resource server is pretty simple. Spring has done a great job reducing boilerplate code. There are four Kotlin source files in the project, all in the com.okta.springbootvue package:

  • SpringBootVueApplication: the main entry point into the application
  • RestRepositoryConfigurator: used to configure the auto-generated REST resource to return resource IDs
  • Todo: defines the Todo class data model
  • TodoRepository: configures the auto-generated Spring Boot JPA repository

The first file, SpringBootVueApplication, is the place where the application starts. It contains the @SpringBootApplication annotation that loads the Spring Boot framework. It also contains a function named init() that loads some initial test data into the repository. It contains a bean that configures a CORS filter so that you can make calls from the Vue.js application without getting cross-origin errors. Finally, it contains the good, old-fashioned main() function where the magic begins.

com/okta/springbootvue/SpringBootVueApplication.kt

Java
 




xxxxxxxxxx
1
37


 
1
@SpringBootApplication
2
class SpringBootVueApplication {
3
   // Bootstrap some test data into the in-memory database
4
   @Bean
5
   fun init(repository: TodoRepository): ApplicationRunner {
6
       return ApplicationRunner { _: ApplicationArguments? ->
7
           arrayOf("Buy milk", "Eat pizza", "Write tutorial", "Study Vue.js", "Go kayaking").forEach {
8
               val todo = Todo(it, false)
9
               repository.save(todo)
10
           }
11
           repository.findAll().forEach(Consumer { x: Todo? -> println(x) })
12
       }
13
   }
14
 
          
15
   // Fix the CORS errors
16
   @Bean
17
   fun simpleCorsFilter(): FilterRegistrationBean<*> {
18
       val source = UrlBasedCorsConfigurationSource()
19
       val config = CorsConfiguration()
20
       config.allowCredentials = true
21
       // *** URL below needs to match the Vue client URL and port ***
22
       config.allowedOrigins = listOf("http://localhost:8080")
23
       config.allowedMethods = listOf("*")
24
       config.allowedHeaders = listOf("*")
25
       source.registerCorsConfiguration("/**", config)
26
       val bean: FilterRegistrationBean<*> = FilterRegistrationBean(CorsFilter(source))
27
       bean.order = Ordered.HIGHEST_PRECEDENCE
28
       return bean
29
   }
30
 
          
31
   companion object {
32
       @JvmStatic
33
       fun main(args: Array<String>) {
34
           SpringApplication.run(SpringBootVueApplication::class.java, *args)
35
       }
36
   }
37
}



The Todo class is what defines the data model. Specifically, it defines three properties: 1) a string title, 2) a boolean completed, and 3) an auto-generated id integer value. If you’re not familiar with Kotlin you might find this strange, but the title and the completed properties are declared on the first line of the class definition in the default constructor.

Other than that, the @Entity annotation is what tells Spring that this class is a data model entity. There’s also a helper toString() override.

Java
 




xxxxxxxxxx
1


1
@Entity
2
class Todo(var title: String, var completed: Boolean) {
3
   @Id
4
   @GeneratedValue
5
   var id: Long? = null
6
   override fun toString() : String {
7
       return "Title='$title', Completed=$completed";
8
   }
9
}



Spring is doing a lot of work behind the scenes. Pretty much all of the resource server’s infrastructure is auto-generated. You’re simply defining the data model and pointing Spring Boot to it. This is achingly clear in the next class, TodoRepository, which is the class that defines and creates the REST interface for your resource server as well as your persistence store.

The persistence store in this case (the class that is responsible for saving and loading your data resources from a database) uses the default store, which is an in-memory database. It is great for examples and testing but needs to be overridden in production so that you can actually persist your data.

The coolest part is the @RepositoryRestResource annotation. All you have to do is add it to the JpaRepository class and Spring will turn your repository into a REST interface. This annotation requires the spring-boot-starter-data-rest dependency. The details of the resource server can be configured extensively. Take a look at the Spring Data REST Reference documentation for more info.

Now for the code:

com/okta/springbootvue/TodoRepository.kt

Java
 




xxxxxxxxxx
1


1
@RepositoryRestResource
2
interface TodoRepository:JpaRepository<Todo, Long>



Yep, that’s it!

The last file is RestRepositoryConfigurator. This file overrides RepositoryRestConfigurer and is used to configure the REST repository. Specifically, it configures it to expose IDs for the Todo class. This tells Spring that when it returns Todo objects, it should return the object ID with it as well. Doing this makes it a lot easier to implement the CRUD methods on the client-side.

Java
 




xxxxxxxxxx
1


 
1
@Component
2
class RestRepositoryConfigurator : RepositoryRestConfigurer {
3
   override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration) {
4
       config.exposeIdsFor(Todo::class.java)
5
   }
6
}



Test the Spring Boot Resource Server

That’s all you need to create a working REST API.

Now you’re going to test it using HTTPie. But first, open a shell, navigate to the /server subdirectory, and start the server using ./gradlew bootRun.

You should see some output that ends like this:

Java
 




xxxxxxxxxx
1


 
1
2020-06-24 19:31:34.033  INFO 25910 --- [  restartedMain] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2
2020-06-24 19:31:34.039  INFO 25910 --- [  restartedMain] c.o.s.SpringBootVueApplication$Companion : Started SpringBootVueApplication.Companion in 2.287 seconds (JVM running for 2.532)
3
Title='Buy milk', Completed=false
4
Title='Eat pizza', Completed=false
5
Title='Write tutorial', Completed=false
6
Title='Study Vue.js', Completed=false
7
Title='Go kayaking', Completed=false
8
<==========---> 80% EXECUTING [13s]
9
> :bootRun



Now, open a separate terminal window and perform a basic GET request on the server endpoint using the following command: http :9000, which is short for http GET http://localhost:9000.


Java
 




xxxxxxxxxx
1
15


 
1
HTTP/1.1 200
2
Connection: keep-alive
3
...
4
 
          
5
{
6
   "_links": {
7
       "profile": {
8
           "href": "http://localhost:9000/profile"
9
       },
10
       "todos": {
11
           "href": "http://localhost:9000/todos{?page,size,sort}",
12
           "templated": true
13
       }
14
   }
15
}



The profile link refers to the ALPS (Application-Level Profile Semantics). Take a look at the Spring docs on it. It’s a way to describe the available resources exposed by the REST API.

The todos link is the endpoint generated from the Todo class.

Perform a GET on the /todos endpoint:

Java
 




xxxxxxxxxx
1


 
1
http :9000/todos



You’ll see an output like below (for brevity I’ve omitted all but the first of the todo objects).

Java
 




xxxxxxxxxx
1
44


 
1
HTTP/1.1 200
2
Connection: keep-alive
3
Content-Type: application/hal+json
4
Date: Wed, 24 Jun 2020 02:35:59 GMT
5
Keep-Alive: timeout=60
6
Transfer-Encoding: chunked
7
Vary: Origin
8
Vary: Access-Control-Request-Method
9
Vary: Access-Control-Request-Headers
10
 
          
11
{
12
   "_embedded": {
13
       "todos": [
14
           {
15
               "_links": {
16
                   "self": {
17
                       "href": "http://localhost:9000/todos/1"
18
                   },
19
                   "todo": {
20
                       "href": "http://localhost:9000/todos/1"
21
                   }
22
               },
23
               "completed": false,
24
               "id": 1,
25
               "title": "Buy milk"
26
           },
27
           ...
28
       ]
29
   },
30
   "_links": {
31
       "profile": {
32
           "href": "http://localhost:9000/profile/todos"
33
       },
34
       "self": {
35
           "href": "http://localhost:9000/todos"
36
       }
37
   },
38
   "page": {
39
       "number": 0,
40
       "size": 20,
41
       "totalElements": 5,
42
       "totalPages": 1
43
   }
44
}



You can try adding a todo using a POST.

Java
 




xxxxxxxxxx
1
21


 
1
http POST :9000/todos title="Drink more coffee"
2
 
          
3
HTTP/1.1 201
4
Connection: keep-alive
5
Content-Type: application/json
6
...
7
 
          
8
{
9
   "_links": {
10
       "self": {
11
           "href": "http://localhost:9000/todos/6"
12
       },
13
       "todo": {
14
           "href": "http://localhost:9000/todos/6"
15
       }
16
   },
17
   "completed": false,
18
   "id": 6,
19
   "title": "Drink more coffee"
20
}
21
 
          



If you perform another GET on the /todos endpoint, you’ll see that there are now six todo items and the last item is your newly added todo.

Java
 




xxxxxxxxxx
1


 
1
http :9000/todos



Again, a lot of the output below has been omitted for brevity, but notice the page.totalElements value now equals six and that there is a new todo item.

Java
 




xxxxxxxxxx
1
32


 
1
HTTP/1.1 200
2
Connection: keep-alive
3
Content-Type: application/hal+json
4
...
5
 
          
6
{
7
   "_embedded": {
8
       "todos": [
9
           ...
10
           {
11
               "_links": {
12
                   "self": {
13
                       "href": "http://localhost:9000/todos/6"
14
                   },
15
                   "todo": {
16
                       "href": "http://localhost:9000/todos/6"
17
                   }
18
               },
19
               "completed": false,
20
               "id": 6,
21
               "title": "Drink more coffee"
22
           }
23
       ]
24
   },
25
   ...
26
   "page": {
27
       "number": 0,
28
       "size": 20,
29
       "totalElements": 6,
30
       "totalPages": 1
31
   }
32
}



Test the Vue.js Client App

I’m not going to go into a ton of detail on the Vue.js client app. A lot of this was covered in the previous tutorial and is largely the same. I will explicitly show you how to modify the unsecured client app (and server) to use Okta OAuth.

The Vue module that is the heart of the app is src/components/Todos.vue. This is what you see and what controls the application flow.

Another important file is src/Api.js. This module encapsulates the functionality for interacting with the resource server using the axios HTTP client module. If you look at the code below you’ll see that this module contains clear methods for Create, Read, Update, and Delete. You’ll also notice how I’m able to create a base axios instance that is configured with some global settings, such as the server URL and timeout. Later, this comes in handy when we need to configure the token authentication.

Java
 




xxxxxxxxxx
1
23


 
1
import axios from 'axios'
2
 
          
3
const SERVER_URL = 'http://localhost:9000';
4
 
          
5
const instance = axios.create({
6
   baseURL: SERVER_URL,
7
   timeout: 1000
8
});
9
 
          
10
export default {
11
   // (C)reate
12
   createNew: (text, completed) => instance.post('todos', {title: text, completed: completed}),
13
   // (R)ead
14
   getAll: () => instance.get('todos', {
15
       transformResponse: [function (data) {
16
           return data? JSON.parse(data)._embedded.todos : data;
17
       }]
18
   }),
19
   // (U)pdate
20
   updateForId: (id, text, completed) => instance.put('todos/'+id, {title: text, completed: completed}),
21
   // (D)elete
22
   removeForId: (id) => instance.delete('todos/'+id)
23
}



Open a shell in the /client sub-directory.

Before running the client app, install the dependencies: yarn install.

Go ahead and run the client using yarn serve. Make sure your resource server is still running, as well. If it’s not, run it using ./gradlew bootRun.

Open a browser and navigate to http://localhost:8080.

To dos

Try it out! You can edit existing todos, delete them, and create new ones.

Create an OIDC Application for Vue Authentication

You should have already signed up for a free Okta developer account. The next step is to create an OpenID Connect (OIDC) application. Once you’ve logged into your Okta developer dashboard, click on the Application top-menu item, and then on the Add Application button.

Select application type Single-Page App.

Click Next.

Give the app a name. I named mine Vue Client. Change the Login redirect URI to be http://localhost:8080/callback. Since you’re using OAuth 2.0’s authorization code flow, there’s no reason to have /implicit in your redirect URL.

The rest of the default values will work.

Click Done.

On the next screen, take note of the Client ID (near the bottom) as you’ll need it in a bit.

Application settings

Add Authentication to Vue

To configure Vue.js to use Okta as an OAuth 2.0 and OIDC provider, you’re going to use the okta-vue module. This greatly simplifies integrating Okta authentication into your client application. You can take a look at the project GitHub page for more info.

Stop your client Vue.js application. Open a shell and, from the client sub-directory of the example project, use Yarn to install okta-vue.

Java
 




xxxxxxxxxx
1


1
yarn add @okta/okta-vue@2.0.0



Now create a src/router.js file in the client app project.


Java
 




xxxxxxxxxx
1
34


 
1
import Auth from '@okta/okta-vue';
2
import Vue from 'vue'
3
import Router from 'vue-router'
4
import Todos from './components/Todos'
5
 
          
6
Vue.use(Auth, {
7
  issuer: 'https://{yourOktaDomain}/oauth2/default',
8
  clientId: '{yourClientId}',
9
  redirectUri: window.location.origin + '/callback'
10
});
11
 
          
12
Vue.use(Router);
13
 
          
14
let router = new Router({
15
  mode: 'history',
16
  routes: [
17
    {
18
      path: '/',
19
      name: 'Todos',
20
      component: Todos,
21
      meta: {
22
        requiresAuth: true
23
      }
24
    },
25
    {
26
      path: '/callback',
27
      component: Auth.handleCallback(),
28
    },
29
  ]
30
});
31
 
          
32
router.beforeEach(Vue.prototype.$auth.authRedirectGuard());
33
 
          
34
export default router;



Next, replace {yourClientId} with the Client ID from the OIDC app you just created. You’ll also need to change {yourOktaDomain} to your Okta developer domain—something like dev-123456.okta.com. Make sure to remove the {...} placeholders and just use the raw values.

The Okta Vue authentication plugin injects an authClient object into your Vue instance which can be accessed by calling this.$auth anywhere inside this instance.

There are only two routes. The home route is the todo app itself. The meta: { requiresAuth: true } } property turns on authentication for that route.

The other route, /callback, is the OAuth 2.0 callback route that handles a successful authentication from the Okta servers.

Now you need to update the src/main.js to use the router.

Add the following import statement near the top of the file:

Java
 




xxxxxxxxxx
1


 
1
import router from './router'



And update the Vue app instance to use the imported router, replacing the old Vue instance declaration:

Java
 




xxxxxxxxxx
1


 
1
new Vue({ 
2
 el: '#app', 
3
 router,  // <-- add this line
4
 template: '<App/>', 
5
 components: { App } 
6
})
7
 
          



Next, update the src/App.vue module to match the following:


Java
 




xxxxxxxxxx
1
27


1
<template>
2
  <div id="app">
3
    <router-view :activeUser="activeUser"/>
4
    <footer class="info">
5
      <p v-if="activeUser" class="logout-link"><a @click="handleLogout" href="#">Logout</a></p>
6
      <p>Based on a project written by <a href="http://evanyou.me">Evan You</a></p>
7
      <p>Original Vue TodoApp project is <a href="https://vuejs.org/v2/examples/todomvc.html">here</a></p>
8
      <p>Modified for this tutorial by Andrew Hughes</p>
9
    </footer>
10
  </div>
11
</template>
12
 
          
13
<script>
14
  // app Vue instance 
15
  const app = {
16
    name: 'app',
17
    // app initial state 
18
    data: () => {
19
      return {
20
        activeUser: null
21
      }
22
    },
23
 
          
24
    async created() {
25
      await this.refreshActiveUser()
26
    },
27
watch: {
28
      '$route': 'refreshActiveUser'
29
    },
30
 
          
31
    methods: {
32
      async refreshActiveUser() {
33
        this.activeUser = await this.$auth.getUser()
34
        this.$log.debug('activeUser', this.activeUser)
35
      },
36
 
          
37
      async handleLogout() {
38
        await this.$auth.logout()
39
        await this.refreshActiveUser()
40
        this.$router.push({ path: '/' })
41
      }
42
    },
43
  }
44
 
          
45
  export default app
46
</script>
47
 
          
48
<style>
49
  [v-cloak] {
50
    display: none;
51
  }
52
</style>
53
 
          



These changes demonstrate a couple of things. First, the code creates and updates a property, activeUser, that passes information to the Todos module about the currently active user (if there is one, or null if there isn’t). It also adds a logout link to the footer.

The last thing you need to do is update the src/Api.js file to add the access token to each request.

Java
 




xxxxxxxxxx
1
49


 
1
import axios from 'axios'
2
import Vue from 'vue'
3
 
          
4
const SERVER_URL = 'http://localhost:9000';
5
 
          
6
const instance = axios.create({
7
  baseURL: SERVER_URL,
8
  timeout: 1000
9
});
10
 
          
11
export default {
12
 
          
13
  async execute(method, resource, data, config) {
14
    let accessToken = await Vue.prototype.$auth.getAccessToken()
15
    return instance({
16
      method: method,
17
      url: resource,
18
      data,
19
      headers: {
20
        Authorization: `Bearer ${accessToken}`
21
      },
22
      ...config
23
    })
24
  },
25
 
          
26
  // (C)reate 
27
  createNew(text, completed) {
28
    return this.execute('POST', 'todos', {title: text, completed: completed})
29
  },
30
  
31
  // (R)ead 
32
  getAll() {
33
    return this.execute('GET', 'todos', null, {
34
      transformResponse: [function (data) {
35
        return data ? JSON.parse(data)._embedded.todos : data;
36
      }]
37
    })
38
  },
39
  
40
  // (U)pdate 
41
  updateForId(id, text, completed) {
42
    return this.execute('PUT', 'todos/' + id, {title: text, completed: completed})
43
  },
44
 
          
45
  // (D)elete 
46
  removeForId(id) {
47
    return this.execute('DELETE', 'todos/' + id)
48
  }
49
}


These changes take the access token from the Okta Vue Auth module and inject it into the API request methods.

Test Your Vue App’s OAuth Flow

At this point, you can run the client application and it will force you to log in.

Run: yarn serve and navigate to: http://localhost:8080.

You may need to use a private or incognito browser window to see the login screen.

Okta sign in

Once you log in, you’ll see the authenticated todo app with your email.

Authenticated todo app

You’re not done yet, however. The Spring Boot resource server is still unsecured and isn’t requiring a valid JSON Web Token yet.

Configure Spring Boot Server for JWT Auth

To add OAuth 2.0 JSON Web Token (JWT) authentication to your Spring Boot project, Okta provides a helper project, the Okta Spring Boot Starter (check out the GitHub project).

You need to add this as a dependency in the build.gradle file. Add the following dependency to the dependency block.

Java
 




xxxxxxxxxx
1


 
1
dependencies {
2
    implementation("com.okta.spring:okta-spring-boot-starter:1.4.0")
3
    ...
4
}



Next, create a new Kotlin file com/okta/springbootvue/SecurityConfiguration.kt. This file configures the Spring Boot project to authorize all requests and to use JWT authentication.

Java
 




xxxxxxxxxx
1
14


 
1
package com.okta.springbootvue 
2
 
          
3
import org.springframework.security.config.annotation.web.builders.HttpSecurity 
4
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 
5
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 
6
 
          
7
@EnableWebSecurity 
8
class SecurityConfiguration : WebSecurityConfigurerAdapter() { 
9
   override fun configure(http: HttpSecurity?) { 
10
       http!!.authorizeRequests() 
11
               .anyRequest().authenticated() 
12
               .and().oauth2ResourceServer().jwt() 
13
   } 
14
}



Finally, add some properties to your src/main/resources/application.properties file. Don’t forget to substitute in the correct values for your Okta domain and your OIDC Client ID (these are the same values you used above).

Java
 




xxxxxxxxxx
1


 
1
server.port=9000
2
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
3
okta.oauth2.clientId={yourClientId}



Start (or re-start) your Spring Boot resource server.

Java
 




xxxxxxxxxx
1


1
./gradlew bootRun



If you want to verify that this endpoint is now secure, use HTTPie to run a simple GET from another shell.

Java
 




xxxxxxxxxx
1


 
1
http :9000/todos



You’ll see that the previously public endpoint is now protected.

Java
 




xxxxxxxxxx
1


1
HTTP/1.1 401
2
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
3
Connection: keep-alive
4
...



That’s pretty much it. If you go back to the todo app, you’ll see that it’s still working with the protected resource server.

You can check out the auth branch of the example project to see the finished, fully authenticated code.

Java
 




xxxxxxxxxx
1


 
1
git clone -b auth https://github.com/oktadeveloper/okta-kotlin-spring-boot-vue-example.git



Moving Forward with Okta, Vue, and Spring Boot

This tutorial covered quite a lot. You built a Vue.js client application and a Spring Boot REST service, using them to demonstrate a fully functioning CRUD application. You also added authentication using Okta and the Okta Vue SDK.

If you’d like to dig a little deeper, take a look at the Okta Vue SDK project.

The Spring Boot REST service used Spring Data’s JPA implementation to persist data based on a Java class. Spring Data and JPA is a very complex area, and the Spring docs on it are a great place to learn more.

Okta also has a number of other great related tutorials.


If you have any questions about this post, please add a comment below. For more awesome content, follow @oktadev on Twitter, like us on Facebook, or subscribe to our YouTube channel.


Changelog:

Topics:
code, crud app, java, kotlin, spring boot, vue

Published at DZone with permission of Andrew Hughes , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}