ScalaのSpray-routingとSlickで簡易RESTful APIつくってみる
What Spray-routing?
spray | Documentation » 1.2.4 / 1.3.4 » spray-routing
Spray-routingは、RESTfulなWebサービスのためのハイレベルかつ、柔軟なルーティングを提供するモジュール。簡単にルーティングを作れる。
What Slick?
準備
今回もPostgreSQLを使います。
以前のSlick記事にMacOSへのインストール方法書いてます。
作成するユーザ、データベース、テーブルも以前の記事に合わせます。
# PostgreSQL 起動 brew services start postgresql # PostgreSQL 接続 psql -d postgres ## 今回の接続ユーザであるrootを作成 create user root; ## 今回の接続データベースであるrootを作成 create database root; ## PostgreSQL 接続終了 \q # rootデータベースにrootユーザで接続 psql -d root -U root ## 今回使用するテーブル作成 create table players ( id serial primary key, name varchar(80), birthday varchar(80) ); ## rootでの接続終了 \q
Installation
// build.sbt name := "sprayrouting_app" version := "1.0" scalaVersion := "2.11.8" libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % "2.4.10", "com.typesafe.slick" %% "slick" % "3.1.1", "org.postgresql" % "postgresql" % "9.4-1200-jdbc41", "io.spray" %% "spray-can" % "1.3.3", "io.spray" %% "spray-routing" % "1.3.3", "io.spray" %% "spray-json" % "1.3.2" )
設定ファイルの作成
// src/main/resources/application.conf pg_db = { url = "jdbc:postgresql://localhost/root?user=root" driver = org.postgresql.Driver connectionPool = disabled keepAliveConnection = true }
Source code
// src/main/scala/Boot.scala import akka.actor.{Props, ActorSystem} import akka.io.IO import spray.can.Http object Boot extends App { implicit val system = ActorSystem("on-spray-can") val service = system.actorOf(Props[MyServiceActor]) IO(Http) ! Http.Bind(service, interface = "localhost", port = 8080) }
Boot.scalaはアクターのエントリーポイントを作成。
// src/main/scala/MyService.scala import akka.actor.Actor import scala.concurrent.Await import scala.concurrent.duration.Duration import scala.concurrent.ExecutionContext.Implicits.global import slick.driver.PostgresDriver.api._ import spray.routing._ import spray.json._ import DefaultJsonProtocol._ class MyServiceActor extends Actor with MyService { def actorRefFactory = context def receive = runRoute(myRoute) } trait MyService extends HttpService { val db = Database.forConfig("pg_db") val players = TableQuery[Players] def getPlayers() = { val query = db.run(players.result) val result = Await.result(query, Duration.Inf) result.toJson.toString } def createUser(name: String, birthday: String) = { val query = db.run(players.map(p => (p.name, p.birthday)) += (name, birthday)) val message = "created name:" + name + " player" message.toString } def getPlayer(id: Int) = { val query = db.run(players.filter { _.id === id }.result) val result = Await.result(query, Duration.Inf) result.toJson.toString } def updateUser(id: Int, name: String, birthday: String) = { val query = db.run(players.filter(_.id === id).map(p => (p.name, p.birthday)).update((name, birthday))) val message = "updated id:" + id + " player" message.toString } def deleteUser(id: Int) = { val query = db.run(players.filter { _.id === id }.delete) val message = "deleted id:" + id + " player" message.toString } val myRoute = path("players") { get { complete { getPlayers() } } ~ post { formFields('name, 'birthday) { (name, birthday) => complete { createUser(name, birthday) } } } } ~ path("players" / IntNumber) { id => get { complete { getPlayer(id) } } ~ put { formFields('name, 'birthday) { (name, birthday) => complete { updateUser(id, name, birthday) } } } ~ delete { complete { deleteUser(id) } } } }
MyService.scalaはルーティングとデータベース処理。
Awaitつかってブロッキングです。
// src/main/scala/Player.scala import scala.concurrent.ExecutionContext.Implicits.global import slick.driver.PostgresDriver.api._ class Players(tag: Tag) extends Table[(Int, String, String)](tag, "players") { def id = column[Int]("id", O.PrimaryKey) def name = column[String]("name") def birthday = column[String]("birthday") def * = (id, name, birthday) }
Players.scalaには作成したplayersテーブルの情報。
Compile & Run
# コンパイル、実行
sbt compile run
Try Request
# curl インストール brew install curl # Try Create curl -X POST -F "name=a" -F "birthday=1990-01-01" http://localhost:8080/players # Try Read curl -X GET http://localhost:8080/players curl -X GET http://localhost:8080/players/1 # Try Update curl -X PUT -F "name=b" -F "birthday=1980-12-30" http://localhost:8080/players/1 # Try Delete curl -X DELETE http://localhost:8080/players/1