Uk News

How to distribute Scala Play to Heroku

I’ve been a web developer for years, but I haven’t touched Java for a long time like the late 90s. At that time, Java development felt cumbersome: numerous boiler plates and complex configuration. It wasn’t exactly a pleasant experience to create simple web applications. Recently, when I started to discover the scala and the game frame, I was more curious about everything. Is the Java developer experience better? Actually, is it something I want to use today?

Scala works on the Java virtual machine, but brings a more impressive, modern syntax to the table. Usually used in rear end development, especially when performance and scalability are important. Play is a web frame designed to be a fast, reactive and developer friendly, built for Scala. And you use SBT, the building vehicle of Scala – can be compared with Maven or Gradle in the Java world.

In this article, I will establish a basic Scala play application, run locally and then distribute it to Heroku. My hope is to show how you will make your application work smoothly without having to know a lot about how the game works under the hood of JVM or the game.

Introduce the sample application

I’m starting with an existing sample project to keep things simple: Play Scala Rest API example. It is a small application that creates several extremes to create and receiving blog broadcasts. All data are stored in memory, so there is no database to be configured – perfect for test and distribution experiments.

To make things easier for this road track, I cloned this sample repo and made a few fine adjustment to prepare it for Heroku. You can follow my Github Repo. This is not an application ready for production and that’s the point. Exploring how the game works and seeing how a Scala application works and how to distribute actually feels.

Scrumpify the code

Before distributing something, let’s take a quick tour of the application. This is a small code of code, but there are a few things worth pointing – especially if you are new in Scala or you are wondering how it is compared with more traditional Java. To create an API by reading this basic description page, I learned a lot about the entrances and outputs of the Play Framework. Here are some important points:

Case Class for Model Resources

The basic source for this Rest API is a idlinktitleAnd body. The simplest way to model this is a Scala Case class that looks like this:

case class PostResource(
  id: String,
  link: String,
  title: String,
  body: String
)

This happens on top app/v1/post/PostResourceHandler.scala (connection).

Routing is clean and central

. conf/routes File maps are in an easy format to read and replace HTTP requests to a router. It feels closer to an XML -based Java configuration such as express or bottle. In our example, the file is only one line:

->         /v1/posts               v1.post.PostRouter

From there, the router app/v1/post/PostRouter.scala (Link) How to define the controller methods of different requests on this road map.

override def routes: Routes = {
  case GET(p"/") =>
    controller.index

  case POST(p"/") =>
    controller.process

  case GET(p"/$id") =>
    controller.show(id)
}

This is quite clear. A GET Demand for the Road Root takes us to the supervisor index method, POST The request takes us to him process method. Meanwhile, GET Request with a blog post including id Will take us show method and given id.

Auditors Essential

This brings us to our controller app/v1/post/PostController.scala (connection). Every end point, one ActionPlaying with JSON as a result of the play’s settled assistants. For example:

def show(id: String): Action[AnyContent] = PostAction.async {
  implicit request =>
    logger.trace(s"show: id = $id")
    postResourceHandler.lookup(id).map { post =>
      Ok(Json.toJson(post))
    }
}

There are very few source plates – do not need separate interface notifications or detailed additional explanations.

JSON is handled with implicit values

Play, JSON uses implicit values ​​to process serialization and processing. You define a implicit Format[PostResource] Play Json’s macros, and then the Play knows how to transform your objects into JSON and over and over again. No manual separation or detailed configuration is required. We see this app/v1/post/PostResourceHandler.scala (connection):

object PostResource {
  /**
    * Mapping to read/write a PostResource out as a JSON value.
    */
    implicit val format: Format[PostResource] = Json.format
}

Modern, impressive syntax

When examining the code, I see some of the more impressive features of Scala in motion – things like map operations and matching. His syntact is new at first, but it quickly sounds like a aerodynamic mixture of Java and JavaScript.

Run the application locally

Before distributing it to the cloud, it is always a good idea to make sure that the application works locally. This helps us capture obvious problems and allows us to poke around API.

Java in my machine and sbt loaded.

~/project$ java --version
openjdk 17.0.14 2025-01-21
OpenJDK Runtime Environment (build 17.0.14+7-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 17.0.14+7-Ubuntu-120.04, mixed mode, sharing)

~/project$ sbt --version
sbt version in this project: 1.10.6
sbt script version: 1.10.6

Then I run the following from my project root:

~/project$ sbt run

[info] welcome to sbt 1.10.6 (Ubuntu Java 17.0.14)
[info] loading settings for project scala-api-heroku-build from plugins.sbt...
[info] loading project definition from /home/alvin/repositories/devspotlight/heroku/scala/scala-api-heroku/project
[info] loading settings for project root from build.sbt...
[info] loading settings for project docs from build.sbt...
[info]   __              __
[info]   \ \     ____   / /____ _ __  __
[info]    \ \   / __ \ / // __ `// / / /
[info]    / /  / /_/ // // /_/ // /_/ /
[info]   /_/  / .___//_/ \__,_/ \__, /
[info]       /_/               /____/
[info] 
[info] Version 3.0.6 running Java 17.0.14
[info] 
[info] Play is run entirely by the community. Please consider contributing and/or donating:
[info] https://www.playframework.com/sponsors
[info] 

