50% OFF Plus
--:--:--
¡Ver!

Integración con Vue

Avanzado
SpringBoot
SpringBoot
Actualizado: 08/10/2025

Consumo de API REST desde Vue con Axios

Vue 3.5 ofrece una arquitectura moderna basada en la Composition API que simplifica significativamente el consumo de servicios REST. Axios se mantiene como la biblioteca de referencia para realizar peticiones HTTP, proporcionando una interfaz intuitiva y funcionalidades avanzadas como interceptores y manejo automático de respuestas JSON.

Instalación y configuración inicial

Para integrar Axios en un proyecto Vue 3.5, primero instalamos la dependencia:

npm install axios

La configuración recomendada consiste en crear una instancia configurada de Axios que centralize la configuración base:

// src/services/api.js
import axios from 'axios'

const api = axios.create({
  baseURL: 'http://localhost:8080/api',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
})

export default api

Esta configuración establece la URL base del backend Spring Boot y define headers comunes para todas las peticiones.

Implementación de servicios con Composition API

Vue 3.5 recomienda organizar las llamadas a la API mediante composables reutilizables. Un composable encapsula la lógica de estado y las operaciones asíncronas:

// src/composables/useProducts.js
import { ref, reactive } from 'vue'
import api from '@/services/api'

export function useProducts() {
  const products = ref([])
  const loading = ref(false)
  const error = ref(null)

  const fetchProducts = async () => {
    loading.value = true
    error.value = null
    
    try {
      const response = await api.get('/productos')
      products.value = response.data
    } catch (err) {
      error.value = err.response?.data?.message || 'Error al cargar productos'
    } finally {
      loading.value = false
    }
  }

  const createProduct = async (productData) => {
    try {
      const response = await api.post('/productos', productData)
      products.value.push(response.data)
      return response.data
    } catch (err) {
      error.value = err.response?.data?.message || 'Error al crear producto'
      throw err
    }
  }

  return {
    products,
    loading,
    error,
    fetchProducts,
    createProduct
  }
}

Consumo en componentes Vue

Los composables se integran naturalmente en componentes Vue usando la Composition API:

<template>
  <div class="products-container">
    <div v-if="loading" class="loading">
      Cargando productos...
    </div>
    
    <div v-else-if="error" class="error">
      {{ error }}
    </div>
    
    <div v-else class="products-grid">
      <div 
        v-for="product in products" 
        :key="product.id"
        class="product-card"
      >
        <h3>{{ product.nombre }}</h3>
        <p>{{ product.descripcion }}</p>
        <span class="price">€{{ product.precio }}</span>
      </div>
    </div>
    
    <button @click="loadProducts" :disabled="loading">
      Recargar productos
    </button>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import { useProducts } from '@/composables/useProducts'

const { products, loading, error, fetchProducts } = useProducts()

const loadProducts = () => {
  fetchProducts()
}

onMounted(() => {
  fetchProducts()
})
</script>

Manejo de diferentes tipos de peticiones HTTP

Para operaciones CRUD completas, podemos extender el composable con todos los métodos HTTP necesarios:

// src/composables/useProductsCrud.js
export function useProductsCrud() {
  const products = ref([])
  const loading = ref(false)
  const error = ref(null)

  // GET - Obtener producto por ID
  const getProduct = async (id) => {
    try {
      const response = await api.get(`/productos/${id}`)
      return response.data
    } catch (err) {
      error.value = `Error al obtener producto: ${err.message}`
      throw err
    }
  }

  // PUT - Actualizar producto completo
  const updateProduct = async (id, productData) => {
    try {
      const response = await api.put(`/productos/${id}`, productData)
      const index = products.value.findIndex(p => p.id === id)
      if (index !== -1) {
        products.value[index] = response.data
      }
      return response.data
    } catch (err) {
      error.value = `Error al actualizar: ${err.message}`
      throw err
    }
  }

  // DELETE - Eliminar producto
  const deleteProduct = async (id) => {
    try {
      await api.delete(`/productos/${id}`)
      products.value = products.value.filter(p => p.id !== id)
    } catch (err) {
      error.value = `Error al eliminar: ${err.message}`
      throw err
    }
  }

  return {
    products,
    loading,
    error,
    getProduct,
    updateProduct,
    deleteProduct
  }
}

