0

I have set up a server in Ktor with a Postgres database in Docker, but figured it would be useful to be able to develop the server locally without rebuilding the docker container each time.

In application.conf I have

// ...

db {
    jdbcUrl = ${DATABASE_URL}
    dbDriver = "org.postgresql.Driver"
    dbDriver = ${?DATABASE_DRIVER}
    dbUser = ${DATABASE_USER}
    dbPassword = ${DATABASE_PASSWORD}
}

and in my DatabaseFactory I have

object DatabaseFactory {

    private val appConfig = HoconApplicationConfig(ConfigFactory.load())
    private val dbUrl = appConfig.property("db.jdbcUrl").getString()
    private val dbDriver = appConfig.property("db.dbDriver").getString()
    private val dbUser = appConfig.property("db.dbUser").getString()
    private val dbPassword = appConfig.property("db.dbPassword").getString()

    fun init() {
        Database.connect(hikari())

        transaction {
            val flyway = Flyway.configure().dataSource(dbUrl, dbUser, dbPassword).load()
            flyway.migrate()
        }
    }

    private fun hikari(): HikariDataSource {
        val config = HikariConfig()
        config.driverClassName = dbDriver
        config.jdbcUrl = dbUrl
        config.username = dbUser
        config.password = dbPassword
        config.maximumPoolSize = 3
        config.isAutoCommit = false
        config.transactionIsolation = "TRANSACTION_REPEATABLE_READ"
        config.validate()
        return HikariDataSource(config)
    }

    suspend fun <T> dbQuery(block: () -> T): T =
        withContext(Dispatchers.IO) {
            transaction { block() }
        }

}

I have edited the Gradle run configuration with the following environment config:

DATABASE_URL=jdbc:h2:mem:default;DATABASE_DRIVER=org.h2.Driver;DATABASE_USER=test;DATABASE_PASSWORD=password

When I run the task I get this error: Could not resolve substitution to a value: ${DATABASE_URL}, but if I set a breakpoint on the first line (private val appConfig) and evaluate System.getenv("DATABASE_URL") it is resolved to the correct value.

My questions are:

  1. Why does this not work?
  2. What is the best (or: a good) setup for developing the server without packing it in a container? Preferably without running the database in another container.
Jørgen
  • 180
  • 9
  • It works in debug mode - not sure why that makes a difference. However the app crashes as soon as I try to interact with the H2 database - possibly H2/Postgres differences? – Jørgen Mar 07 '20 at 16:56

4 Answers4

1

You need to specify:

appConfig.property("ktor.db.jdbcUrl").getString()

Chris Catignani
  • 5,040
  • 16
  • 42
  • 49
  • We need to specify `ktor` first. This is missing on the official doc as well https://ktor.io/docs/jwt.html#validate-payload – Chetan Gaikwad Oct 08 '21 at 15:53
0

I found that setting environment variables for the task in gradle.config.kts works:

tasks {
    "run"(JavaExec::class) {
        environment("DATABASE_URL", "jdbc:postgresql://localhost:5432/test")
        environment("DATABASE_USER", "test")
        environment("DATABASE_PASSWORD", "password")
    }
}

(source: Setting environment variables in build.gradle.kts)

As for why my initial approach only works in debug mode I have no idea. As for question #2 I have a suspicion that H2 and Postgres could have some syntactic differences that will cause trouble. Running the database container in the background works fine for now.

Jørgen
  • 180
  • 9
0

As answered by @leonardo-freitas we need to specify ktor. first before accessing application.conf properties.

environment.config.property("ktor.db.jdbcUrl").getString()

This is missing on the official doc as well https://ktor.io/docs/jwt.html#validate-payload

Chetan Gaikwad
  • 1,268
  • 1
  • 13
  • 26
0

I experienced the same problem. I had to manually restart the IntelliJ (NOT via File). Simply closed the IDE, and then turned it on again. Also, check that your environment variable is permanent.

Hawklike
  • 952
  • 16
  • 23