I have to admit I am far from expert in Scala, that's why when recently I had to prototype an API in this language, I shuddered. Frameworks like Akka HTTP or HTTP4S were far too complex for a novice in the field like me, because learning Scala is not about the syntax, but about the mindset. And it's only logical that someone coming from the field of totally different languages such as Python or Javascript, can have a little trouble understanding concepts like the actor model or the functional proper way of using HTTP4S.
The original Actor Model
Cask
That's why I welcomed the discovery of Cask, a micro HTTP framework which tries to follow closely the footsteps of Flask for Python. This looked like a good compromise between not switching straight away to Akka/Http4s, but still enjoying the benefits of a strongly typed language as interesting as Scala.
Example of Cask usage for an API endpoint from a Postgres database
Setting up:
The author recommends to use his own build tool called Mill. Even though I found it easy to understand, I decided to stick with SBT out of habit, since that is what I had used in the past.
Dependencies for this project:
libraryDependencies ++= Seq("com.lihaoyi" %% "cask" % "0.2.9",
"com.lihaoyi" %% "utest" % "0.7.4" % Test,
"com.lihaoyi" %% "requests" % "0.5.1",
"org.postgresql" % "postgresql" % "42.2.8",
"ch.qos.logback" % "logback-classic" % "1.2.3",
"io.getquill" %% "quill-jdbc" % "3.2.0",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
"com.typesafe" % "config" % "1.4.0")
What are we retrieving in the API
This is an API which retrieves information about movies showing in the local cinemas of my city, so the modeled data is basically a movie. We create a case class, because that is what will be used by Quill to map to the values in the Postgres table and be able to retrieve the data. So when I do:
case class Movies(
cinema: String,
details: String,
title: String,
movie_date: String,
movie_time: String
)
with this I actually mean that there is a table with the name movies
that has as fields each of the case class fields. By the way! I am aware that certain data types are not correct. Date and time should not be strings. However, this is for the moment done like this to deal with data in a database that has been scraped and does not conform to proper date format yet. Work in progress!
How to manage all the connection settings for the DB
As simple as creating an application.properties
file in your resources folder with the following content
ctx.dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
ctx.dataSource.user=your_user
ctx.dataSource.password=your_password
ctx.dataSource.databaseName=your_db_name
ctx.dataSource.portNumber=port_(5432_for_postgres)
ctx.dataSource.serverName=your_server
ctx.connectionTimeout=up_to_you
Of course, take the necessary precautions when dealing with version control and this file.
So where is my easy API?
There you go. We do this via defining a route for movies
with a function which creates a context to run the database query. See the Quill documentation for more details, but in this example we are basically doing a SELECT * from movies
, so it couldn't get easier.
object moviescrape extends cask.MainRoutes with LazyLogging {
override def port: Int = 8081
lazy val ctx = new PostgresJdbcContext(SnakeCase, "ctx")
import ctx._
@cask.get("/movies")
def getMovies() = {
val content = ctx.run(query[Movies])
.map(m => {ujson.Obj("cinema" -> ujson.Str(m.cinema),
"details" -> ujson.Str(m.details), "title" -> ujson.Str(m.title),
"movie_date" -> ujson.Str(m.movie_date),
"movie_time" -> ujson.Str(m.movie_time))})
upickle.default.write(ujson.Obj("data" -> content))
}
initialize()
}
Things might get a bit tricky around the part of returning the proper json with the response. I tried a few different tools, such as Circe and the author's own Ujson and at the end I settled with the latter.
But... will it scale?
I don't think this library is meant to handle your super scalable APIs for deployments that need to bear a load of millions of users, but if you are ever in the need of playing around with API development in Scala in order to understand the language a little better, Cask will definitely help you to get your feet wet without the headaches of jumping straight into Akka HTTP.
Top comments (1)
Cool article, thanks for sharing.
Finatra is also a really cool tool to create powerful scalable APIs (itβs the one Twitter uses and itβs open source)