LoginSignup
5
3

More than 1 year has passed since last update.

逆引きQuarkus

Posted at

backend-example-quarkusを実装する過程での知見を整理した。QuarkusでReactiveなRestful APIを実装する際の勘所をまとめている。

基礎知識を学ぶ

RESTful APIを実装するのであれば、下記が全体を理解するのに役に立つ。

開発環境を構築する

Quarkus CLIというスタンドアロンなコマンドが提供されているが、Mavenプラグインで同じことができるため、こちらの方が手軽。Quarkus CLIを利用する場合、WindowsであればScoopでインストールするのが簡単である。

IDEにVisual Studio Codeを利用してDevContainerで開発環境を作る方法がおすすめ。devcontainer.json設定例では、Java Featureに加えてQuarkusプラグインとSonarLintプラグインを設定している。

プロジェクトを新規作成する

code.quarkus.ioで作成したプロジェクトをインポートするか、Quarkus CLIもしくはMavenプラグインでプロジェクト作成する。

Quarkus CLIでのプロジェクト作成

quarkus create app {Group ID}:{Artifact ID} --extension=resteasy-reactive-jackson --no-code

基本的な開発の流れ

Visual Studio Codeでソースコードを編集して、Maven (Wrapper) でアプリケーションを開発モードで起動・テスト実行、必要に応じてVisual Studio Codeのデバッガを接続する、という流れが効率がよい。

# 開発モードでアプリケーションを起動する
./mvnw quarkus:dev

