The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

ReactとPatternflyでQuarkusアプリにEyecandyを追加する

コンテナ化された分散型のQuarkusアプリケーションで一般的なパターンとして、別のフロントエンドアプリケーションのための効率的でスケーラブルなバックエンドとして機能することがあります。Kubernetesの世界では、フロントエンドアプリケーションはReact、Angular、Vue.jsなどの純粋なウェブレイヤーPodであり、すべてのRESTコールをQuarkusのサービスレイヤーPodに委ねることができます。

frontend pod pattern

このアプローチには、独立したコンテナのスケーリングと独立したライフサイクルという利点がありますが、小規模なアプリケーションには過剰なエンジニアリングとなる場合があります。

最新のWeb GUI技術に妥協することなく、自己完結型の小さなCRUDアプリケーションを作りたいと思っているのではないでしょうか? Angularでこれを実現する方法を見てきましたが、少なくともGitHubの星の数において、人気の点でAngularを上回っているReactはどうでしょうか?今回は、Reactを統合するだけでなく、 Patternflyと呼ばれるWebコンポーネントフレームワークを使用する方法を紹介します。Patternflyは、リッチで一貫性のあるインタラクティブなGUIを作成するためのパターン、コンポーネント、スタイルを集めたものです。

React/Patternflyのテーブルに表示される原子パーティクルオブジェクトを提供するRESTエンドポイントを持つQuarkus Appを作成してみましょう。最終的なアプリケーションのソースコードは こちらにあります。

ステップ1 - Quarkusプロジェクトの生成

スタータープロジェクトを生成するには、 https://code.quarkus.io にアクセスします。Maven Groupとして "io.quarkus" 、Artifactとして "quarkus-react" を入力します。エクステンションのリストで、 RESTEasy JAX-RSRESTEasy JSON-B を選択します。その後、プロジェクトを生成してダウンロードし、ローカル・マシンで解凍して、お気に入りのIDEでプロジェクト・フォルダを開きます。

ステップ2 - Quarkus RESTエンドポイントの追加

サンプルの GreetingResource.java が存在するJavaパッケージフォルダ/src/main/java/io/quarkusに、 Particle.java という名前の新しいBeanクラスを以下の内容で作成します。

package io.quarkus;

public class Particle {

    private String name;

    public void setName(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return this.name;
    }

}

ここで、 ParticleResource.java という新しいRESTリソースクラスを作成し、いくつかのテストパーティクルを返します(ここでは原子物理学的な意味合いは無視します)。

package io.quarkus;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/particles")
public class ParticleResource {

    @GET
    public Set<Particle> getUsers() {

        Set <Particle> particleList = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));

        Particle particle = new Particle();
        particle.setName("Graviton");
        particleList.add(particle);

        Particle particle2 = new Particle();
        particle2.setName("Pentaquark");
        particleList.add(particle2);
        return particleList;
    }
}

開発モードでQuarkusを起動して、すべてが動作するかどうかをテストします。

$ mvn quarkus:dev

新しい端末で、エンドポイントを呼び出して、パーティクルの応答を確認します。

$ curl http://localhost:8080/particles

[{"name":"Graviton"},{"name":"Pentaquark"}]

Quarkusのデータレイヤーの準備ができたので、次はReactのGUIを作りましょう。

ステップ3 - Reactプロジェクトの生成

お使いのシステムに Node.jsnpm がインストールされていることを確認してください。必要に応じて、 公式ドキュメントを参照してください。

プロジェクト内に新しいフォルダ/src/main/ webapp を作成し、これをReactプロジェクトのベースフォルダとします。このフォルダで別のターミナルを開き、Reactプロジェクトイニシャライザを実行して、Patternfly Nodeモジュールを追加します。

$ npx create-react-app .
$ npm install @patternfly/patternfly --save
Reactアプリを起動すると、デフォルトではポート3000で実行されます。その後、ポート8080でQuarkusのエンドポイントにアクセスしようとすると、 CORSセキュリティポリシーのためにブラウザがこれを阻止します。本番環境では、コンパイルされた静的なReactアプリがポート8080でQuarkusから提供されるため、これは問題になりませんが、開発中はこれが問題になります。

幸いなことに、Reactには未知のエンドポイントへのすべてのリクエストを別のURLに転送する proxy というちょっとした機能があります。 webapp フォルダで package.json ファイルを開き、proxyの行を追加して、すべてのリクエストをQuarkusのポート8080に転送します。

