{"id":86333,"date":"2019-01-17T15:27:03","date_gmt":"2019-01-17T13:27:03","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=86333"},"modified":"2019-01-28T09:12:11","modified_gmt":"2019-01-28T07:12:11","slug":"bootiful-development-spring-boot-vue","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2019\/01\/bootiful-development-spring-boot-vue.html","title":{"rendered":"Bootiful Development with Spring Boot and Vue"},"content":{"rendered":"<p><span style=\"font-size: 20px;\"><b>\u201cI love writing authentication and authorization code.\u201d ~ No Java Developer Ever.<\/b> Tired of building the same login screens over and over? <a href=\"https:\/\/developer.okta.com\/signup?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Try the Okta API for hosted authentication, authorization, and multi-factor auth.<\/a><\/span><\/p>\n<p>Vue is a web framework that\u2019s gotten a lot of attention lately because it\u2019s lean and mean. Its baseline framework cost is around 40K and is known as a minimalistic web framework. With all of the recent attention on web performance and mobile-first, mobile-fast, it\u2019s no surprise that Vue has become more and more popular. If you spent the time to learn AngularJS back in the day, chances are you\u2019ll find an old friend in Vue.js.<\/p>\n<p>Spring Boot is one of my favorite frameworks in the Java ecosystem. Yes, I\u2019m biased. I\u2019ve been a fan of the Spring Framework since way back in 2004. It was neat to be able to write Java webapps with Spring MVC, but most people used XML to configure things. Even though Spring supported JavaConfig, it wasn\u2019t until Spring Boot (in 2014) that it really took off. Nowadays, you never see a Spring tutorial that shows you how to configure things with XML. Nice work, Spring Boot team!<\/p>\n<p>I\u2019m writing this tutorial because I\u2019m a big fan of Vue. If you know me, you\u2019ll know I\u2019m a web framework aficionado. That is, I\u2019m a big fan of web frameworks. Much like an NBA fan has a few favorite players, I have a few favorite frameworks. Vue has recently become one of those, and I\u2019d like to show you why.<\/p>\n<p>In this post, I\u2019ll show you how to build a Spring Boot API using Spring Data JPA and Hibernate. Then I\u2019ll show you how to create a Vue PWA and customize it to display the data from your API. Then you\u2019ll add in some animated gifs, a sprinkle of authentication, and have a jolly good time doing it!<\/p>\n<h2 class=\"wp-block-heading\" id=\"build-a-rest-api-with-spring-boot\">Build a REST API with Spring Boot<\/h2>\n<p>To get started with Spring Boot, navigate to&nbsp;<a href=\"https:\/\/start.spring.io\/\">start.spring.io<\/a>&nbsp;and choose version 2.1.1+. In the &#8220;Search for dependencies&#8221; field, select the following:<\/p>\n<ul class=\"wp-block-list\">\n<li><a href=\"http:\/\/www.h2database.com\/html\/main.html\">H2<\/a>: An in-memory database<\/li>\n<li><a href=\"https:\/\/projectlombok.org\/\">Lombok<\/a>: Because no one likes generating (or even worse, writing!) getters and setters<\/li>\n<li><a href=\"http:\/\/hibernate.org\/orm\/\">JPA<\/a>: Standard ORM for Java<\/li>\n<li><a href=\"http:\/\/projects.spring.io\/spring-data-rest\/\">Rest Repositories<\/a>: Allows you to expose your JPA repositories as REST endpoints<\/li>\n<li><a href=\"https:\/\/github.com\/spring-projects\/spring-boot\/blob\/master\/spring-boot-project\/spring-boot-starters\/spring-boot-starter-web\/pom.xml\">Web<\/a>: Spring MVC with Jackson (for JSON), Hibernate Validator, and embedded Tomcat<\/li>\n<\/ul>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/start.spring.io_-1024x647.png\" alt=\"Bootiful Development\" class=\"wp-image-86334\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/start.spring.io_-1024x647.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/start.spring.io_-300x190.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/start.spring.io_-768x485.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/start.spring.io_.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>If you like the command-line better, install&nbsp;<a href=\"https:\/\/httpie.org\/\">HTTPie<\/a>&nbsp;and run the following command to download a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">demo.zip<\/code>.<\/p>\n<pre class=\"gutter: false;brush:bash\">http https:\/\/start.spring.io\/starter.zip dependencies==h2,lombok,data-jpa,data-rest,web \\\n  packageName==com.okta.developer.demo -d\n<\/pre>\n<p>Create a directory called&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">spring-boot-vue-example<\/code>. Expand the contents of&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">demo.zip<\/code>&nbsp;into its&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">server<\/code>&nbsp;directory.<\/p>\n<pre class=\"gutter: false;brush:bash\">mkdir spring-boot-vue-example\nunzip demo.zip -d spring-boot-vue-example\/server\n<\/pre>\n<p>Open the &#8220;server&#8221; project in your favorite IDE and run&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">DemoApplication<\/code>&nbsp;or start it from the command line using&nbsp;<code>.\/mvnw spring-boot:run<\/code>.<\/p>\n<p>Create a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">com.okta.developer.demo.beer<\/code>&nbsp;package and a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">Beer.java<\/code>&nbsp;file in it. This class will be the entity that holds your data.<\/p>\n<pre class=\"gutter: false;brush:java\">package com.okta.developer.demo.beer;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.NonNull;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n\n@Data\n@NoArgsConstructor\n@Entity\nclass Beer {\n\n    public Beer(String name) {\n        this.name = name;\n    }\n\n    @Id\n    @GeneratedValue\n    private Long id;\n\n    @NonNull\n    private String name;\n}\n<\/pre>\n<p>Add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerRepository<\/code>&nbsp;class that leverages Spring Data to do CRUD on this entity.<\/p>\n<pre class=\"gutter: false;brush:java\">package com.okta.developer.demo.beer;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.data.rest.core.annotation.RepositoryRestResource;\n\n@RepositoryRestResource\ninterface BeerRepository extends JpaRepository&lt;Beer, Long&gt; {\n}\n<\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> Adding the&nbsp;<a href=\"http:\/\/docs.spring.io\/spring-data\/rest\/docs\/2.6.x\/api\/org\/springframework\/data\/rest\/core\/annotation\/RepositoryRestResource.html\"><code>@RepositoryRestResource<\/code><\/a>&nbsp;annotation to&nbsp;<code>BeerRepository<\/code>&nbsp;exposes all its CRUD operations as REST endpoints. <\/p>\n<\/blockquote>\n<p>Add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerCommandLineRunner<\/code>&nbsp;that uses this repository and creates a default set of data.<\/p>\n<pre class=\"gutter: false;brush:java\">package com.okta.developer.demo.beer;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\n\nimport java.util.stream.Stream;\n\n@Component\npublic class BeerCommandLineRunner implements CommandLineRunner {\n\n    private final BeerRepository repository;\n\n    public BeerCommandLineRunner(BeerRepository repository) {\n        this.repository = repository;\n    }\n\n    @Override\n    public void run(String... strings) throws Exception {\n        \/\/ Top beers from https:\/\/www.beeradvocate.com\/lists\/us, November 2018\n        Stream.of(\"Kentucky Brunch Brand Stout\", \"Marshmallow Handjee\", \"Barrel-Aged Abraxas\",\n            \"Hunahpu's Imperial Stout\", \"King Julius\", \"Heady Topper\",\n            \"Budweiser\", \"Coors Light\", \"PBR\").forEach(name -&gt;\n            repository.save(new Beer(name))\n        );\n        repository.findAll().forEach(System.out::println);\n    }\n}\n<\/pre>\n<p>Restart your app, and you should see a list of beers printed in your terminal.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/beers-in-terminal-1024x417.png\" alt=\"Bootiful Development\" class=\"wp-image-86335\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/beers-in-terminal-1024x417.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/beers-in-terminal-300x122.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/beers-in-terminal-768x313.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/beers-in-terminal.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>Add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerController<\/code>&nbsp;class to create an endpoint that filters out less-than-great beers.<\/p>\n<pre class=\"gutter: false;brush:java\">\npackage com.okta.developer.demo.beer;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Collection;\nimport java.util.stream.Collectors;\n\n@RestController\npublic class BeerController {\n    private BeerRepository repository;\n\n    public BeerController(BeerRepository repository) {\n        this.repository = repository;\n    }\n\n    @GetMapping(\"\/good-beers\")\n    public Collection<Beer> goodBeers() {\n        return repository.findAll().stream()\n                .filter(this::isGreat)\n                .collect(Collectors.toList());\n    }\n\n    private boolean isGreat(Beer beer) {\n        return !beer.getName().equals(\"Budweiser\") &amp;&amp;\n                !beer.getName().equals(\"Coors Light\") &amp;&amp;\n                !beer.getName().equals(\"PBR\");\n    }\n}\n<\/pre>\n<p>Re-build your application and navigate to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8080\/good-beers\">http:\/\/localhost:8080\/good-beers<\/a><\/code>. You should see the list of good beers in your browser.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/good-beers-json-1024x881.png\" alt=\"Bootiful Development\" class=\"wp-image-86336\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/good-beers-json-1024x881.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/good-beers-json-300x258.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/good-beers-json-768x660.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/good-beers-json.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>You should also see the same result in your terminal window when using HTTPie.<\/p>\n<pre class=\"gutter: false;brush:bash\">http :8080\/good-beers\n<\/pre>\n<h2 class=\"wp-block-heading\" id=\"create-a-project-with-vue-cli\">Create a Project with Vue CLI<\/h2>\n<p>Creating an API seems to be the easy part these days, thanks in large part to Spring Boot. In this section, I hope to show you that creating a UI with Vue is pretty simple too. I\u2019ll also show you how to develop the Vue app with TypeScript. If you follow the steps below, you\u2019ll create a new Vue app, fetch beer names and images from APIs, and create components to display their data.<\/p>\n<p>To create a Vue project, make sure you have&nbsp;<a href=\"https:\/\/nodejs.org\/\">Node.js<\/a>, and&nbsp;<a href=\"https:\/\/cli.vuejs.org\/\">Vue CLI 3<\/a>&nbsp;installed. I used Node 11.3.0 when I created this tutorial.<\/p>\n<pre class=\"gutter: false;brush:bash\">npm install -g @vue\/cli@3.2.1<\/pre>\n<p>From a terminal window, cd into the root of the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">spring-boot-vue-example<\/code>&nbsp;directory and run the following command. This command will create a new Vue application and prompt you for options.<\/p>\n<pre class=\"gutter: false;brush:bash\">vue create client<\/pre>\n<p>When prompted to pick a present, choose&nbsp;<strong>Manually select features<\/strong>.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-cli-features-1024x450.png\" alt=\"Bootiful Development\" class=\"wp-image-86337\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-cli-features-1024x450.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-cli-features-300x132.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-cli-features-768x337.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>Check the&nbsp;<strong>TypeScript<\/strong>,&nbsp;<strong>PWA<\/strong>, and&nbsp;<strong>Router<\/strong>&nbsp;features. Choose the defaults (by pressing&nbsp;<strong>Enter<\/strong>) for the rest of the questions.<\/p>\n<p>In a terminal window, cd into the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client<\/code>&nbsp;directory and open&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">package.json<\/code>&nbsp;in your favorite editor. Add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">start<\/code>&nbsp;script that\u2019s the same as the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">serve<\/code>&nbsp;script.<\/p>\n<pre class=\"gutter: false;brush:js\">\"scripts\": {\n  \"start\": \"vue-cli-service serve\",\n  \"serve\": \"vue-cli-service serve\",\n  \"build\": \"vue-cli-service build\",\n  \"lint\": \"vue-cli-service lint\"\n},\n<\/pre>\n<p>Now you can start your Vue app using&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">npm start<\/code>. Your Spring Boot app should be still running on port 8080, which will cause your Vue app to use port 8081. I expect you to run your Vue app on 8081 throughout this tutorial. To ensure it always runs on this port, create a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/vue.config.js<\/code>&nbsp;file and add the following JavaScript to it.<\/p>\n<pre class=\"gutter: false;brush:bash\">module.exports = {\n  devServer: {\n    port: 8081\n  }\n};\n<\/pre>\n<p>Open&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8081\/\">http:\/\/localhost:8081<\/a><\/code>&nbsp;in your browser, and you should see a page like the one below.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-welcome-1024x703.png\" alt=\"Bootiful Development\" class=\"wp-image-86338\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-welcome-1024x703.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-welcome-300x206.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-welcome-768x527.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<h3 class=\"wp-block-heading\" id=\"create-a-good-beers-ui-in-vue\">Create a Good Beers UI in Vue<\/h3>\n<p>So far, you\u2019ve created a good beers API and a Vue client, but you haven\u2019t created the UI to display the list of beers from your API. To do this, open&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/views\/Home.vue<\/code>&nbsp;and add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">created()<\/code>&nbsp;method.<\/p>\n<pre class=\"gutter: false;brush:java\">import axios from 'axios';\n...\n\nprivate async created() {\n  const response = await axios.get('\/good-beers');\n  this.beers = await response.data;\n}\n<\/pre>\n<p>Vue\u2019s component lifecycle will call the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">created()<\/code>&nbsp;method.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> John Papa\u2019s&nbsp;<a href=\"https:\/\/johnpapa.net\/vue-typescript\/\">Vue.js with TypeScript<\/a>&nbsp;was a big help in figuring out how to use TypeScript with Vue. Vue\u2019s&nbsp;<a href=\"https:\/\/vuejs.org\/v2\/guide\/typescript.html\">TypeScript docs<\/a>&nbsp;were also helpful. <\/p>\n<\/blockquote>\n<p>You\u2019ll need to install&nbsp;<a href=\"https:\/\/www.npmjs.com\/package\/axios\">axios<\/a>&nbsp;for this code to compile.<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\"gutter: false;brush:bash\">npm i axios<\/pre>\n<p>You can see this puts the response data into a local&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">beers<\/code>&nbsp;variable. To properly define this variable, create a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">Beer<\/code>&nbsp;interface and initialize the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">Home<\/code>&nbsp;class\u2019s&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">beers<\/code>&nbsp;variable to be an empty array.<\/p>\n<pre class=\"gutter: false;brush:java\">export interface Beer {\n  id: number;\n  name: string;\n  giphyUrl: string;\n}\n\n@Component({\n  components: {\n    HelloWorld,\n  },\n})\nexport default class Home extends Vue {\n  public beers: Beer[] = [];\n\n  private async created() {\n    const response = await axios.get('\/good-beers');\n    this.beers = await response.data;\n  }\n}\n<\/pre>\n<p>A keen eye will notice this makes a request to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">\/good-beers<\/code>&nbsp;on the same port as the Vue application (since it\u2019s a relative URL). For this to work, you\u2019ll need to modify&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/vue.config.js<\/code>&nbsp;to have a proxy that sends this URL to your Spring Boot app.<\/p>\n<pre class=\"gutter: false;brush:bash\">module.exports = {\n  devServer: {\n    port: 8081,\n    proxy: {\n      \"\/good-beers\": {\n        target: \"http:\/\/localhost:8080\",\n        secure: false\n      }\n    }\n  }\n};\n<\/pre>\n<p>Modify the template in&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/views\/Home.vue<\/code>&nbsp;to display the list of good beers from your API.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;div class=\"home\"&gt;\n    &lt;img alt=\"Vue logo\" src=\"..\/assets\/logo.png\"&gt;\n    &lt;h1&gt;Beer List&lt;\/h1&gt;\n    &lt;div v-for=\"beer in beers\"&gt;\n      {{ beer.name }}\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n<\/pre>\n<p>Restart your Vue app using&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">npm start<\/code>&nbsp;and refresh your app on&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8081\/\">http:\/\/localhost:8081<\/a><\/code>. You should see a list of beers from your Spring Boot API.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-1024x743.png\" alt=\"Bootiful Development\" class=\"wp-image-86339\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-1024x743.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-300x218.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-768x557.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list.png 1990w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<h3 class=\"wp-block-heading\" id=\"create-a-beerlist-component\">Create a BeerList Component<\/h3>\n<p>To make this application easier to maintain, move the beer list logic and rendering to its own&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerList<\/code>&nbsp;component. Create&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/components\/BeerList.vue<\/code>&nbsp;and populate it with the code from&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">Home.vue<\/code>. Remove the Vue logo, customize the template\u2019s main class name, and remove the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">HelloWorld<\/code>&nbsp;component. It should look as follows when you\u2019re done.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;div class=\"beer-list\"&gt;\n    &lt;h1&gt;Beer List&lt;\/h1&gt;\n    &lt;div v-for=\"beer in beers\"&gt;\n      {{ beer.name }}\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script lang=\"ts\"&gt;\nimport { Component, Vue } from 'vue-property-decorator';\nimport axios from 'axios';\n\nexport interface Beer {\n  id: number;\n  name: string;\n  giphyUrl: string;\n}\n\n@Component\nexport default class BeerList extends Vue {\n  public beers: Beer[] = [];\n\n  private async created() {\n    const response = await axios.get('\/good-beers');\n    this.beers = await response.data;\n  }\n}\n&lt;\/script&gt;\n<\/pre>\n<p>Then change&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/views\/Home.vue<\/code>&nbsp;so it only contains the logo and a reference to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;BeerList\/&gt;<\/code>.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;div class=\"home\"&gt;\n    &lt;img alt=\"Vue logo\" src=\"..\/assets\/logo.png\"&gt;\n    &lt;BeerList\/&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script lang=\"ts\"&gt;\nimport { Component, Vue } from 'vue-property-decorator';\nimport BeerList from '@\/components\/BeerList.vue';\n\n@Component({\n  components: {\n    BeerList,\n  },\n})\nexport default class Home extends Vue {}\n&lt;\/script&gt;\n<\/pre>\n<h3 class=\"wp-block-heading\" id=\"create-a-giphyimage-component\">Create a GiphyImage Component<\/h3>\n<p>To make things look a little better, add a&nbsp;<a href=\"http:\/\/giphy.com\/\">GIPHY<\/a>&nbsp;component to fetch images based on the beer\u2019s name. Create&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/components\/GiphyImage.vue<\/code>&nbsp;and place the following code inside it.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;img :src=giphyUrl v-bind:alt=name height=\"200\"\/&gt;\n&lt;\/template&gt;\n\n&lt;script lang=\"ts\"&gt;\nimport { Component, Prop, Vue } from 'vue-property-decorator';\nimport axios from 'axios';\n\n@Component\nexport default class GiphyImage extends Vue {\n  @Prop() private name!: string;\n  private giphyUrl: string = '';\n\n  private async created() {\n    const giphyApi = '\/\/api.giphy.com\/v1\/gifs\/search?api_key=dc6zaTOxFJmzC&amp;limit=1&amp;q=';\n\n    const response = await axios.get(giphyApi + this.name);\n    const data = await response.data.data;\n    if (data.length) {\n      this.giphyUrl = data[0].images.original.url;\n    } else {\n      this.giphyUrl = '\/\/media.giphy.com\/media\/YaOxRsmrv9IeA\/giphy.gif';\n    }\n  }\n}\n&lt;\/script&gt;\n\n&lt;!-- The \"scoped\" attribute limits CSS to this component only --&gt;\n&lt;style scoped&gt;\nimg {\n  margin: 10px 0 0;\n}\n&lt;\/style&gt;\n<\/pre>\n<p>Change&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerList.vue<\/code>&nbsp;to use the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;GiphyImage\/&gt;<\/code>&nbsp;component in its template:<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;div v-for=\"beer in beers\"&gt;\n  {{ beer.name }}&lt;br\/&gt;\n  &lt;GiphyImage :name=\"beer.name\"\/&gt;\n&lt;\/div&gt;<\/pre>\n<p>And add it to the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">components<\/code>&nbsp;list in the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;script&gt;<\/code>&nbsp;block:<\/p>\n<pre class=\"gutter: false;brush:java\">import GiphyImage from '@\/components\/GiphyImage.vue';\n\n@Component({\n  components: {GiphyImage},\n})\nexport default class BeerList extends Vue { ... }\n<\/pre>\n<p>In this same file, add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;style&gt;<\/code>&nbsp;section at the bottom and use&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_Grid_Layout\/Auto-placement_in_CSS_Grid_Layout\">CSS Grid layout<\/a>&nbsp;to organize the beers in rows.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;style scoped&gt;\n.grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  grid-gap: 10px;\n  grid-auto-rows: minmax(100px, auto);\n}\n&lt;\/style&gt;\n<\/pre>\n<p>You\u2019ll need to wrap a div around the beer list template for this to have any effect.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;div class=\"grid\"&gt;\n  &lt;div v-for=\"beer in beers\"&gt;\n    {{ beer.name }}&lt;br\/&gt;\n    &lt;GiphyImage :name=\"beer.name\"\/&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;\n<\/pre>\n<p>After making these changes, your UI should look something like the following list of beer names and matching images.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-giphy-1024x816.png\" alt=\"Bootiful Development\" class=\"wp-image-86342\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-giphy-1024x816.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-giphy-300x239.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-giphy-768x612.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/vue-beer-list-giphy.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>You just created a Vue app that talks to a Spring Boot API. Congratulations! \ud83c\udf89<\/p>\n<h2 class=\"wp-block-heading\" id=\"add-pwa-support\">Add PWA Support<\/h2>\n<p>Vue CLI has support for progressive web applications (PWAs) out-of-the-box. When you created your Vue app, you selected PWA as a feature.<\/p>\n<p>PWA features are only enabled in production, because having assets cached in development can be a real pain. Run&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">npm run build<\/code>&nbsp;in the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client<\/code>&nbsp;directory to create a build ready for production. Then use&nbsp;<a href=\"https:\/\/www.npmjs.com\/package\/serve\">serve<\/a>&nbsp;to create a web server and show your app.<\/p>\n<pre class=\"gutter: false;brush:bash\">npm i -g serve\nserve -s dist -p 8081<\/pre>\n<p>You should be able to open your browser and see your app at&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8081\/\">http:\/\/localhost:8081<\/a><\/code>. When I first tried this, I found that loading the page didn\u2019t render any beer names and all the images were the same. This is because the client attempts to make a request to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">\/good-beers<\/code>&nbsp;and there\u2019s no proxy configured in production mode.<\/p>\n<p>To fix this issue, you\u2019ll need to change the URL in the client and configure Spring Boot to allow cross-domain access from&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8081\/\">http:\/\/localhost:8081<\/a><\/code>.<\/p>\n<p>Modify&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/components\/BeerList.vue<\/code>&nbsp;to use the full URL to your Spring Boot API.<\/p>\n<pre class=\"gutter: false;brush:java\">private async created() {\n  const response = await axios.get('http:\/\/localhost:8080\/good-beers');\n  this.beers = await response.data;\n}\n<\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> If you don\u2019t see any changes in your UI after making these changes, it\u2019s because your browser has cached your app. Use an incognito window, or clear your cache (in Chrome:&nbsp;<strong>Developer Tools<\/strong>&nbsp;&gt;&nbsp;<strong>Application<\/strong>&nbsp;&gt;&nbsp;<strong>Clear storage<\/strong>&gt;&nbsp;<strong>Clear site data<\/strong>) to fix this issue. <\/p>\n<\/blockquote>\n<h3 class=\"wp-block-heading\" id=\"configure-cors-for-spring-boot\">Configure CORS for Spring Boot<\/h3>\n<p>In the server project, open&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">src\/main\/java\/\u2026\u200b\/demo\/beer\/BeerController.java<\/code>&nbsp;and add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">@CrossOrigin<\/code>&nbsp;annotation to enable cross-origin resource sharing (CORS) from the client (<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8081\/\">http:\/\/localhost:8081<\/a><\/code>).<\/p>\n<pre class=\"gutter: false;brush:java\">import org.springframework.web.bind.annotation.CrossOrigin;\n...\n    @GetMapping(\"\/good-beers\")\n    @CrossOrigin(origins = \"http:\/\/localhost:8081\")\n    public Collection&lt;Beer&gt; goodBeers() {\n<\/pre>\n<p>After making these changes, rebuild your Vue app for production, refresh your browser, and everything should render as expected.<\/p>\n<h3 class=\"wp-block-heading\" id=\"use-lighthouse-to-see-your-pwa-score\">Use Lighthouse to See Your PWA Score<\/h3>\n<p>I ran a&nbsp;<a href=\"https:\/\/developers.google.com\/web\/tools\/lighthouse\/\">Lighthouse<\/a>&nbsp;audit in Chrome and found that this app scores a 81\/100 at this point. The most prominent complaint from this report was that I wasn\u2019t using HTTPS. To see how the app would score when it used HTTPS, I deployed it to&nbsp;<a href=\"https:\/\/pivotal.io\/platform\">Pivotal Cloud Foundry<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/www.heroku.com\/\">Heroku<\/a>. I was pumped to discover it scored high on both platforms.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-heroku-beaded4-1024x513.png\" alt=\"Bootiful Development\" class=\"wp-image-86343\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-heroku-beaded4-1024x513.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-heroku-beaded4-300x150.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-heroku-beaded4-768x384.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-heroku-beaded4.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-cloudfoundry-1024x509.png\" alt=\"Bootiful Development\" class=\"wp-image-86344\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-cloudfoundry-1024x509.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-cloudfoundry-300x149.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-cloudfoundry-768x382.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/lighthouse-cloudfoundry.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>The reason it scores a 96 is because&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">The viewport size is 939px, whereas the window size is 412px.<\/code>&nbsp;I\u2019m not sure what\u2019s causing this issue, maybe it\u2019s the CSS Grid layout?<\/p>\n<p>To see the scripts I used to deploy everything, see&nbsp;<a href=\"https:\/\/github.com\/oktadeveloper\/spring-boot-vue-example\/blob\/master\/heroku.sh\"><code style=\"font-size:13px\" class=\"highlighter-rouge\">heroku.sh<\/code><\/a>&nbsp;and&nbsp;<a href=\"https:\/\/github.com\/oktadeveloper\/spring-boot-vue-example\/blob\/master\/cloudfoundry.sh\"><code style=\"font-size:13px\" class=\"highlighter-rouge\">cloudfoundry.sh<\/code><\/a>&nbsp;in this post\u2019s companion GitHub repository.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> You will need to initialize Git before running the deployment scripts. Run&nbsp;<code>rm -rf client\/.git<\/code>, followed by&nbsp;<code>git commit -a \"Add project\"<\/code>. <\/p>\n<\/blockquote>\n<h2 class=\"wp-block-heading\" id=\"add-authentication-with-okta\">Add Authentication with Okta<\/h2>\n<p>You might be thinking, &#8220;this is pretty cool, it\u2019s easy to see why people dig Vue.&#8221; There\u2019s another tool you might dig after you\u2019ve tried it: Authentication with Okta! Why Okta? Because you can get&nbsp;<a href=\"https:\/\/developer.okta.com\/pricing\/?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">1,000 active monthly users for free<\/a>! It\u2019s worth a try, especially when you see how easy it is to add auth to Spring Boot and Vue with Okta.<\/p>\n<h3 class=\"wp-block-heading\" id=\"okta-spring-boot-starter\">Okta Spring Boot Starter<\/h3>\n<p>To secure your API, you can use&nbsp;<a href=\"https:\/\/github.com\/okta\/okta-spring-boot\">Okta\u2019s Spring Boot Starter<\/a>. To integrate this starter, add the following dependencies to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">server\/pom.xml<\/code>:<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;dependency&gt;\n    &lt;groupId&gt;com.okta.spring&lt;\/groupId&gt;\n    &lt;artifactId&gt;okta-spring-boot-starter&lt;\/artifactId&gt;\n    &lt;version&gt;0.6.1&lt;\/version&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.security.oauth.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-security-oauth2-autoconfigure&lt;\/artifactId&gt;\n    &lt;version&gt;2.1.1.RELEASE&lt;\/version&gt;\n&lt;\/dependency&gt;\n<\/pre>\n<p>Now you need to configure the server to use Okta for authentication. You\u2019ll need to create an OIDC app in Okta for that.<\/p>\n<h3 class=\"wp-block-heading\" id=\"create-an-oidc-app-in-okta\">Create an OIDC App in Okta<\/h3>\n<p>Log in to your Okta Developer account (or&nbsp;<a href=\"https:\/\/developer.okta.com\/signup\/?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">sign up<\/a>&nbsp;if you don\u2019t have an account) and navigate to&nbsp;<strong>Applications<\/strong>&nbsp;&gt;&nbsp;<strong>Add Application<\/strong>. Click&nbsp;<strong>Single-Page App<\/strong>, click&nbsp;<strong>Next<\/strong>, and give the app a name you\u2019ll remember. Change all instances of&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">localhost:8080<\/code>&nbsp;to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">localhost:8081<\/code>&nbsp;and click&nbsp;<strong>Done<\/strong>.<\/p>\n<p>Copy the client ID into your&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">server\/src\/main\/resources\/application.properties<\/code>&nbsp;file. While you\u2019re in there, add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">okta.oauth2.issuer<\/code>&nbsp;property that matches your Okta domain. For example:<\/p>\n<pre class=\"gutter: false;brush:bash\">okta.oauth2.issuer=https:\/\/{yourOktaDomain}\/oauth2\/default\nokta.oauth2.client-id={yourClientId}\n<\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> Replace&nbsp;<code>{yourOktaDomain}<\/code>&nbsp;with your org URL, which you can find on the Dashboard of the Developer Console. Make sure you don\u2019t include&nbsp;<code>-admin<\/code>&nbsp;in the value! <\/p>\n<\/blockquote>\n<p>Update&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">server\/src\/main\/java\/\u2026\u200b\/demo\/DemoApplication.java<\/code>&nbsp;to enable it as a resource server.<\/p>\n<pre class=\"gutter: false;brush:java; wrap-lines:false\">import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\n\n@EnableResourceServer\n@SpringBootApplication\n<\/pre>\n<p>After making these changes, you should be able to restart the server and see access denied when you try to navigate to&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\"><a href=\"http:\/\/localhost:8080\/\">http:\/\/localhost:8080<\/a><\/code>.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/access-denied-error-1024x413.png\" alt=\"Bootiful Development\" class=\"wp-image-86345\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/access-denied-error-1024x413.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/access-denied-error-300x121.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/access-denied-error-768x310.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/access-denied-error.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<h3 class=\"wp-block-heading\" id=\"oktas-vue-support\">Okta\u2019s Vue Support<\/h3>\n<p>Okta\u2019s Vue SDK allows you to integrate OIDC into a Vue application. You can learn more about Okta\u2019s Vue SDK can be&nbsp;<a href=\"https:\/\/www.npmjs.com\/package\/@okta\/okta-vue\">found on npmjs.com<\/a>. To install, run the following commands in the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client<\/code>&nbsp;directory:<\/p>\n<pre class=\"gutter: false;brush:bash\">npm i @okta\/okta-vue@1.0.7\nnpm i -D @types\/okta__okta-vue\n<\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The types for Okta\u2019s Vue SDK may be included in a future release. I&nbsp;<a href=\"https:\/\/github.com\/okta\/okta-oidc-js\/pull\/353\">created a pull request<\/a>&nbsp;to add them. <\/p>\n<\/blockquote>\n<p>Open&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/router.ts<\/code>&nbsp;and add your Okta configuration. The&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">router.ts<\/code>&nbsp;below also includes a path for the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerList<\/code>, a callback that\u2019s required for authentication, and a navigation guard to require authentication for the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">\/beer-list<\/code>&nbsp;path. Replace yours with this one, then update&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">yourClientDomain<\/code>&nbsp;and&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">yourClientId<\/code>&nbsp;to match your settings. Make sure to remove the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">{}<\/code>since those are just placeholders.<\/p>\n<pre class=\"gutter: false;brush:java\">import Vue from 'vue';\nimport Router from 'vue-router';\nimport Home from '.\/views\/Home.vue';\nimport OktaVuePlugin from '@okta\/okta-vue';\nimport BeerList from '@\/components\/BeerList.vue';\n\nVue.use(Router);\nVue.use(OktaVuePlugin, {\n  issuer: 'https:\/\/{yourOktaDomain}\/oauth2\/default',\n  client_id: '{yourClientId}',\n  redirect_uri: window.location.origin + '\/implicit\/callback',\n  scope: 'openid profile email',\n});\n\nconst router = new Router({\n  mode: 'history',\n  base: process.env.BASE_URL,\n  routes: [\n    {\n      path: '\/',\n      name: 'home',\n      component: Home,\n    },\n    {\n      path: '\/about',\n      name: 'about',\n      \/\/ route level code-splitting\n      \/\/ this generates a separate chunk (about.[hash].js) for this route\n      \/\/ which is lazy-loaded when the route is visited.\n      component: () =&gt; import(\/* webpackChunkName: \"about\" *\/ '.\/views\/About.vue'),\n    },\n    {\n      path: '\/beer-list',\n      name: 'beer-list',\n      component: BeerList,\n      meta: {\n        requiresAuth: true,\n      },\n    },\n    { path: '\/implicit\/callback', component: OktaVuePlugin.handleCallback() },\n  ],\n});\n\nrouter.beforeEach(Vue.prototype.$auth.authRedirectGuard());\n\nexport default router;\n<\/pre>\n<p>Since you have a route for&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">BeerList<\/code>&nbsp;remove it from&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/views\/Home.vue<\/code>.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;div class=\"home\"&gt;\n    &lt;img alt=\"Vue logo\" src=\"..\/assets\/logo.png\"&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script lang=\"ts\"&gt;\nimport { Component, Vue } from 'vue-property-decorator';\n\n@Component\nexport default class Home extends Vue {}\n&lt;\/script&gt;\n<\/pre>\n<p>Add a link to the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&gt;BeerList<\/code>&nbsp;in&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/App.vue<\/code>. You\u2019ll also need to add code that detects if the user is logged in or not. Replace the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;template&gt;<\/code>&nbsp;section and add the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">&lt;script&gt;<\/code>&nbsp;below to your&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">App.vue<\/code>.<\/p>\n<pre class=\"gutter: false;brush:xml\">&lt;template&gt;\n  &lt;div id=\"app\"&gt;\n    &lt;div id=\"nav\"&gt;\n      &lt;router-link to=\"\/\"&gt;Home&lt;\/router-link&gt; |\n      &lt;router-link to=\"\/about\"&gt;About&lt;\/router-link&gt;\n      &lt;template v-if=\"authenticated\"&gt; |\n        &lt;router-link to=\"\/beer-list\"&gt;Good Beers&lt;\/router-link&gt;\n      &lt;\/template&gt;\n    &lt;\/div&gt;\n    &lt;button v-if=\"authenticated\" v-on:click=\"logout\"&gt;Logout&lt;\/button&gt;\n    &lt;button v-else v-on:click=\"$auth.loginRedirect()\"&gt;Login&lt;\/button&gt;\n    &lt;router-view\/&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script lang=\"ts\"&gt;\nimport { Component, Vue, Watch } from 'vue-property-decorator';\n\n@Component\nexport default class App extends Vue {\n  public authenticated: boolean = false;\n\n  private created() {\n    this.isAuthenticated();\n  }\n\n  @Watch('$route')\n  private async isAuthenticated() {\n    this.authenticated = await this.$auth.isAuthenticated();\n  }\n\n  private async logout() {\n    await this.$auth.logout();\n    await this.isAuthenticated();\n\n    \/\/ Navigate back to home\n    this.$router.push({path: '\/'});\n  }\n}\n&lt;\/script&gt;\n<\/pre>\n<p>Restart your Vue app and you should see a button to log in.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/login-button-1024x599.png\" alt=\"Bootiful Development\" class=\"wp-image-86346\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/login-button-1024x599.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/login-button-300x176.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/login-button-768x449.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/login-button.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>Click on it and you\u2019ll be redirected to Okta. Enter the credentials you used to sign up for Okta and you\u2019ll be redirected back to the app. You should see a Logout button and a link to see some good beers.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><img decoding=\"async\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/post-login-1024x599.png\" alt=\"Bootiful Development\" class=\"wp-image-86347\" width=\"820\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/post-login-1024x599.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/post-login-300x176.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/post-login-768x449.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/post-login.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>If you click to on the&nbsp;<strong>Good Beers<\/strong>&nbsp;link, you\u2019ll see the component\u2019s header, but no data. If you look at your JavaScript console, you\u2019ll see there\u2019s a CORS error.<\/p>\n<p>This error happens because Spring\u2019s&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">@CrossOrigin<\/code>&nbsp;doesn\u2019t play well with Spring Security. To solve this problem, add a&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">simpleCorsFilter<\/code>&nbsp;bean to the body of&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">DemoApplication.java<\/code>.<\/p>\n<pre class=\"gutter: false;brush:java; wrap-lines:false\">package com.okta.developer.demo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.Ordered;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\n\nimport java.util.Collections;\n\n@EnableResourceServer\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n    @Bean\n    public FilterRegistrationBean&lt;CorsFilter&gt; simpleCorsFilter() {\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowCredentials(true);\n        config.setAllowedOrigins(Collections.singletonList(\"http:\/\/localhost:8081\"));\n        config.setAllowedMethods(Collections.singletonList(\"*\"));\n        config.setAllowedHeaders(Collections.singletonList(\"*\"));\n        source.registerCorsConfiguration(\"\/**\", config);\n        FilterRegistrationBean&lt;CorsFilter&gt; bean = new FilterRegistrationBean&lt;&gt;(new CorsFilter(source));\n        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);\n        return bean;\n    }\n}\n<\/pre>\n<p>Restart your server after making this change. To make it all work on the client, modify the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">created()<\/code>&nbsp;method in&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">client\/src\/components\/BeerList.vue<\/code>&nbsp;to set an authorization header.<\/p>\n<pre class=\"gutter: false;brush:java\">private async created() {\n  const response = await axios.get('http:\/\/localhost:8080\/good-beers',\n    {\n      headers: {\n        Authorization: `Bearer ${await this.$auth.getAccessToken()}`,\n      },\n    },\n  );\n  this.beers = await response.data;\n}\n<\/pre>\n<p>Now you should be able to see the good beer list as an authenticated user.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" width=\"1024\" height=\"744\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/success-1024x744.png\" alt=\"Bootiful Development\" class=\"wp-image-86348\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/success-1024x744.png 1024w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/success-300x218.png 300w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/success-768x558.png 768w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2019\/01\/success.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n<p>If it works, excellent! \ud83d\udc4d<\/p>\n<h2 class=\"wp-block-heading\" id=\"learn-more-about-spring-boot-and-vue\">Learn More About Spring Boot and Vue<\/h2>\n<p>This tutorial showed you how to build an app that uses modern frameworks like Spring Boot and Vue. You learned how to add authentication with OIDC and protect routes using Okta\u2019s Vue SDK. If you\u2019d like to watch a video of this tutorial, I&nbsp;<a href=\"https:\/\/youtu.be\/aBXmi-J4LQs\">published it as a screencast to YouTube<\/a>.<\/p>\n<figure class=\"wp-block-embed-youtube aligncenter wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\">\n<div class=\"wp-block-embed__wrapper\">\n<iframe title=\"Bootiful Development with Spring Boot and Vue\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/aBXmi-J4LQs?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div>\n<\/figure>\n<p>If you want to learn more about the Vue phenomenon, I have a couple of recommended articles. First of all, I think it\u2019s awesome it\u2019s not sponsored by a company (like Angular + Google and React + Facebook), and that\u2019s it\u2019s mostly community driven.&nbsp;<a href=\"https:\/\/www.wired.com\/story\/the-solo-javascript-developer-challenging-google-facebook\/\">The Solo JavaScript Developer Challenging Google and Facebook<\/a>&nbsp;is an article in Wired that explains why this is so amazing.<\/p>\n<p>Regarding JavaScript framework performance,&nbsp;<a href=\"https:\/\/blog.uncommon.is\/the-baseline-costs-of-javascript-frameworks-f768e2865d4a\">The Baseline Costs of JavaScript Frameworks<\/a>&nbsp;is an interesting blog post from&nbsp;<a href=\"https:\/\/twitter.com\/ankurs3thi\">Anku Sethi<\/a>. I like his motivation for writing it:<\/p>\n<p>Last week I was curious about how much of a performance impact just including React on a page can have. So I ran some numbers on a cheap Android phone and wrote about it.<\/p>\n<p>To learn more about Vue, Spring Boot, or Okta, check out the following resources:<\/p>\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/developer.okta.com\/blog\/2018\/11\/20\/build-crud-spring-and-vue?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Build a Simple CRUD App with Spring Boot and Vue.js<\/a><\/li>\n<li><a href=\"https:\/\/developer.okta.com\/blog\/2018\/02\/15\/build-crud-app-vuejs-node?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Build a Basic CRUD App with Vue.js and Node<\/a><\/li>\n<li><a href=\"https:\/\/developer.okta.com\/blog\/2018\/10\/23\/build-a-single-page-app-with-go-and-vue?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Build a Single-Page App with Go and Vue<\/a><\/li>\n<li><a href=\"https:\/\/developer.okta.com\/blog\/2018\/11\/26\/spring-boot-2-dot-1-oidc-oauth2-reactive-apis?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Spring Boot 2.1: Outstanding OIDC, OAuth 2.0, and Reactive API Support<\/a><\/li>\n<\/ul>\n<p>You can find the source code associated with this article&nbsp;<a href=\"https:\/\/github.com\/oktadeveloper\/spring-boot-vue-example\">on GitHub<\/a>. The primary example (without authentication) is in the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">master<\/code>&nbsp;branch, while the Okta integration is in the&nbsp;<code style=\"font-size:13px\" class=\"highlighter-rouge\">okta<\/code>&nbsp;branch. To check out the Okta branch on your local machine, run the following command.<\/p>\n<pre class=\"gutter: false;brush:bash\">git clone -b okta https:\/\/github.com\/oktadeveloper\/spring-boot-vue-example.git<\/pre>\n<p>If you find any issues, please add a comment below, and I\u2019ll do my best to help. If you liked this tutorial, you should&nbsp;<a href=\"https:\/\/twitter.com\/oktadev\">follow my team on Twitter<\/a>. We also have a&nbsp;<a href=\"https:\/\/www.youtube.com\/channel\/UC5AMiWqFVFxF1q9Ya1FuZ_Q\">YouTube channel<\/a>&nbsp;where we publish screencasts.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>There are&nbsp;<a href=\"https:\/\/developer.okta.com\/blog\/2017\/04\/26\/bootiful-development-with-spring-boot-and-angular?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Angular<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/developer.okta.com\/blog\/2017\/12\/06\/bootiful-development-with-spring-boot-and-react?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">React<\/a>&nbsp;versions of this same tutorial. <\/p>\n<\/blockquote>\n<p><span style=\"font-size: 20px;\"><b>\u201cI love writing authentication and authorization code.\u201d ~ No Java Developer Ever.<\/b> Tired of building the same login screens over and over? <a href=\"https:\/\/developer.okta.com\/signup?utm_campaign=text_website_all_multiple_dev_ciam_spring-boot-vue-typescript_null&amp;utm_source=jcg&amp;utm_medium=cpc\">Try the Okta API for hosted authentication, authorization, and multi-factor auth.<\/a><\/span><\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript\" target=\"_blank\" rel=\"noreferrer noopener\">Bootiful Development with Spring Boot and Vue&#8217;<\/a>&nbsp;was originally published on the Okta developer blog on December 03, 2018.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u201cI love writing authentication and authorization code.\u201d ~ No Java Developer Ever. Tired of building the same login screens over and over? Try the Okta API for hosted authentication, authorization, and multi-factor auth. Vue is a web framework that\u2019s gotten a lot of attention lately because it\u2019s lean and mean. Its baseline framework cost is &hellip;<\/p>\n","protected":false},"author":13127,"featured_media":240,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[30,854,1844],"class_list":["post-86333","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-spring","tag-spring-boot","tag-vue"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Bootiful Development with Spring Boot and Vue - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Bootiful Development with Spring Boot and Vue - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2019-01-17T13:27:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-01-28T07:12:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Matt Raible\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@mraible\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Matt Raible\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"22 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/01\\\/bootiful-development-spring-boot-vue.html\"},\"author\":{\"name\":\"Matt Raible\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/54edd49deb980d7706e2af51514c3f7f\"},\"headline\":\"Bootiful Development with Spring Boot and Vue\",\"datePublished\":\"2019-01-17T13:27:03+00:00\",\"dateModified\":\"2019-01-28T07:12:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/01\\\/bootiful-development-spring-boot-vue.html\"},\"wordCount\":2766,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"keywords\":[\"Spring\",\"Spring Boot\",\"Vue\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2019\\\/01\\\/bootiful-development-spring-boot-vue.html\",\"url\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript\",\"name\":\"Bootiful Development with Spring Boot and Vue - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"datePublished\":\"2019-01-17T13:27:03+00:00\",\"dateModified\":\"2019-01-28T07:12:11+00:00\",\"description\":\"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/spring-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"spring-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/developer.okta.com\\\/blog\\\/2018\\\/12\\\/03\\\/bootiful-spring-boot-java-vue-typescript#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Bootiful Development with Spring Boot and Vue\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/54edd49deb980d7706e2af51514c3f7f\",\"name\":\"Matt Raible\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g\",\"caption\":\"Matt Raible\"},\"description\":\"Java Champion and Developer Advocate @okta with a passion for skiing, mtn biking, VWs, &amp; good beer.\",\"sameAs\":[\"https:\\\/\\\/developer.okta.com\",\"https:\\\/\\\/x.com\\\/mraible\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/matt-raible\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Bootiful Development with Spring Boot and Vue - Java Code Geeks","description":"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript","og_locale":"en_US","og_type":"article","og_title":"Bootiful Development with Spring Boot and Vue - Java Code Geeks","og_description":"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.","og_url":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2019-01-17T13:27:03+00:00","article_modified_time":"2019-01-28T07:12:11+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","type":"image\/jpeg"}],"author":"Matt Raible","twitter_card":"summary_large_image","twitter_creator":"@mraible","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Matt Raible","Est. reading time":"22 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/01\/bootiful-development-spring-boot-vue.html"},"author":{"name":"Matt Raible","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/54edd49deb980d7706e2af51514c3f7f"},"headline":"Bootiful Development with Spring Boot and Vue","datePublished":"2019-01-17T13:27:03+00:00","dateModified":"2019-01-28T07:12:11+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2019\/01\/bootiful-development-spring-boot-vue.html"},"wordCount":2766,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","keywords":["Spring","Spring Boot","Vue"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2019\/01\/bootiful-development-spring-boot-vue.html","url":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript","name":"Bootiful Development with Spring Boot and Vue - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#primaryimage"},"image":{"@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","datePublished":"2019-01-17T13:27:03+00:00","dateModified":"2019-01-28T07:12:11+00:00","description":"Interested to learn about Bootiful Development? Check our article explaining how to build a Spring Boot API using Spring Data JPA and Hibernate.","breadcrumb":{"@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/spring-logo.jpg","width":150,"height":150,"caption":"spring-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/developer.okta.com\/blog\/2018\/12\/03\/bootiful-spring-boot-java-vue-typescript#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Bootiful Development with Spring Boot and Vue"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/54edd49deb980d7706e2af51514c3f7f","name":"Matt Raible","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/753d82443e50aed1ed2746573af191fe3e45c277ff3bd29873012a1b614355a7?s=96&d=mm&r=g","caption":"Matt Raible"},"description":"Java Champion and Developer Advocate @okta with a passion for skiing, mtn biking, VWs, &amp; good beer.","sameAs":["https:\/\/developer.okta.com","https:\/\/x.com\/mraible"],"url":"https:\/\/www.javacodegeeks.com\/author\/matt-raible"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/86333","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/13127"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=86333"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/86333\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/240"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=86333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=86333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=86333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}