デバッガの接続のための設定はlaunch.jsonを参照。Visual Studio Codeの「実行とデバッグ」メニューからデバッガを起動する他に、QuarkusプラグインCtrl + Shift + PQuarkus: Debug current Quarkus projectを実行すると、アプリ起動+デバッグ実行を一発でできる。(参考:Quarkus developer joy for VS Code

ファイル更新時にSonarLintの分析が動作して問題パネルに指摘が表示される。現在のところ、全ファイルを一括で分析することはできないとのこと。(参考:How to enable SonarLint on all files in Visual Studio code - SonarLint / VS Code - Sonar Community

入力に対するバリデーションを行う

Hibernate Validator (Bean Validation) がサポートされている。Extenstionをインストールすれば使えるようになる。

quarkus extension add 'hibernate-validator'

参考:VALIDATION WITH HIBERNATE VALIDATOR

Hibernate ValidatorビルトインのアノテーションはHibernate Validatorのドキュメントを参照。独自バリデーション(アノテーション)の実装方法は6. Creating custom constraintsを参照。

共通の例外処理を実装する

JAX-RSのExceptionMapperとRESTEasyのServerExceptionMapperの両方がサポートされている。(参考:Exception Mapping)両者の機能的な差異は、ServerExceptionMapperでは複数のExceptionを受け取る設定をアノテーションで記載する機能がある程度。

ビルトインのExceptionMapperが定義されており、エラーハンドリングをカスタマイズするにはPriorityを設定して上書き登録が必要となる。また、これが原因で全ての例外を受け取るExceptionMapper定義もできない制約がある。(参考:built-in ExceptionMappers cause unexpected behavior · Issue #7883 · quarkusio/quarkus

Quarkus Securityにて発生する例外をExceptionMapperで扱うには以下の設定を入れる必要がある。

quarkus.http.auth.proactive=false

ただし、smallrye-jwt (quarkus-smallrye-jwt) のトークン検証エラー(AuthenticationFailedException)を受け取るExceptionMapperを定義しようとすると java.lang.IllegalStateException: Response has already been written が発生することを確認している。原因は調査できていない。

データベースにアクセスする

ORM (+α) を使いたい場合はHibernate Reactive + Panache (quarkus-hibernate-reactive-panache) を利用するとよい。(2022年9月時点ではPreview扱い。)

実装例:TodoEntity.java

実装上のTips

  • レコードのIDをカスタマイズする場合はPanacheEntityではなくPanacheEntityBaseを使う。(参考:Custom IDs

  • ページングはpageメソッドで実装できる。(参考:Paging

    MyEntity.findAll().page(Page.of(...))
    
  • テーブルにインデックスを設定するには @Table アノテーションの indexes プロパティを設定すればよい。(参考:Defining Indexes in JPA | Baeldung

  • アプリケーション起動時にデータベースの初期化を実行したい場合はStartupEventを捕捉するメソッドを用意すればよい。(参考:DBInit.java

    @ApplicationScoped public class DBInit {
      void onStart(@Observes StartupEvent ev) { ... }
    

参考

トランザクションを利用する

Bean(コンポーネント)のメソッドに@ReactiveTransactionalを記述することで、トランザクション境界を設定できる。もしくはPanache.withTransaction()の中でDB操作を記述することで手続き的に記述もできる。

参考:Simplified Hibernate Reactive with Panache - Quarkus

トランザクションを使っている場合、複数のクエリを Uni.combine で結合すると以下のエラーが発生するため、withTransaction で囲う記述とする必要がある。(参考:First Request to Reactive SQL fail · Issue #14709 · quarkusio/quarkus

java.lang.IllegalStateException: session is currently connecting to database

アクセストークンを利用した認証を実装する

以下の3つのExtensionが選択肢となる。

  • quarkus-oidc
  • quarkus-smallrye-jwt
  • quarkus-elytron-security-oauth2

今回のプロジェクトではJWT形式のトークンの検証ができればよいのでquarkus-smallrye-jwtを選択した。Quarkus CLIでインストールできる。

quarkus extension add 'smallrye-jwt'

javax.annotation.securityやJAX-RS (javax.ws.rs.core) のアノテーションの他にQuarkus独自 (io.quarkus.security) のアノテーションを使って権限設定ができる。(参考:Authorization using Annotations

実装上のTips

  • デフォルトではJWTトークンにiatクレームが指定されていることが必須となるが、下記の設定で任意に変更できる。

    smallrye.jwt.time-to-live=-1
    

インターセプタを実装する

Java Interceptors (javax.interceptor) がサポートされているため、@Interceptorなどのアノテーションを利用できる。(参考:Interceptors

ログを出力する

JDK (JUL), JBoss Logging, SLF4J, Apache Commons LoggingのAPIがサポートされている。(内部でJBoss Loggingに橋渡しされる仕組み。)

参考:Configuring Logging - Quarkus

LoggerインスタンスをDIする方法の他に、staticメソッドで呼び出す仕組み(コンパイル時にインスタンスを利用するコードに置き換えられる)も提供される。

package com.example;

import io.quarkus.logging.Log; 

class MyService { 
    public void doSomething() {
        Log.info("Simple!"); 
    }
}

ログ設定はapplication.propertiesに記載する。(参考:Runtime configuration

単体テストを実装する

quarkus-junit5を依存関係に追加してテストクラスに@QuarkusTestを設定すると、JUnit5を利用したテスト実装にて、アプリケーションのテスト用の起動やテスト対象クラスのDIなどの便利機能が利用できるようになる。(参考:TESTING YOUR APPLICATION

Uni/MultiのアサーションにはSubscriberを利用する。(参考:How can I write unit / integration tests? - SmallRye Mutiny

Mockitoの基本的なサポートはquarkus-junit5に含まれているが、quarkus-junit5-mockitoを追加すると@InjectMockなど便利アノテーションが使えるようになる。(参考:Mock Support

Panacheをモック化するにはquarkus-panache-mockを使う。

  • PanacheQueryインスタンスを返すEntityメソッドをモック化する場合はPanacheQueryのモックオブジェクトを用意する。(詳細は公式ガイドを参照。)
  • PanacheQueryのモック生成時はMockitoの制約により、キャストが必要になることに注意。(参考:Using Mockito to mock classes with generic parameters

Quarkus Securityをモック化するにはquarkus-test-securityを使う。(参考:Security Testing

  • 単体テストのコンテキストでJAX-RSのSecurityContextを参照しようとしたタイミングで java.lang.IllegalStateException: No RESTEasy Reactive request in progress が発生するため、Mockitoでモック化する必要がある。(原因不明)

単体テストの実行はVisual Studio Codeのテストツールから行ってもよいが、起動に時間がかかるため、Quarkusのdev modeを起動(mvn quarkus:dev)しておき、テスト再実行(r)すると効率がよい。

E2Eテストを実装する

E2Eテストの実装にはRESTAssuredを利用できる。@TestHttpEndpointをクラスもしくはメソッドに指定するとRestassuredのbasePathをリソースのパスに自動で設定してくれる。(参考:5.2 Restassured

アプリケーションの設定を管理する

アプリケーション設定はシステムプロパティ、環境変数、.envファイル、プロパティファイル等で入れ込める。ソースの優先順位はConfig Sourcesを参照。

起動ポートなどの動作設定など設定可能な項目はConfiguring Quarkusを参照。独自のアプリケーション設定を定義することもできる。

環境(の分類)ごとの設定をプロファイルとして管理できる。デフォルトでは dev test prod の3つのプロファイルが定義される。(参考:4. Profiles

プロパティのキーのプレフィックスを %{profile-name}. の形式にすることで、特定のプロファイル用の設定を定義できる。もしくは特定のプロファイル用のプロパティファイルを application-{profole-name}.properties として作成できる。

プログラム中でプロファイル名を取得するには ProfileManager.getActiveProfile() を呼び出せばよい。(参考:How to determine programmatically the current active profiles in Quarkus - Stack Overflow

Nativeイメージをビルドする

自力でGraalVMをインストールしてビルドすることもできるがDockerコンテナを使うのが簡単。Quarkus CLI (Mavenプラグイン) ではさらにコンテナを自動で起動してビルドしてくれるコマンドが提供されている。

./mvnw package -Pnative -Dquarkus.native.container-build=true

参考:Creating Linux executable without GraalVM installed

Nativeイメージが ./target/{project name and version}-runner に作成されるので、実行すればOK。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3