--- (Running the application, auto-reloading is enabled) ---

INFO  p.c.s.PekkoHttpServer - Listening for HTTP on /[0:0:0:0:0:0:0:0]:9000

(Server started, use Enter to stop and go back to the console...)

While my local server rises and listens to the harbor 9000I open a new terminal and send API by sending a request:

$ curl http://localhost:9000/v1/posts | jq

[
  {
    "id": "1",
    "link": "/v1/posts/1",
    "title": "title 1",
    "body": "blog post 1"
  },
  {
    "id": "2",
    "link": "/v1/posts/2",
    "title": "title 2",
    "body": "blog post 2"
  },
  {
    "id": "3",
    "link": "/v1/posts/3",
    "title": "title 3",
    "body": "blog post 3"
  },
  {
    "id": "4",
    "link": "/v1/posts/4",
    "title": "title 4",
    "body": "blog post 4"
  },
  {
    "id": "5",
    "link": "/v1/posts/5",
    "title": "title 5",
    "body": "blog post 5"
  }
]

Beautiful. This was fast. Then, I want to play with a few requests – buy and create a blog post.

$ curl http://localhost:9000/v1/posts/3 | jq

{
  "id": "3",
  "link": "/v1/posts/3",
  "title": "title 3",
  "body": "blog post 3"
}


$ curl -X POST \
    --header "Content-type:application/json" \
    --data '{ "title": "Just Another Blog Post", "body": "This is my blog post body." }' \
    http://localhost:9000/v1/posts | jq


{
  "id": "999",
  "link": "/v1/posts/999",
  "title": "Just Another Blog Post",
  "body": "This is my blog post body."
}

He is preparing to be deployed to Heroku

Okay, we’re ready to distribute our Scala Play application to Heroku. However, being new for Scala and the game is prone to hit a few speeds of speeds. I want to cover them so that you can stay away from them in your development.

To understand AllowedHostsFilter

When I run my application locally sbt runI have no problem in sending Curl requests and getting answers. But as soon as it happens in the cloud, I use a heroku application url, not localhost. For security, play has a permitted daily activity, which means that you should clearly specify which host computers can access.

I change conf/application.conf To include the block below:

play.filters.hosts {
  allowed = [
    "localhost:9000",
    ${?PLAY_ALLOWED_HOSTS}
  ]
}

I can adjust this way PLAY_ALLOWED_HOSTS For my Heroku application, add the configuration variable to the list of hosts permissible.

Setting a game secret

The game requires an application secret for safety. Used for signing and encryption. You it could be Set the application secretly conf/application.confBut this fixes it to your repo, which is not a good practice. Instead, let’s adjust on working time according to an environmental configuration variable. We add the lines below conf/application.conf:

play.http.secret.key="changeme"
play.http.secret.key=${?PLAY_SECRET}

The first line sets a secret switch with the default, while the second line sets out of our configuration variable, PLAY_SECRETIf it is set.

SBT RUN against the compiled binary running

Finally, I want to talk a little sbt run. But first let’s wrap it back: I thought I would turn my server by making Heroku’s execution. sbt run. When I did this, my dynamum continued to collapse. I didn’t notice how much this mode was intense. It is designed for development, not production.

During this first trial, I opened the log-runtime meters to apply Heroku. My application was using more than 800 meters in memory – too much for small eco Dyno, which is only 512m. If I want to use sbt runI would need Performance-M Dyno. That didn’t seem right.

When I read the play of Play’s distribution documents, I noticed that I should run my application through the compiled duo that made up with another command. sbt stage. This version of my application has been compiled in advance and much more memory is efficient – up to about 180 MB. Assuming sbt stage First I used to work, I had to change my starting command to run the duo.

Why did I go with heroku

I chose Heroku because it runs my Scala application because the installation removes most of its friction. I don’t need to configure a server manually or install addictions. Heroku knows that it is a Scala application (by detecting the presence of Project/Build.properties and Build.sbt files) and applies the Buildpack for Scala. This means running automatically like running resolution and compilation. sbt stage.

For only Scala and someone who tries to play, this type of zero-config distribution is ideal. I can focus on understanding the frame and code base without being removed by the infrastructure. When I separate a few Gotcha once early – AllowedHostsFilterPlay Secret and Startup command – distribution could be quick and repeated.

Well, let’s go!

Distribute the application to Heroku

For distribution, I installed Heroku Cli and I don’t authenticate heroku login. My first step is to create a new Heroku application.

~/project$ heroku apps:create scala-blog-rest-api


Creating ⬢ scala-blog-rest-api... done
https://scala-blog-rest-api-5d26d52bd1e4.herokuapp.com/ | https://git.heroku.com/scala-blog-rest-api.git

Create procfile

Later, Procfile This tells Heroku how to return my application. In my project root, I create the file containing this single line:

web: target/universal/stage/bin/play-scala-rest-api-example -Dhttp.port=${PORT}

