Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

シンプルで使いやすいマイクロHTTPフレームワーク『Cask』を紹介するよ

今日はScalaで動作するマイクロHTTPフレームワークであるCaskを紹介したいと思います。Scalaを見る目が変わると思います。Scalaはけっこう複雑寄りのエンタープライズグレードの超強いライブラリが多いような気がするのですが、今回ご紹介するのはとてもeasyで便利なやつです。僕は当分これでいいと思います。

この記事は、はてなエンジニア アドベントカレンダー12日目です。

developer.hatenastaff.com

昨日の記事はmob programming を續けてみた - c4se記:さっちゃんですよ☆でした。モブプロ自分のチームでもたまにやってるのですが、目玉が多いだけあってどんどん進みますよね。すごく疲れるけど・・・

Caskって何よ

CaskPythonのFlaskなどにインスパイアされたScalaのマイクロHTTPフレームワークです。

com-lihaoyi.github.io

作者はScalaのOSS界では高品質なプロダクトをどんどん作っていることでおなじみのlihaoyiさんです。

www.lihaoyi.com

元来、Scalaにはhttp4sやPlay Framework、Akka HTTPなどのHTTPフレームワーク/ライブラリが多く用意されています。そんな中なぜフレームワークを作ったのでしょう?

作者のlihaoyiさんによれば、CaskはPythonのFlaskにインスパイアされた、シンプルさ・柔軟さ・使いやすさを主眼に置いたフレームワークだそうです。

従来のライブラリはシンプルさと多機能さを重視するあまり、初心者にとっては暗号のようなDSLを駆使しなければならなかったりと、Node.jsやPythonなどの文化と比べるとかなりハードルが高くなりがちでした。

Caskでは他の多くのライブラリで採用されているバックプレッシャやストリーミングといった専門的な機能を捨ててシンプルなHTTPサーバとして割り切ることで、素朴なScalaで書けるとても使いやすいAPIを実現しています。

Scala Scriptで書いてみよう

試しにCaskでちょっとしたサーバを書いてみましょう。CaskはsbtやmillといったScalaのビルドツールで利用できますが、Scala CLIを使うのが一番簡単です。適当なディレクトリにserver.scalaを作成し、以下のように記載しましょう。

//> using scala "3.3.1"
//> using dep "com.lihaoyi::cask:0.9.1"

object MyApp extends cask.MainRoutes:
  initialize()

Scala CLIは以下の手順を参考に簡単にインストールできます。

blog.3qe.us

コードを見ての通り、cask.MainRoutesを継承したオブジェクトを作ってinitialize()を呼ぶだけで最小限のサーバが起動します。

実際にscala-cliコマンドで起動してみましょう。

% scala-cli server.scala

これだけでlocalhost:8080にHTTPサーバが起動します。何もエンドポイントが無いので、どこにアクセスしても404を返します。

エンドポイントを生やしてみよう

これだけだと404しか返さないのでエンドポイントを書きます。メソッドにアノテーションを付けるとそれがそのままエンドポイントになります。めちゃ簡単じゃないですか?

//> using scala "3.3.1"
//> using dep "com.lihaoyi::cask:0.9.1"

object MyApp extends cask.MainRoutes:
  @cask.get("/")
  def hello(who: String = "Cask") = s"Hello, $who !"

  initialize()
end MyApp

これでサーバを起動すると、whoをクエリパラメータに受け取るエンドポイントが動作します。

% curl localhost:8080/?who=Windymelt
Hello, Windymelt !

もちろんPOSTも対応できるし、JSONやフォームデータの受け取りにも対応してます。JSONライブラリとしてデフォルトではujsonが勝手に付いてきますが、ユーザが少し書けばCirceなどの本格的なJSONライブラリも利用可能になっているようです。

//> using scala "3.3.1"
//> using dep "com.lihaoyi::cask:0.9.1"

object MyApp extends cask.MainRoutes:
  @cask.get("/")
  def hello(who: String = "Cask") = s"Hello, $who !"

  @cask.get("/json")
  def helloJson() =
    ujson.Obj(
      "hello" -> "Cask JSON!"
    )

  @cask.post("/do-thing")
  def doThing(request: cask.Request) =
    request.text().reverse

  @cask.postJson("/do-thing-json")
  def doThingJson(key1: String, key2: Option[Int]) =
    ujson.Obj(
      "length_of_key1" -> key1.length,
      "double_of_key2" -> 2 * key2.getOrElse(42)
    )

  initialize()
end MyApp

何の変哲もないScalaのコードなので、初心者にもかなり取っつきやすく、上級者でもだいたい十分な機能を備えていると感じます。

どうして紹介したかというと最近Honoを触って体験が良かったから

Caskを紹介しようと思った背景には、最近触ったHonoの存在があります。Honoもまた、ミニマルなHTTPフレームワークであり、エッジで動作することを前提に開発されたNode.jsライブラリです。

hono.dev

最近仕事でHonoを使うことがあって、その書き味の良さに感激したんですよね(というか、はてなエンジニアはこういうミニマルで透明なものを好む傾向があると思います)。

何が琴線に触れたのかな〜と思い返してみたのですが、「ベースラインが超簡単であること」「やりたい事の難しさに応じて徐々に難しくなるような比例関係が成立していること」「言語が元々持っているセマンティクスを尊重すること」が良さだと感じました。

これらの特性を満たしているツールは楽しくサクサク書き進めることができるように思います。こういったツールが増えないかな〜と思っていたところCaskを知り、これもまた使い勝手がとても良かったので紹介しようと思い立ったというわけです。

Funな書き味を目指して

さて、僕はScalaの日本語コミュニティ『Scalaわいわいランド』を共同運営している程度にはScalaが好きで、普及のために色々なことを考えているのですが、最近の課題として、初学者がスムーズにScalaを使えるようになってほしいけれど教材として使えるライブラリは一級品である代わりに難しくなりがち、という意識がありました。業務の目線でも、ちょっとした仕事をしてほしいだけなのに、オーバーキルなライブラリを使わなければならない、それゆえにメンテナンスコストが上がって採用しづらいという矛盾を感じていました。

Caskのようなeasyなツールは専門的な機能を切り離していますが、だからといって力不足だとか使えないということは決してなく、十分強力でありながら専門知識をあまり要求せず、エンジニアのfunを満たしてくれる、そんなツールだと思います。

こうしたeasyでfunなツールが増えていけば、Scalaを使ってみる間口が広がっていき、分厚いユーザ層を構築できるのではないかと考えています。やっていくぞ!

明日は

明日は @_maiyama18 だッッッ!!!

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?