Deploying a full stack Scala application on Heroku

Antoine Doeraene
6 min readFeb 19, 2020

--

Not so long ago I was thinking “how is it not possible in 2020 to have a free (crappy) host for a JVM based server with a (potentially also crappy) database?”. And then I discovered Heroku.

Heroku gives you the opportunity to host for free a server with a small (no more that 10k lines) Postgres database. They moreover have built in support for a Scala Play application, in which case you can very easily push your application code, and Heroku compiles and deploy everything for you.

In my case, though, I wanted a little bit more: my frontend is also developed in Scala, using Scala.js for generating the JavaScript. The advantage of doing so is real: you can share a lot of code between your frontend and your backend, avoiding a lot of hassle in writing twice model definitions and validations. The dream, then, is of course to be able to run a simple sbt magicCommand in order to compile the frontend, package the backend with it, ship it to Heroku and deploy. Good news: that dream easily comes true.

A small example project is available here. The backend there uses Play (but could use anything else such as http4s or cask) and the frontend uses Laminar (but could certainly also use Slinky, Scala-js-react or Scalatags).

TLDR: use the sbt stage backend/deployHeroku from the sbt-heroku plugin where stage should also compile the frontend.

Heroku and Scala logos

Project structure

A full stack Scala project is typically divided into three different sub-projects: one for the backend, one for the frontend, and a third one on which the two first depend and that will therefore cross-compile for JS and for the JVM.

The general structure of the project then looks like

build.sbt
- project
- backend
-- src
- frontend
-- src
-- webpack
- shared
-- src

The shared project is the place where you put all the code that both your frontend and your backend are going to use. For example, all of your model definitions can go there because you will need them everywhere. That way, there is only one source of truth for your models and they are always in sync.

In particular, the frontend and the backend communicate using JSON, and to that end the shared project (and hence all of them by transitive dependencies) uses the circe library for JSON (de)serialisation. Since the library itself is cross-compiled, we are sure that our protocoles will never mismatch.

Compiling the frontend

Following the great work done by Slinky, the frontend uses webpack and scala-js-bundler for development and build purposes. We get a nice “hot reload” functionality when coding (thanks to the dev alias command) and a simple frontend/fullOptJS/webpack task to build the frontend ready for production, by outputting the packages files to the backend’s public folder.

Create the app

Creating a new application is extremely easy with the Heroku CLI. In our case, we want to create a simple application, together with a free Postgres database. This database is limited to 10000 lines (which should not give you the idea to transpose your tables).

We simply need the two following lines (after issuing heroku login ):

heroku apps:create APP_NAME --region eu

This creates the app with the given name on a server located in Europe (creating the server in the US is also possible). The APP_NAME will be important for the sbt plugin. Then we add the database with

heroku addons:create heroku-postgresql:hobby-dev

And that is it! We will now deploy some code on the app.

The sbt-Heroku plugin

A Heroku application can typically be deployed by sending the code and let Heroku manage compilation and deployment. And that is possible for 8 different programming languages and at least that many web frameworks. However, for Scala applications, there is another route which is through the “sbt-heroku” plugin. This plugin gives us the ability to package the application ourselves (which is good in this case since we need to package the frontend anyway) and then send the packaged output to Heroku so that it can deploy the app for us.

The first thing to do is to add the plugin to the project/plugins.sbt file (you should check the version on the github repo):

addSbtPlugin("com.heroku" % "sbt-heroku" % "2.1.3")

And we can disable it for every project but the backend (via disablePlugins(HerokuPlugin)), since it’s the only one that we are going to deploy. Then, sbt-heroku requires some settings that are described in the doc. The small catch is that in our case, these settings must be defined inside the backend project. I.e., something like

lazy val `backend` = (project in file("./backend"))
.enablePlugins(PlayScala) // for example
.settings(
herokuAppName in Compile := "APP_NAME",
herokuProcessTypes in Compile := Map(
"web" -> "target/universal/stage/bin/backend -Dhttp.port=$PORT" // command for Heroku to launch the server
//"worker" -> "java -jar target/universal/stage/lib/my-worker.jar"
),
herokuIncludePaths in Compile := Seq(
"backend/app",
"backend/conf/routes",
"backend/conf/application.conf",
"backend/public"
),
// [...]
)
.dependsOn(sharedJVM)

After login in to Heroku (via heroku login ), if you now run sbt stage backend/deployHeroku , you will likely hit two problems

  • the frontend does not build into the backend public directories, and
  • you will see that the task backend/deployHeroku finishes with “success” but nothing actually happened.

(If you do not use Play, you might also have the problem that the stage task is not defined. You need the sbt native packager for that to work.)

The first thing is easy to fix. We simply need to override the stage task in the following way

stage := {
val webpackValue = (frontend / Compile / fullOptJS / webpack).value
println(s"Webpack value is $webpackValue")
(stage in backend).value
}

The second “failure” comes from the fact that when building, sbt-heroku checks that either there exists a “project” directories, or that herokuSkipSubProjects is false. This is a bit weird but, in any case, it’s easily solved by adding herokuSkipSubProjects in Compile := falsein the backend settings.

You might also run into the issue of OOM with the fullOptJS task (because of the Google closure compiler). This is easily fixed by giving sbt more memory.

And that is all you have to do in order to compile, package and deploy your full stack Scala application!

Accessing the database

We created an app with a Postgres database. To connect to it, we need to have, for example, the JDBC url. In Heroku, any application provided with a Postgres database has an environment variable called JDBC_DATABASE_URL . If you use the configuration files from Typesafe, you can for example retrieve it with

url = ${?JDBC_DATABASE_URL}

The “?” means that it does not override a previous value for url if the environment variable does not exist. This is handy for working in dev: you can use a local database address in that case.

Specific to Play

If you are using the Play framework, here are some small random remarks that could serve as a FAQ:

  • in production, you need to define an application secret. You can easily do that by setting an environment variable on your Heroku app, for example via heroku config:set APPLICATION_SECRET=mycoolsecret and then in the configuration file, use play.http.secret.key = ${?APPLICATION_SECRET}
  • your application in Heroku lives in a container, so the origin actually accessing your app is probably unknown. Play will reject incoming connections unless you set play.filters.host.allowed = ["."] .
  • another consequence on having the application living in a container is that you need to know the port on which you traffic will pass. On Heroku, you receive an environment variable PORT which contains that piece of information
  • you most likely want to enable auto evolutions of the database. This is done via play.evolutions.autoApply = true
  • in dev mode, you can create a configuration file with dev values for all the afore-mentioned variables, such as the db url

Wrap up

Hopefully this post can save you the hassle to collect all the small bits of information needed to put all of this together. Admittedly, this post is also a letter to me in the future, but I figured I could share it!

Once everything is set up (which is not even that hard/long), it’s a genuine pleasure to only have one sbt task to run, and have everything deployed within a few minutes!

--

--

Antoine Doeraene
Antoine Doeraene

Written by Antoine Doeraene

Mathematician, Scala enthusiast, father of two.

Responses (1)