{
  "name": "webapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "proxy": "http://localhost:8080/",
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

ステップ4 - Patternflyのテーブルコンポーネントを作成して、データで埋める

Reactプロジェクトの /webapp/index.js にPatternflyのCSSをインポートします。

...
import './index.css';
import '@patternfly/patternfly/patternfly.css';
import App from './App';
...

webapp/srcに components という新しいフォルダを作り、その中に particles.js というファイルを作り、パーティクルオブジェクトのリストをループさせてPatternflyのテーブルを生成します。

import React from 'react'

const Particles = ({ particles }) => {
    return (
        <div>
            <center><h1>Particles List</h1></center>
            <table class="pf-c-table pf-m-grid-md" role="grid" aria-label="Supersonic Subatomic Particles" id="table-basic">
                <caption>Supersonic Subatomic Particles</caption>
                <thead>
                    <tr role="row">
                        <th role="columnheader" scope="col">Name</th>
                    </tr>
                </thead>
                {particles.map((particle) => (
                    <tbody role="rowgroup">
                        <tr role="row">
                            <td role="cell" data-label="Particle name">{particle.name}</td>
                        </tr>
                    </tbody>
                ))}
            </table>
        </div>

    )
};

export default Particles

次に、 /webapp/src/App.js を調整して、Quarkusサービスのエンドポイントを呼び出し、レスポンスデータを使ってパーティクルコンポーネントをレンダリングします。

import React, {Component} from 'react';
import Particles from './components/particles'

class App extends Component {
  state = {
    particles: []
  }

  componentDidMount() {
    fetch('/particles')
    .then(res => res.json())
    .then((data) => {
      this.setState({ particles: data })
    })
    .catch(console.log)
  }

  render () {
    return (
      <Particles particles={this.state.particles} />
    );
  }
}

export default App;

これで、Reactの開発モードでGUIをテストする準備が整いました。

別のターミナルでQuarkusがまだ起動していることを確認します。

/webapp フォルダ内で実行します。

$ npm start

ブラウザで http://localhost:3000 を開きます。開かない場合は、そのURLに移動してください。Quarkusのパーティクルバックエンドからテーブルが動的に入力されているのがわかると思います。ブラウザの開発者ツールでは、データが実際に http://localhost:3000/particles から取得されていることがわかります。

patternfly table

現在、ダブルデベロッパーモードで運用しています。JavaとJavascriptのどちらかのコードに加えられた変更は、すぐに適用されます。これは開発者にとって至福の時ではないでしょうか。

しかし、コンテナを使ったデプロイについてはどうでしょうか?新しいGUIアプリが完成したら、実行可能なjarやイメージを作成するために、いくつかの調整が必要になります。

ステップ5 - プロジェクトの本番ビルドへの準備

webapp/package.json にprodのビルドフェーズを追加:

...
 "eject": "react-scripts eject",
 "prod": "react-scripts build --dest && rsync -a build/* ../resources/META-INF/resources"
  },
  "proxy": "http://localhost:8080/",
...

これにより、静的なReactアセットのプロダクションビルドが行われ、Quarkusのデフォルトの静的ファイル用ディレクトリにコピーされます。

rsyncコマンドは、Linux専用です。他のOSの場合は、同等のコマンドで代用してください。

最後のステップでは、優れた frontend-maven-pluginを pom.xml に追加して、通常の maven ビルドから React のビルドフェーズを起動します。npmとnodeのバージョンを、ローカルにインストールされているものに合わせて調整します。mavenのインストールフェーズでは、実際にこれらの2つのパッケージがインストールされますが、これはCI/CDビルドやこのプロジェクトの起動時に便利です。

<build>
...
 <plugin>
  <groupId>com.github.eirslett</groupId>
  <artifactId>frontend-maven-plugin</artifactId>
  <version>1.11.2</version>
  <configuration>
    <workingDirectory>${project.basedir}/src/main/webapp</workingDirectory>
    <installDirectory>target</installDirectory>
  </configuration>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals>
        <goal>install-node-and-npm</goal>
      </goals>
      <configuration>
        <nodeVersion>v14.15.4</nodeVersion>
        <npmVersion>6.14.10</npmVersion>
      </configuration>
    </execution>
    <execution>
      <id>npm install</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <configuration>
        <arguments>install</arguments>
      </configuration>
    </execution>
    <execution>
      <id>npm run build</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <configuration>
        <arguments>run prod</arguments>
      </configuration>
    </execution>
  </executions>
</plugin>
</build>

それでは、実行可能なjarをビルドしてみましょう。

$ mvn clean package

他の稼働中の環境をすべて停止してポートを空け、実行します。

$ java -jar target/quarkus-app/quarkus-run.jar

ブラウザで http://localhost:8080 を開くと、最適化された単一のQuarkus jarから実行されているテーブルが表示されます。

まとめ

いくつかの簡単なステップで、ReactとPatternflyの力を使って、Quarkusアプリに洗練された見た目のインタラクティブなGUIを追加することができます。

このアプリを次は ネイティブにコンパイルして、極めて軽量なWebアプリを展開するのはどうでしょうか。あるいは、他の Patternflyコンポーネントを使用して、より洗練されたウェブインターフェイスを作成することもできます。いずれにしても、Quarkusアプリを輝かせるためには、あなたの創造性以外に制限はありません。