前置き
ScalaのWebフレームワークを触ってみる第2弾として、今回はAkka Http を軽〜く触ってみます。
Akka Http は正確にはフレームワークではなく、 HTTPベースのサービスを提供および利用するためのより一般的なツールキット との事。
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of akka-actor and akka-stream. It’s not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction with a browser is of course also in scope it is not the primary focus of Akka HTTP.
事前準備
Akka Httpを動かすのに必要な次のものを事前にインストールしてください。今回は次の通り。
- Java 11
 - Scala 2.13.3
 - sbt 1.3.13
 
Akka Httpプロジェクト作成
好きなところにディレクトリを作成して次のコマンドを実行
sbt new akka/akka-http-quickstart-scala.g8
対話形式で色々聞かれるので今回は次の構成とします。
name [My Akka HTTP Project]: akka-http-sample
scala_version [2.13.3]:
akka_http_version [10.2.0]:
akka_version [2.6.9]:
organization [com.example]:
package [com.example]:
作成されたプロジェクトをIntelliJで読み込むと次のように色々ファイル群が生成されています。

JsonFormats
JSONのリクエスト/レスポンスをフォーマットして任意のcase classにマッピングしてくれる。QuickStartの例だと、UserとUsersについて定義されている。
※jsonFormatN()のNは対象case classの引数の数
QuickstartApp
サーバーの起動に関するメイン処理。ここでActorの起動や、Routeの指定をしている。
UserRegistry
Routeで処理している各種Userに関するCRUD処理を定義している。
UserRoutes
Routing定義
動作確認
実際に動かして確認してみます。
起動する
IntelliJで QuickstartAppのmainを実行するとサーバが http://localhost:8080 で起動します。
リクエストを投げてみる
UserRoutesには、既にユーザーに対する処理が実装されています。それぞれに対応するリクエストを投げてみます。
0. いきなり番外編
http://localhost:8080 に対する処理は実装されていませんので、対応するルーティングをちょっと記述します。
val userRoutes: Route =
    pathSingleSlash { // 追加ここから
      get {
        complete((StatusCodes.NotFound, "hello"))
      }
    } ~ // 追加ここまで
    pathPrefix("users") { // 以下省略 }
これで http://localhost:8080 にアクセスすると "hello"が表示されます。
1. POST /users
post {
  entity(as[User]) { user =>
    complete(createUser(user))
    onSuccess(createUser(user)) { performed =>
      complete((StatusCodes.Created, performed))
    }
  }
}
リクエスト
POST http://localhost:8080/users
Content-Type: application/json
{
  "name": "山田太郎",
  "age": 18,
  "countryOfResidence": "Japan"
}
JsonFormatsの次の定義を解釈してJSONをUserケースクラスにマッピングしてくれます。
implicit val userJsonFormat = jsonFormat3(User)
レスポンス
{
  "description": "User 山田太郎 created."
}
Response code: 201 (Created); Time: 21ms; Content length: 36 byte
2. GET /users
get {
  complete(getUsers())
}
リクエスト
GET http://localhost:8080/users
Accept: application/json
レスポンス
{
  "users": [
    {
      "age": 18,
      "countryOfResidence": "Japan",
      "name": "山田太郎"
    },
    {
      "age": 22,
      "countryOfResidence": "Japan",
      "name": "田中次郎"
    }
  ]
}
Response code: 200 (OK); Time: 694ms; Content length: 119 bytes
こちらも、JsonFormatsの次の定義を解釈してUsersケースクラスをJSONにパースしてくれます。
implicit val usersJsonFormat = jsonFormat1(Users)
3. GET /users/{name}
path(Segment) { name =>  //path(Segment)でリクエスト中の{name}の文字列を取得している
  concat(
    get {
      //#retrieve-user-info
      rejectEmptyResponse {
        onSuccess(getUser(name)) { response =>
          complete(response.maybeUser)
        }
      }
    //#retrieve-user-info
    },
リクエスト
GET http://localhost:8080/users/山田太郎
Accept: application/json
レスポンス
{
  "age": 18,
  "countryOfResidence": "Japan",
  "name": "山田太郎"
}
Response code: 200 (OK); Time: 23ms; Content length: 53 bytes
4. DELETE /users/{name}
path(Segment) { name => 
  concat(
    { // 省略・・・
    },
    delete {
      //#users-delete-logic
      onSuccess(deleteUser(name)) { performed =>
        complete((StatusCodes.OK, performed))
       }
      //#users-delete-logic
    }
  )
}
リクエスト
DELETE http://localhost:8080/users/山田太郎
レスポンス
{
  "description": "User 山田太郎 deleted."
}
Response code: 200 (OK); Time: 689ms; Content length: 36 bytes
まとめ
ほんとに軽〜く触ってみましたが、Quickstartで生成されるサンプルにAkkaやActorの処理が入り込んでいるので、パッと見はとっつきづらい印象がありますね。軽いREST APIを書くのであれば Scalatraの方が個人的にはわかり易かったです。