Gestión de formularios con peticiones POST

Para formularios que envían datos al backend, combinamos reactive con validación básica:

<template>
  <form @submit.prevent="handleSubmit" class="product-form">
    <div class="form-group">
      <label for="nombre">Nombre:</label>
      <input 
        id="nombre"
        v-model="form.nombre" 
        type="text" 
        required 
      />
    </div>
    
    <div class="form-group">
      <label for="precio">Precio:</label>
      <input 
        id="precio"
        v-model.number="form.precio" 
        type="number" 
        step="0.01" 
        required 
      />
    </div>
    
    <button type="submit" :disabled="loading">
      {{ loading ? 'Guardando...' : 'Guardar Producto' }}
    </button>
    
    <div v-if="error" class="error">{{ error }}</div>
    <div v-if="success" class="success">¡Producto creado exitosamente!</div>
  </form>
</template>

<script setup>
import { reactive, ref } from 'vue'
import { useProducts } from '@/composables/useProducts'

const { createProduct, loading, error } = useProducts()

const form = reactive({
  nombre: '',
  descripcion: '',
  precio: 0
})

const success = ref(false)

const handleSubmit = async () => {
  success.value = false
  
  try {
    await createProduct(form)
    success.value = true
    // Limpiar formulario
    Object.assign(form, {
      nombre: '',
      descripcion: '',
      precio: 0
    })
  } catch (err) {
    console.error('Error al enviar formulario:', err)
  }
}
</script>

Interceptores para manejo global

Los interceptores de Axios permiten procesar respuestas y errores de forma centralizada:

// src/services/api.js
import axios from 'axios'

const api = axios.create({
  baseURL: 'http://localhost:8080/api',
  timeout: 5000
})

// Interceptor de respuesta para manejo global de errores
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 404) {
      console.error('Recurso no encontrado')
    } else if (error.response?.status >= 500) {
      console.error('Error del servidor')
    }
    
    return Promise.reject(error)
  }
)

export default api

Esta configuración proporciona una base sólida para el consumo de APIs REST desde Vue 3.5, aprovechando las ventajas de la Composition API y las capacidades avanzadas de Axios para crear aplicaciones frontend robustas y mantenibles.

Configuración de CORS y build conjunto con Spring Boot

La integración completa entre Vue y Spring Boot requiere dos configuraciones esenciales: la habilitación de CORS para permitir peticiones cross-origin durante el desarrollo, y la configuración del build conjunto para servir la aplicación Vue desde el mismo servidor Spring Boot en producción.

Configuración de CORS en Spring Boot

CORS (Cross-Origin Resource Sharing) es necesario porque durante el desarrollo, Vue se ejecuta típicamente en http://localhost:5173 (Vite) mientras que Spring Boot corre en http://localhost:8080. Los navegadores bloquean estas peticiones cross-origin por seguridad.

Configuración global de CORS

La forma más eficiente de habilitar CORS es mediante una configuración global que aplique a todos los endpoints:

// src/main/java/com/example/config/CorsConfig.java
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig {
    
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                    .allowedOrigins("http://localhost:5173", "http://localhost:3000")
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                    .allowedHeaders("*")
                    .allowCredentials(true)
                    .maxAge(3600);
            }
        };
    }
}

Esta configuración permite peticiones desde los puertos comunes de desarrollo de Vue (Vite usa 5173, otros servidores pueden usar 3000) y habilita todos los métodos HTTP necesarios.

Configuración de CORS por controlador

Para casos específicos, podemos configurar CORS directamente en los controladores REST:

// src/main/java/com/example/controller/ProductoController.java
package com.example.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/productos")
@CrossOrigin(origins = {"http://localhost:5173", "http://localhost:3000"})
public class ProductoController {
    
    @GetMapping
    public List<Producto> obtenerProductos() {
        return productoService.obtenerTodos();
    }
    
    @PostMapping
    public Producto crearProducto(@RequestBody Producto producto) {
        return productoService.crear(producto);
    }
}

Configuración de CORS con Spring Security

Si la aplicación usa Spring Security, la configuración de CORS debe integrarse con la cadena de filtros de seguridad:

// src/main/java/com/example/config/SecurityConfig.java
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/**").permitAll()
                .anyRequest().authenticated()
            );
        
        return http.build();
    }
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("http://localhost:5173"));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }
}

Build conjunto para producción

En producción, es recomendable servir la aplicación Vue directamente desde Spring Boot para simplificar el despliegue y evitar problemas de CORS.

Configuración del build de Vue

Primero, configuramos Vue para que genere los archivos estáticos en el directorio correcto de Spring Boot:

// vue.config.js (raíz del proyecto Vue)
module.exports = {
  outputDir: '../backend/src/main/resources/static',
  assetsDir: 'assets',
  publicPath: '/',
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      }
    }
  }
}

Si usas Vite (recomendado para Vue 3.5), la configuración equivalente sería:

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: '../backend/src/main/resources/static',
    assetsDir: 'assets'
  },
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      }
    }
  }
})

Automatización del build con Maven

Para automatizar el proceso de build, configuramos Maven para que compile Vue antes de generar el JAR de Spring Boot:

<!-- pom.xml -->
<build>
    <plugins>
        <!-- Plugin para ejecutar comandos Node.js -->
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>1.15.0</version>
            <configuration>
                <workingDirectory>../frontend</workingDirectory>
            </configuration>
            <executions>
                <!-- Instalar Node.js y npm -->
                <execution>
                    <id>install node and npm</id>
                    <goals>
                        <goal>install-node-and-npm</goal>
                    </goals>
                    <configuration>
                        <nodeVersion>v20.10.0</nodeVersion>
                        <npmVersion>10.2.3</npmVersion>
                    </configuration>
                </execution>
                
                <!-- Instalar dependencias -->
                <execution>
                    <id>npm install</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <configuration>
                        <arguments>install</arguments>
                    </configuration>
                </execution>
                
                <!-- Build de producción -->
                <execution>
                    <id>npm run build</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <configuration>
                        <arguments>run build</arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Configuración del controlador para servir la aplicación Vue

Para que Spring Boot sirva correctamente la aplicación SPA de Vue, necesitamos un controlador que redirija todas las rutas no-API al index.html:

// src/main/java/com/example/controller/SpaController.java
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class SpaController {
    
    @RequestMapping(value = {
        "/", 
        "/productos", 
        "/usuarios", 
        "/dashboard/**"
    })
    public String spa() {
        return "forward:/index.html";
    }
}

Configuración de recursos estáticos

Aseguramos que Spring Boot sirva correctamente los recursos estáticos de Vue:

// src/main/java/com/example/config/WebConfig.java
package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**")
            .addResourceLocations("classpath:/static/assets/")
            .setCachePeriod(31536000); // 1 año de cache para assets
            
        registry.addResourceHandler("/**")
            .addResourceLocations("classpath:/static/")
            .setCachePeriod(0); // Sin cache para HTML
    }
}

Flujo de desarrollo recomendado

Para un desarrollo eficiente, recomendamos este flujo:

1. Desarrollo local:

  • Vue en http://localhost:5173 con proxy a Spring Boot
  • Spring Boot en http://localhost:8080 con CORS habilitado
  • Hot reload automático en ambos proyectos

2. Build de producción:

# Desde el directorio del frontend Vue
npm run build

# Desde el directorio del backend Spring Boot  
mvn clean package

# El JAR resultante contiene tanto la API como la aplicación Vue
java -jar target/mi-aplicacion-0.0.1-SNAPSHOT.jar

Esta configuración permite un desarrollo ágil con hot reload mientras garantiza un despliegue simplificado en producción con una sola aplicación que sirve tanto la API REST como la interfaz de usuario Vue.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en SpringBoot

Documentación oficial de SpringBoot
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SpringBoot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de SpringBoot

Explora más contenido relacionado con SpringBoot y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender cómo consumir APIs REST desde Vue 3.5 utilizando Axios y la Composition API.
  • Aprender a configurar CORS en Spring Boot para permitir peticiones cross-origin durante el desarrollo.
  • Implementar composables en Vue para manejar operaciones CRUD y formularios con validación básica.
  • Configurar el build conjunto de Vue y Spring Boot para un despliegue integrado en producción.
  • Gestionar interceptores de Axios para un manejo global de respuestas y errores.