Slick codegen and Scala.js05 Nov 2015. 3 minute read.
Since Scala.js came out, I’ve wanted to use the Slick codegen module to generate my data model and share classes between the client and server. It took me a while to reach a setup I liked, below are some of my lessons.
TL;DR. Haven’t found a perfect solution yet, but working example is on Github.
This post assumes you have knowledge of both sbt and Slick, no knowledge of Scala.js is required.
Let’s begin, I have some SQL code from which I want to generate boilerplate Slick mapping. My sbt project looks like this
build.sbt <-- second step project/ plugins.sbt <-- first step Codegen.scala <-- third step server/ src/models/Tables.scala <-- autogenerated conf/db/evolutions/1.sql <-- database stuff shared/ src/models/Tables.scala <-- autogenerated src/models/Time.scala <-- inherits java.util.Date client/ slick-driver/ src/slick/Driver.scala <-- custom driver server-codegen/ <-- empty project to run server codegen shared-codegen/ <-- empty project to run shared codegen flyway/ <-- empty project to run migrations
This seems like a lot of projects. Don’t fear, creating projects in sbt is easy.
The first step is to add necessary plugins to
plugins.sbt, most importantly sbt-slick-codegen.
The second step is to define the projects in
You don’t necessarily need all the bells and whistles from that build definition, but the project definitions are required.
We’re done with the boring part.
The third step is to write code that generates even more code!
We write this in
This section has much room for improvements, but I think my findings below are not too bad.
I struggled most with dates, custom type mapping and using a custom driver.
I want to map
My solution was to create a simple wrapper
Timestamp that inherits from
The benefit to this approach is that
Timestamp is a case class.
Case classes play well with Slick’s
* method and also make it possible to use libraries such as
The downside to this approach is that I created Yet Another date class for my own project. Ideally, these should be part of some reusable library.
To get a richer date API, provide an implicit conversion to Joda-Time on the server and Moment.js on the client.
Reuse custom type maps
I want to reuse my custom type mappings between the server and shared code generators.
SourceCodeGenerator API makes this trickier than I expected (or maybe my trait foo isn’t good enough).
Eventually, I settled on using trait mixins, see
I haven’t tried to map more custom types than
I imagine that the
colMap will grow alongside your application.
Use custom slick-pg driver
Skip this part if you don’t want to use the latest and greatest features in PostgreSQL.
slick-pg exposes many of PostgresSQL’s best features with Slick.
Unfortunately, there isn’t a good way to use these features on H2 during testing/development.
For me, the trade-off is worth it.
lazy val profile = is.launaskil.slick.Driver into the server
Tables.scala instead of
val profile: JdbcProfile.
I won’t look back (but let me know if you find a solution ;)).
Note. You must publish the Slick driver locally before running the app, Play’s custom class loaders will otherwise not find the driver, see #266.
Run the app
Excellent, everything is almost complete. The fourth step is to run the app.
First, you need to start the database. I use docker (on OSX) and run the following command
$ docker run -p 5432:5432 --name launaskil-dev -e POSTGRES_PASSWORD=postgres -d postgres
Next, I source the database connection parameters and fire up sbt like this
$ source source_me && sbt
Next, I run migrations with Flyway, as demonstrated in the sbt-slick-codegen-example.
[server] $ flyway/flywayMigrations
p to publish the slick driver and then
cg (short for “codegen”) to run the code generation.
These aliases are defined in build.sbt.
[server] $ p [server] $ cg
Take a look at the generated
The server file should contain only Slick table mappings and the shared file should only contain case classes.
To verify the project compiles, execute
run in the
sbt console and visit
The browser should display a message like
List(AppUserRow(1,Some(Sun Nov 14:14:09 GMT+0100 2015),None,List(a)))
That’s a wrap. We just managed to use Slick codegen to generate our data model from SQL such that we can share classes between the client and server. The solution supports dates, custom type maps and custom drivers.
It would be neat to put some of this stuff into an sbt plugin.
PS. You’ll see
is.launaskil sprinkled around the app.
Launaskil.is is a website I wrote last summer and the examples are borrowed from the app’s closed source.