Hi everyone, I'm very new to backend development, ...
# exposed
y
Hi everyone, I'm very new to backend development, Ktor, and the Exposed framework. I'm working on a project and trying to migrate my database connection from the standard JDBC (with HikariCP) to R2DBC to learn more about reactive programming. Since I couldn't find many direct examples online for this specific setup, I used an AI assistant to help generate the code. Now, I'm not sure if the result is correct or follows best practices. I'd be really grateful if someone with more experience could take a look and give me some feedback. For context, I've posted the 'before' and 'after' versions of my code below for you to check.
Before: Working JDBC + HikariCP Setup
Copy code
Kotlin
// DatabaseFactory.kt
object DatabaseFactory {
    fun init() {
        val database = Database.connect(hikari())
        transaction(database) {
            // Create tables if they don't exist
            SchemaUtils.create(
                UsersTable,
                ChatsTable,
                GeneratedImagesTable,
                ChatMessagesTable
            )
        }
    }
    private fun hikari(): HikariDataSource {
        val config = HikariConfig().apply {
            driverClassName = "org.postgresql.Driver"
            jdbcUrl = System.getenv("DATABASE_URL") ?: "jdbc:<postgresql://localhost:5432/your_db_name>"
            username = System.getenv("DATABASE_USER") ?: "postgres"
            password = System.getenv("DATABASE_PASSWORD") ?: "password"
            maximumPoolSize = 3
            isAutoCommit = false
            transactionIsolation = "TRANSACTION_REPEATABLE_READ"
            validate()
        }
        return HikariDataSource(config)
    }
    suspend fun <T> dbQuery(block: suspend () -> T): T =
        newSuspendedTransaction(<http://Dispatchers.IO|Dispatchers.IO>) { block() }
}
After: My Attempt at an R2DBC Setup
Copy code
Kotlin
// DatabaseFactory.kt
object DatabaseFactory {
    private val database: R2dbcDatabase by lazy {
        val r2dbcUrl = buildR2dbcUrl()
        R2dbcDatabase.connect(
            url = r2dbcUrl,
            user = System.getenv("DATABASE_USER") ?: "postgres",
            password = System.getenv("DATABASE_PASSWORD") ?: "password"
        )
    }

    suspend fun init() {
        // Schema creation in a suspended transaction
        suspendTransaction(<http://Dispatchers.IO|Dispatchers.IO>, db = database) {
            SchemaUtils.create(
                UsersTable,
                ChatsTable,
                GeneratedImagesTable,
                ChatMessagesTable
            )
        }
    }

    private fun buildR2dbcUrl(): String {
        val jdbcUrl = System.getenv("DATABASE_URL") ?: "jdbc:<postgresql://localhost:5432/your_db_name>"
        // Handle different JDBC URL formats
        return if (jdbcUrl.startsWith("jdbc:postgresql://")) {
            jdbcUrl.replace("jdbc:postgresql://", "r2dbc:postgresql://")
        } else {
            // Fallback to default R2DBC URL
            "r2dbc:<postgresql://localhost:5432/your_db_name>"
        }
    }

    suspend fun <T> dbQuery(block: suspend () -> T): T =
        suspendTransaction(<http://Dispatchers.IO|Dispatchers.IO>, db = database) { block() }
}
a
There is a sample in the github repo, which looks pretty similar to what you have, maybe it gives some more insights. I basically struggled with the similar question and found this one (so i cannot give much feedback) https://github.com/JetBrains/Exposed/tree/main/samples/exposed-ktor-r2dbc
1
y
Albrecht, thank you for the help