A logo showing the text blog.marcnuri.com
English
Inicio»Java»Integración de React + Quarkus usando Maven

Entradas Recientes

  • Kubernetes MCP Server se une a la organización Containers
  • MCP Tool Annotations: Añadiendo Metadatos y Contexto a Tus Herramientas de IA
  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • julio 2025
  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • septiembre 2022
  • agosto 2022
  • julio 2022
  • junio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • enero 2021
  • diciembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Integración de React + Quarkus usando Maven

2020-09-21 en Java etiquetado Maven / Quarkus / React / YAKC por Marc Nuri | Última actualización: 2025-08-28
English version

Introducción

En este post te mostraré cómo servir una aplicación front-end ReactJS (o cualquier otra SPA de JavaScript) usando una aplicación Java Quarkus como back-end y servidor de páginas estáticas. El post también incluye instrucciones sobre cómo configurar Maven para realizar las tareas de construcción del front-end junto con el empaquetado de tu aplicación.

Este artículo es muy similar a mi post de blog de 2017 Integración de Angular + Spring Boot usando Gradle, con algunas características adicionales para habilitar el funcionamiento del router de React.

Requisitos

Las librerías y tecnologías necesarias para este ejemplo son:

  • Node.js
  • React
  • Quarkus
  • Maven

El objetivo principal es lograr un npm install + build que se integre con el plugin de recursos de Maven y despliegue los archivos de distribución a un directorio donde Quarkus pueda servirlos. Quarkus actuará entonces como el servicio back-end así como el gateway API para servir el front-end también.

Puedes encontrar el código para estas configuraciones en mi aplicación quick start YAKC – Kubernetes Dashboard para YAKC (Yet Another Kubernetes Client).

Back-end (Quarkus)

En este nuevo mundo de Microservicios, tiene sentido que el front-end y el/los back-end(s) sean servicios diferentes que usualmente son servidos y orquestados por un gateway API. Es difícil encontrar esas antiguas aplicaciones web donde el front-end y back-end eran parte del mismo servicio.

Una imagen mostrando un microservicio vs un monolito
Una imagen mostrando un microservicio vs un monolito

Esta arquitectura distribuida tiene sentido para proyectos empresariales grandes, pero a veces solo queremos hacer un prototipo o construir rápidamente una aplicación web simple. Ese es el momento cuando empiezas a extrañar esos viejos monolitos y su simplicidad que te permitía crear y especialmente desplegar tu aplicación rápidamente.

El objetivo principal es lograr que Quarkus actúe como un gateway API. Así que además de proporcionar la API REST, sirve los archivos del front-end también. De esta manera conseguimos mantener ReactJS como nuestra tecnología principal de front-end, Quarkus como nuestro back-end, y evitar todas las complejidades extra necesarias para un despliegue de 2 (o más) servicios separados.

Puedes pensar que Quarkus ya proporciona una manera de servir recursos estáticos y que la solución es tan simple como almacenar los archivos construidos del front-end en este directorio antes de construir el back-end. Sin embargo, esto no permitirá que el router de React funcione y no podrías enrutar /nested/front-end/routed/paths (Solo intenta refrescar tu navegador en uno de esos paths anidados).

Clase GatewayResource

@Path("/")
public class GatewayResource {

  private static final String FALLBACK_RESOURCE = "/frontend/index.html";
  private final ApiResource apiResource;

  @Inject
  public GatewayResource(ApiResource apiResource) {
    this.apiResource = apiResource;
  }

  @Path("/api/v1")
  public ApiResource getApiResource() {
    return apiResource;
  }

  @GET
  @Path("/")
  public Response getFrontendRoot() throws IOException {
    return getFrontendStaticFile("index.html");
  }

  @GET
  @Path("/{fileName:.+}")
  public Response getFrontendStaticFile(@PathParam("fileName") String fileName) throws IOException {
    final InputStream requestedFileStream = GatewayResource.class.getResourceAsStream("/frontend/" + fileName);
    final InputStream inputStream = requestedFileStream != null ?
      requestedFileStream :
      GatewayResource.class.getResourceAsStream(FALLBACK_RESOURCE);
    final StreamingOutput streamingOutput = outputStream -> IOUtils.copy(inputStream, outputStream);
    return Response
      .ok(streamingOutput)
      .cacheControl(CacheControl.valueOf("max-age=900"))
      .type(URLConnection.guessContentTypeFromStream(inputStream))
      .build();
  }

}

Esta clase es el punto de entrada principal para cualquier solicitud HTTP que la aplicación recibe.

El primer método, getApiResource delega cualquier solicitud a /api/v1 a la instancia subresource ApiResource que se encargará de ella.

El getFrontendRoot se encargará de cualquier solicitud al path raíz / solicitando el archivo index.html al getFrontendStaticFile que describiré a continuación.

Finalmente, el getFrontendStaticFile se encargará del resto de las solicitudes. El método primero trata de localizar cualquier archivo con el nombre de archivo proporcionado en el directorio de recursos /frontend. Este directorio debería ser empaquetado durante la fase de construcción de la aplicación y contiene la aplicación ReactJS construida.

Si no se encuentra ningún archivo con este nombre, servirá el archivo index.html. Esta es la parte que permite que el router de React funcione. Cualquier solicitud no coincidente ya sea por el subresource de API o un archivo de front-end servirá la aplicación React que luego se encargará de realizar un enrutamiento de "front-end".

Front-end (React)

