以下の内容はhttps://blog.3qe.us/entry/2025/11/20/231700より取得しました。


Scalambroll: Lambroll + Scala.js + Scala CLIでらくらくFaaSしよう

LambrollというOSSがある。

github.com

AWS Lambdaのデプロイを簡単にしてくれるツールで、非常に使い勝手が良いので仕事で愛用している。

そんなLambrollと、同様に簡単にScalaを書けるようにしてくれるScala CLIとを組み合わせてみたらどうなるだろう、と思って実験してみたところ、すごく簡単にAWS Lambda関数を定義できることがわかったので紹介したい。

今回利用したソースコードはGitHubに公開している:

github.com

Scala CLI

Scala CLIはScala用のスクリプティングツールで、Node.jsでいうところのDenoみたいな存在。

scala-cli.virtuslab.org

blog.3qe.us

Scala.js

今回はScalaをJVMにコンパイルするのではなく、JavaScriptをターゲットにトランスパイルして、Node.jsランタイムでLambdaを実行してみようと思う。JSにトランスパイルすることで、高速な起動時間とV8エンジンのパフォーマンスを得ることができる。

ScalaをJSにトランスパイルするには、Scala.jsを利用すればよい:

www.scala-js.org

Scala CLIでは、--jsオプションをつけるだけでScala.jsが起動して勝手にJSにトランスパイルして実行したりパッケージを作ったりしてくれる。今回はこれを使って楽をするという作戦だ。

下準備

あらかじめAWSのコンソールでNode.jsランタイムのLambda関数を作成しておき、これをlambroll initで取り込んでおく:

λ lambroll --profile=windymelt --region=ap-northeast-1 init --function-name=scalambroll-exercise

関数を書く

index.scalaを作成して関数定義を書こう。JSのランタイムとのインターフェイスで少し気遣いが必要だが、それ以外は普通のScalaだ:

//> using platform js
//> using scala 3.7

import scala.scalajs.js
import scala.scalajs.js.annotation.*

// 各種パラメータなどの型定義

@js.native // こんな感じの型が渡ってくるけど中身はJS側が用意します、というアノテーション
trait Event extends js.Object {
  val name: String = js.native // js.nativeが指定されているところは、「値はJSランタイムで用意するのでコンパイラは気にしないでください」、という意味
}

@js.native
trait Context extends js.Object

// こちらは我々が返す型なのでjs.nativeはない
trait Response extends js.Object {
  val statusCode: Int
  val body: String
}

// 本体

object Index {
  @JSExportTopLevel(name = "handler", moduleID = "index") // JSファイルへの出力を制御するアノテーション。ここではindexモジュールのhandlerという関数としてexportしてください、と明示している
  def handler(
      event: Event,
      context: Context,
      callback: js.Function2[Null, Response, Unit] // JS側の関数を呼び出したいときは若干型の配慮が必要になっている
  ): Unit = {
    println(s"Hello, ${event.name}!")
    val resp = new Response {
      val statusCode: Int = 200
      val body: String = s"Hello, ${event.name}!"
    }
    callback(null, resp)
  }
}

// ダミーオブジェクト(Scala CLIの不具合でいったんこうしている)
// https://github.com/VirtusLab/scala-cli/issues/3964
object Hello {
  def main(args: Array[String]): Unit = 42
}

callback styleはdeprecatedになっているようなので、以下のようにしてpromise styleで書いてもよい:

/* 略 */

object Index {
  @JSExportTopLevel(name = "handler", moduleID = "index")
  def handler(
      event: Event,
      context: Context
  ): js.Promise[Response] = {
    println(s"Hello, ${event.name}!")
    val resp = new Response {
      val statusCode: Int = 200
      val body: String = s"Hello, ${event.name}!"
    }
    js.Promise.resolve(resp)
  }
}

/* 略 */

タスクランナー

今回はMakefileにビルド用のタスクを書いておく:

.PHONY: all build deploy clean
build: dist/index.js
all: build
clean:
    rm -rf dist
dist/index.js: index.scala
    scala-cli package -f --js --js-module-kind es -o dist index.scala