This Heroku Start of the Web Process command Negative sbt ruN. Instead, we are carrying out as a binary. target/universal/stage/bin/play-scala-rest-api-example. This is the duo compiled after running sbt stage. Where play-scala-rest-api-example Is the File Name Coming? This set build.sbt. Make sure this name is consistent build.sbt And you Procfile.

In addition, we set to the value of the environmental variable at the connection point at the connection point. PORT. Heroku PORT When the environmental variable starts our application, so we need to inform our application what this connection point is.

Specify the JDK version

Then, I create a single -line file system.propertiesSpecify the JDK version I want to use for my application. Since my local machine uses OpenJDK 17, system.properties The file looks like this:

java.runtime.version=17

Adjust HeroKU app configuration variables

To ensure that everything is fine, we need to set a few configuration variables for our application:

  • PLAY_SECRET: A series of our choices that should be at least 256 bit.
  • PLAY_ALLOWED_HOSTS: HEROKU APPLICATION URL, then AllowedHostsFilter used conf/application.conf.

We set our configuration variables as follows:

~/project$ heroku config:set \
  PLAY_SECRET='ga87Dd*A7$^SFsrpywMWiyyskeEb9&D$hG!ctWxrp^47HCYI' \
  PLAY_ALLOWED_HOSTS='scala-blog-rest-api-5d26d52bd1e4.herokuapp.com'

Setting PLAY_SECRET, PLAY_ALLOWED_HOSTS and restarting ⬢ scala-blog-rest-api... done, v2
PLAY_ALLOWED_HOSTS: scala-blog-rest-api-5d26d52bd1e4.herokuapp.com
PLAY_SECRET:        ga87Dd*A7$^SFsrpywMWiyyskeEb9&D$hG!ctWxrp^47HCYI

Push code to Heroku

Ours Procfile Created and configuration variables set, we are ready to push our code to Heroku.

~/project$ git push heroku main


remote: Resolving deltas: 100% (14/14), done.
remote: Updated 95 paths from 4dc4853
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-24 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Play 2.x - Scala app detected
remote: -----> Installing Azul Zulu OpenJDK 17.0.14
remote: -----> Priming Ivy cache... done
remote: -----> Running: sbt compile stage
remote: -----> Collecting dependency information
remote: -----> Dropping ivy cache from the slug
remote: -----> Dropping sbt boot dir from the slug
remote: -----> Dropping sbt cache dir from the slug
remote: -----> Dropping compilation artifacts from the slug
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing...
remote:        Done: 128.4M
remote: -----> Launching...
remote:        Released v5
remote:        https://scala-blog-rest-api-5d26d52bd1e4.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/scala-blog-rest-api.git
 * [new branch]      main -> main

I just love to create Procfile And set some configuration variables, and then Heroku is interested in the rest for me.

Test time

The only thing to do is to test my Scala play application with a few fold requests:

$ curl https://scala-blog-rest-api-5d26d52bd1e4.herokuapp.com/v1/posts \
       | jq

[
  {
    "id": "1",
    "link": "/v1/posts/1",
    "title": "title 1",
    "body": "blog post 1"
  },
  {
    "id": "2",
    "link": "/v1/posts/2",
    "title": "title 2",
    "body": "blog post 2"
  },
  {
    "id": "3",
    "link": "/v1/posts/3",
    "title": "title 3",
    "body": "blog post 3"
  },
  {
    "id": "4",
    "link": "/v1/posts/4",
    "title": "title 4",
    "body": "blog post 4"
  },
  {
    "id": "5",
    "link": "/v1/posts/5",
    "title": "title 5",
    "body": "blog post 5"
  }
]

$ curl https://scala-blog-rest-api-5d26d52bd1e4.herokuapp.com/v1/posts/4 \
       | jq

{
  "id": "4",
  "link": "/v1/posts/4",
  "title": "title 4",
  "body": "blog post 4"
}

$ curl -X POST \
    --header "Content-type:application/json" \
    --data '{"title":"My blog title","body":"this is my blog post"}' \
    https://scala-blog-rest-api-5d26d52bd1e4.herokuapp.com/v1/posts \
    | jq

{
  "id": "999",
  "link": "/v1/posts/999",
  "title": "My blog title",
  "body": "this is my blog post"
}

API server works. The distribution was smooth and simple. And I learned a lot about Scala and played along the way. After all, it was a good day.

Wrap

Tens of years after Java, jumping to Scala – and getting used to create web applications with JavaScript – it wasn’t as shocking as I expected. Scala’s syntax felt short and modern, and working with the case classes and simultaneous controllers reminded me of the pattern I already used in Node.js. There is still a learning curve about how the vehicles and jobs are configured in the game – but in general, he felt accessible, not overwhelming.

The distribution to the cloud had a few Gotchas, especially about how the game arms allow hosts, secrets and memory use in production. But after understanding how these pieces work, it was simple to make the application alive in Heroku. With only a few configuration changes and a suitable start command, the process could be clean and repeated.

For the first time, I couldn’t ask for a smoother experience for Scala construction and distribution. Are you ready to try?

Happy Coding!

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button