Tu aplicación front-end completa puede estar ubicada en cualquier lugar dentro de tu proyecto, en mi caso la he ubicado bajo src/main/frontend. Además de notar la ubicación del path para tu aplicación React, realmente no necesitas ninguna configuración extra para que este procedimiento funcione.

Construcción Maven

Para el paso final de integrar el front-end React con el back-end Quarkus necesitas afinar la construcción Maven.

Recursos

<!-- ... -->
  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
      <resource>
        <directory>src/main/frontend/build</directory>
        <targetPath>frontend</targetPath>
      </resource>
    </resources>
    <!-- ... -->
  </build>
  <!-- ... -->

En este primer extracto de código, he añadido una nueva entrada a la configuración de recursos de construcción. Además del estándar src/main/resources, el directorio donde la aplicación React es construida src/main/frontend/build también será ensamblado y copiado al directorio target/classes/frontend. Si recuerdas, en la clase GatewayResource, estaba cargando recursos estáticos desde este directorio.

Npm build + install

<!-- ... -->
  <profiles>
    <profile>
      <id>build-frontend</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>${version.exec-maven-plugin}</version>
            <executions>
              <execution>
                <id>npm-install</id>
                <phase>generate-resources</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
                <configuration>
                  <workingDirectory>src/main/frontend</workingDirectory>
                  <executable>npm</executable>
                  <arguments>
                    <argument>install</argument>
                  </arguments>
                </configuration>
              </execution>
              <execution>
                <id>npm-build</id>
                <phase>generate-resources</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
                <configuration>
                  <workingDirectory>src/main/frontend</workingDirectory>
                  <executable>npm</executable>
                  <arguments>
                    <argument>run</argument>
                    <argument>build</argument>
                  </arguments>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <!-- ... -->
  </profiles>

El segundo extracto de código se usa para ejecutar npm install y npm run build desde Maven. Añadí esto como un perfil separado porque usualmente realizo estos pasos manualmente mientras estoy en mi máquina local. Solo los invoco en el CI, así que mi paso de construcción de CI se ve como mvn -Pbuild-frontend clean package. Sin embargo, no hay problema en añadir estas configuraciones directamente a la sección de construcción de tu pom.xml.

Como puedes ver configuré 2 ejecuciones para el Exec Maven Plugin. La primera invocará npm install usando el directorio home del front-end (src/main/frontend) como su directorio de trabajo. La segunda ejecución tiene una configuración similar pero ejecuta npm run build en su lugar.

Ejecución

Una vez que tu aplicación está empaquetada, puedes ejecutarla ya sea ejecutando el archivo jar generado (java -jar ./target...) o ejecutando mvn quarkus:dev.

Una grabación de cómo funciona el enrutamiento del front-end con Quarkus
Una grabación de cómo funciona el enrutamiento del front-end con Quarkus

Modo de desarrollo

Si quieres disfrutar y beneficiarte de la recarga en caliente/vivo tanto de Quarkus como de React necesitarás alguna configuración extra.

En este caso, ejecutaremos el front-end usando el estándar npm start y apuntando nuestro navegador a http://localhost:3000. Y ejecutaremos nuestro back-end usando mvn quarkus:dev en http://localhost:8080.

Back-end

Dado que el front-end es servido desde una URL diferente, necesitamos habilitar CORS en Quarkus.

Podemos hacer esto modificando nuestro application.properties para incluir:

quarkus.http.cors=true
quarkus.http.cors.origins=http://localhost:3000
quarkus.http.cors.headers=accept, origin, authorization, content-type, x-requested-with
quarkus.http.cors.methods=GET,POST,DELETE,OPTIONS

Estas propiedades habilitan CORS para permitir solicitudes desde el origen de desarrollo del front-end. Puedes modificar las entradas para hacerlo más o menos restrictivo dependiendo de tus necesidades.

Front-end

Para la parte del front-end, necesitamos afinar nuestra aplicación React para acceder a diferentes endpoints del back-end dependiendo del entorno en el que la estemos ejecutando.

En mi caso, dado que estoy usando create-react-app, añadí dos archivos de propiedades de entorno:

.env para producción:

REACT_APP_API_URL=/api/v1

.env.development para desarrollo:

REACT_APP_API_URL=http://localhost:8080/api/v1

Una vez que estos dos archivos están listos, create-react-app se encarga de los reemplazos apropiados en tu aplicación dependiendo de tu entorno objetivo. Para usar esos valores, necesitarás referenciar la URL usando la variable process.env.REACT_APP_API_URL:

const requestDelete = async pod => {
  await fetch(
    `${process.env.REACT_APP_API_URL}/pods/${metadata.selectors.namespace(pod)}/${metadata.selectors.name(pod)}`,
      {method: 'DELETE'}
        );
      }

Conclusión

En este post, te he mostrado cómo servir tanto una aplicación front-end JavaScript como una aplicación back-end Java usando Quarkus. Quarkus actúa como un gateway API y ya sea sirve los archivos estáticos del front-end o procesa la solicitud del back-end usando recursos y servicios. También te he enseñado cómo combinar y usar la recarga en caliente en React y Quarkus afinando la configuración para cada aplicación.

He extraído el código para este post de mi YAKC – Kubernetes Dashboard quickstart. Puedes aprender más visitando el sitio del proyecto GitHub de YAKC. También puedes ver el código relacionado aquí.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Navegador de artículos
Accede al API de Kubernetes desde un Pod con JavaKubernetes 1.19 Ingress API desde Java con YAKC
© 2007 - 2025 Marc Nuri