Tech Blog

Why We Chose Spring Boot Backend + Vue 3 Frontend Separation, and How We Connected Them

by Tech Writer
Vue3 Spring Boot Architecture CORS

Introduction

When building the customer-facing DVD rental app, there was a structure we decided on from the start.

  • Backend: Spring Boot (REST API) → Port 8082
  • Frontend: Vue 3 + Vite → Port 5173 (development)

You might wonder “why not use Thymeleaf?” — let me answer that upfront.

We built the admin panel with Thymeleaf. That was the right choice for an admin panel.
However, the customer-facing app had UX requirements that were difficult to achieve with Thymeleaf.

This article is not about “which is the right choice” but about “how we made that decision.”


Cases Where Thymeleaf Is Sufficient vs. Not

Cases Where Thymeleaf Excels

For admin panels with repeated “form submission → result display” cycles, Thymeleaf is excellent.

  • Returns HTML from the server on each page transition
  • Tight integration with Spring Security, CSRF protection built in
  • Easy server-side validation and error display

Thymeleaf was sufficient for the admin panel.

Why We Chose Vue 3

There was UX we wanted to achieve in the customer-facing app.

  1. Instant filter and search feedback — “Results change without pressing a search button” experience
  2. Smooth cart drawer opening and closing — Interactions without page transitions
  3. Different layouts for mobile and desktop — Dynamic display switching based on viewport
  4. Future API sharing with native apps — The same REST API can be called from iOS/Android apps

These aren’t “impossible” with Thymeleaf, but you’d need to write a lot of JavaScript.
If we’re going to write JS anyway, componentizing with Vue 3 is more maintainable.


Actual Structure

dvd-rental-customer-app/
  backend/           ← Spring Boot (Port 8082)
    src/main/java/
      api/           ← REST API controllers
      service/       ← Business logic
  frontend/          ← Vue 3 + Vite (Port 5173 in development)
    src/
      api/           ← HTTP client for backend
      components/    ← Vue components

CORS and Proxy Settings for Development

The first hurdle with a separated architecture is CORS.
Calling from the frontend (port 5173) to the backend (port 8082) causes the browser to throw a CORS error.

Solution 1: Vite’s Dev Proxy (Development)

// vite.config.ts
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8082',
        changeOrigin: true,
      }
    }
  }
})

In development, Vite forwards requests to /api to the backend, so CORS doesn’t occur.

Solution 2: Spring Boot CORS Configuration (Production)

In production, the frontend and backend may be on different domains or ports.
In that case, allow CORS on the Spring Boot side.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://your-frontend-domain.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowCredentials(true);
    }
}

Production Build Integration

For production, we build Vue with npm run build and serve the output as Spring Boot’s static resources.

<!-- pom.xml (excerpt) -->
<plugin>
    <groupId>com.github.eirslett</groupId>
    <artifactId>frontend-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>npm build</id>
            <goals><goal>npm</goal></goals>
            <configuration>
                <arguments>run build</arguments>
            </configuration>
        </execution>
    </executions>
</plugin>

A single mvn package generates a jar that includes the frontend build.


Actual Pain Points with the Separated Architecture

① Need to Start 2 Processes During Development

Start Vite, then start Spring Boot.
Once you’re used to it, it takes 10 seconds, but initially you forget one and wonder “why isn’t this working?”

② Deploying Without Building the Frontend

It can happen that mvn package runs while the frontend still has an old build.
It’s important to explicitly define the order in CI/CD to always run npm run build first.

③ Type Mismatches

The response types from the backend and the TypeScript types in the frontend tend to diverge.
You need to either share schema definitions with OpenAPI, or have a habit of aligning them regularly.


Summary

AspectThymeleafVue 3 + REST API
Initial development speedFastSomewhat slow (setup required)
Interactive UIDifficultStrength
API sharing (mobile, etc.)Not possiblePossible
Spring Security integrationEasyCustom implementation needed
Deployment complexitySimpleSomewhat complex

Thymeleaf for the admin panel, Vue 3 for the customer-facing app.
This split was the optimal choice for this project.
The important thing is not “which is better” but “choose based on what you want to build.”


Article Map for This Series

Building a DVD Rental End-User App Alongside the Admin Dashboard — Vue 3 + Spring Boot Architecture Overview

Feel free to send a message

Please send a message if you have any technical questions, feedback, or inquiries.