RestyというScala用のREST APIフレームワークを作ってみました

GitHubリポジトリはこちら。

github.com

なぜ作ったのか?

Scala業界のフレームワークは関数型的なアプローチのものが主流になっています。これらのフレームワークはノンブロッキングI/Oや並列処理をうまく扱うことができますが、本質的な複雑さをもたらします。自分の経験では、特に企業システムなどの場合は従来の同期サーブレットでも十分というケースも多いのではないかと感じています。

また、既存のScala用のフレームワークはタイプセーフなDSLでルーティングや入出力パラメータなどを定義するものが多いのですが、この手のものはシンプルな静的解析でメタ情報を抽出することが難しいためSwaggerなどの外部ツールとの相性が悪いという欠点もあります。

Restyはこのようなケースにおいてもプログラミング言語としてのScalaのアドバンテージを活かすことのできるシンプルなフレームワークがあるといいのではということで作ってみたものです。

使ってみよう

以下のコマンドを実行することでサンプルプロジェクトを実行することができます。

$ git clone https://github.com/takezoe/resty-sample.git
$ cd resty-sample/
$ sbt ~jetty:start

Swagger UIもデフォルトで組み込まれているのでhttp://localhost:8080/swagger-ui/にアクセスすることでサンプルプロジェクトで実装されているAPIの動作を確認することができます。

それでは実際のソースコードも簡単に見ていきましょう。以下は最もシンプルなコントローラの実装例です。

class HelloController {
  @Action(method = "GET", path = "/hello/{name}")
  def hello(name: String): Message = {
    Message(s"Hello ${name}!")
  }
}

case class Message(message: String)

APIのエンドポイントになるメソッドには@Actionというアノテーションを付与する必要があります。メソッドの引数はパスやクエリ文字列、オブジェクトの場合はリクエストボディとして送信されたJSONからRestyによって自動的に渡されます。また、戻り値も型に応じて自動的にテキストやJSONとして返却されます。

コントローラをRestyに登録するために以下のようなリスナも作成しておく必要があります。

@WebListener
class InitializeListener extends ServletContextListener {
  override def contextDestroyed(e: ServletContextEvent): Unit = {
  }
  override def contextInitialized(e: ServletContextEvent): Unit = {
    Resty.register(new HelloController())
  }
}

基本的にはこれだけでWeb APIを実装することができます。

SwaggerとHystrix

Restyでは何もしなくてもSwagger、Hystrixとの連携が可能です。追加で設定を行なったり、メタデータを定義したりする必要はありません。

まずSwaggerですが、SwaggerのJSONはコントローラから自動的に生成され、http://localhost:8080/swagger.jsonで公開されます。また、前述のようにSwagger UIはhttp://localhost:8080/swagger-ui/で利用可能です。

f:id:takezoe:20161128005405p:plain

Hystrixのメトリクスはアクションごとに送信されています。ストリームのエンドポイントはhttp://localhost:8080/hystrix.streamで利用可能なのでHystrixのダッシュボードにこのURLを追加すればすぐにモニタリングを開始することができます。

f:id:takezoe:20161128005416p:plain

まとめ

RestyはScalaJSONベースのWeb APIを作成するための最短の方法を提供します。サーブレット上で動作し、関数型アプローチを採っていません。昨今のScala業界のトレンドに反してはいますが、とても簡単でシンプルで安定しており安全です。また、全自動のSwaggerとHystrix連携が標準で組み込まれています。

是非GitHubリポジトリにアクセスして試してみていただければと思います。