Play Framework 2.8, Scala, Slick, MySQL, and sbt (2022)

These are my notes from creating a Play Framework 2.8 project on January 2, 2022, using Scala 2.13, MySQL, and Slick. I share these notes here because I found it hard to find the correct versions of everything that needed to be used, and the correct syntax in the application.conf file. Unfortunately the Play 2.8 documentation is not up to date, so this was much harder than I expected it to be.

Create a Play Framework project

Create a Play Framework 2.8 project from their sbt/Giter8 template:

$ sbt new playframework/play-scala-seed.g8

MySQL database

  • Using MAMP on macOS
  • Username: root
  • Password: root
  • Port: 8889

Create the database and table at the MySQL command prompt:

# STEP 1:
CREATE USER 'play'@'localhost' IDENTIFIED BY 'play';

# STEP 2:
ON play101.*
TO 'play'@'localhost'

# USE the db
use play101;

# CREATE the table(s)
create table stocks (
    id int auto_increment not null,
    symbol varchar(10) not null,
    company varchar(32) not null,
    primary key (id),
    constraint unique index idx_stock_unique (symbol)

# INSERT some data
INSERT INTO stocks (symbol, company) VALUES ('AAPL', 'Apple');
INSERT INTO stocks (symbol, company) VALUES ('GOOG', 'Google');

That’s not a very practical table, but it’s good enough for a simple Play/MySQL example.


My build.sbt file. Note that these versions seem to be correct with Play 2.8 on January 2, 2022:

name         := "play-gcp-101"
organization := "com.example"
version      := "0.1.0"
scalaVersion := "2.13.7"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

libraryDependencies ++= Seq(
    "" %% "scalatestplus-play" % "5.0.0" % Test,
    "mysql" % "mysql-connector-java" % "8.0.27",
    "" %% "play-slick" % "5.0.0",
    "" %% "play-slick-evolutions" % "5.0.0" % Test


The project/plugins.sbt file:

addSbtPlugin("" % "sbt-plugin" % "2.8.11")
addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.13.1")


It took a ridiculously long time to find the correct Play 2.8 conf/application.conf syntax/incantation:

# play.evolutions.db.default.enabled = true
slick.dbs.default.profile = "slick.jdbc.MySQLProfile$"
slick.dbs.default.db.dataSourceClass = "slick.jdbc.DatabaseUrlDataSource"
slick.dbs.default.driver= "slick.driver.MySQLDriver$""jdbc:mysql://"

Note that I use a short timeout value so things fail fast while developing. Play was hanging up for 30 seconds failing to connect to the database for the first 30-60 minutes of dev time, so I found that setting. One thing that might have helped is if I had added this setting to conf/logback.xml earlier:

<!-- this can help to understand with database, connections, mysql, and hikari errors -->
<logger name="com.zaxxer.hikari" level="DEBUG" />


Add this to the bottom of the Play conf/routes file:

GET     /stocks    controllers.StocksController.list


My models/Stock.scala file:

package models

case class Stock (
    val id: Long,
    val symbol: String,
    val company: String


My dao/StockDao.scala file:

package dao

//TODO: clean up these imports
import scala.concurrent.{ ExecutionContext, Future }
import javax.inject.Inject

import models.Stock
import play.api.db.slick.DatabaseConfigProvider
import play.api.db.slick.HasDatabaseConfigProvider
import slick.jdbc.JdbcProfile

class StockDAO @Inject()(
    protected val dbConfigProvider: DatabaseConfigProvider
    implicit executionContext: ExecutionContext
) extends HasDatabaseConfigProvider[JdbcProfile] {

    import profile.api._

    private val Stocks = TableQuery[StocksTable]

    def all(): Future[Seq[Stock]] =

    // SLICK database stuff
    // this first "stocks" string refers to my database table name
    private class StocksTable(tag: Tag) extends Table[Stock](tag, "stocks") {
        def id      = column[Long](  "ID", O.PrimaryKey)
        def symbol  = column[String]("SYMBOL")
        def company = column[String]("COMPANY")
        def *       = (id, symbol, company) <> (Stock.tupled, Stock.unapply)


My controllers/StocksController.scala class:

package controllers

import javax.inject.Inject

// TODO: clean these up.
// SEE:
import play.api._
import play.api.mvc._
import play.api.mvc.{ AbstractController, ControllerComponents }

import scala.concurrent.ExecutionContext

import models.Stock
import dao.StockDAO
import views._

class StocksController @Inject() (
    stockDao: StockDAO,
    controllerComponents: ControllerComponents
    implicit executionContext: ExecutionContext
) extends AbstractController(controllerComponents) {

    // def list = Action {
    //     Ok(html.stock.list(StockDao.selectAll()))
    // }

    // TODO find a better way to do this
    def list = Action.async {
        stockDao.all().map { case (stocks) => Ok(views.html.list(stocks)) }



My views/list.scala.html file:

@(stocks: Seq[Stock])

@main("Stocks") {
    <h1>You have @stocks.size Stock(s)</h1>

   { stock =>


Running and packaging

To run a Play 2.8 app:

$ sbt
sbt> run

# OR:

sbt> ~run

To package a Play 2.8 app:

sbt> show dist
[info] Your package is ready in target/universal/
[info] target/universal/

Per the docs, “To run the application, unzip the file on the target server, and then run the script in the bin directory.”

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller


If you ever need to run Play Framework 2.8 with Scala 2.13, MySQL, and Slick, I hope these notes are helpful.