dist/package.json: # ランタイムにESMであるということを明示するためにpackage.jsonを用意する
    mkdir -p dist
    echo '{ "main": "index.js", "type": "module" }' > dist/package.json
deploy: dist/index.js dist/package.json
    lambroll --profile=windymelt deploy --src=dist

scala-cli package -f --js --js-module-kind es -o dist index.scala としている部分がScala CLIを利用してJSファイルを出力している箇所だ。Scala.jsを利用して、ESMとして、distディレクトリに、index.scalaをコンパイルした結果を出力してください、という意味。

あとはそのまま出力先ディレクトリをlambrollに渡せばデプロイできる。

デプロイ

make deployすれば終わり。

λ make deploy
scala-cli package -f --js --js-module-kind es -o dist index.scala
Compiling project (Scala 3.7.4, Scala.js 1.20.1)
[warn] ./index.scala:39:41
[warn] Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
[warn]   def main(args: Array[String]): Unit = 42
[warn]                                         ^^
Compiled project (Scala 3.7.4, Scala.js 1.20.1)
Wrote /home/windymelt/src/github.com/windymelt/scalambroll-exercise/dist/main.js, run it with
  node ./dist/main.js
lambroll --profile=windymelt deploy --src=dist
2025/11/20 22:35:52 [info] lambroll v1.3.2
2025/11/20 22:35:52 [info] loading Function from function.json
2025/11/20 22:35:52 [info] starting deploy function scalambroll-exercise
2025/11/20 22:35:52 [info] creating zip archive from dist
2025/11/20 22:35:52 [info] zip archive wrote 14291 bytes
2025/11/20 22:35:52 [info] updating function configuration
2025/11/20 22:35:52 [info] updating function configuration ...
2025/11/20 22:35:52 [info] State:Active LastUpdateStatus:Successful
2025/11/20 22:35:52 [info] updating function configuration accepted. waiting for LastUpdateStatus to be successful.
2025/11/20 22:35:52 [info] State:Active LastUpdateStatus:InProgress
2025/11/20 22:35:52 [info] waiting for LastUpdateStatus Successful
2025/11/20 22:35:53 [info] State:Active LastUpdateStatus:InProgress
2025/11/20 22:35:53 [info] waiting for LastUpdateStatus Successful
2025/11/20 22:35:56 [info] State:Active LastUpdateStatus:Successful
2025/11/20 22:35:56 [info] updating function configuration successfully
2025/11/20 22:35:56 [info] updating function code ...
2025/11/20 22:35:56 [info] State:Active LastUpdateStatus:Successful
2025/11/20 22:35:56 [info] updating function code accepted. waiting for LastUpdateStatus to be successful.
2025/11/20 22:35:56 [info] State:Active LastUpdateStatus:InProgress
2025/11/20 22:35:56 [info] waiting for LastUpdateStatus Successful
2025/11/20 22:35:57 [info] State:Active LastUpdateStatus:InProgress
2025/11/20 22:35:57 [info] waiting for LastUpdateStatus Successful
2025/11/20 22:35:59 [info] State:Active LastUpdateStatus:InProgress
2025/11/20 22:35:59 [info] waiting for LastUpdateStatus Successful
2025/11/20 22:36:04 [info] State:Active LastUpdateStatus:Successful
2025/11/20 22:36:04 [info] updating function code successfully
2025/11/20 22:36:04 [info] deployed version 9
2025/11/20 22:36:04 [info] updating alias set current to version 9
2025/11/20 22:36:04 [info] alias updated

試しに{"name": "windymelt"}を渡して実行してみよう。

やった〜。

まとめ

  • Scala CLIを利用すると簡単にScalaをトランスパイルしたESMを作成できることを確認した
  • Lambrollを利用してScalaコードをそのままAWS Lambdaとしてデプロイできることを確認した

Scala CLIとLambrollとは簡単で実用的な組み合わせだ。型安全なソフトウェアを手軽に利用できるし、Scala CLIにはパッケージマネージャが同梱されているため、設定ファイルが一切必要ないのが非常にうれしいポイントだ。




以上の内容はhttps://blog.3qe.us/entry/2025/11/20/231700より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14