Did someone ever used a Cloudflare R2 bucket from ...
# javascript
s
Did someone ever used a Cloudflare R2 bucket from a Cloudflare worker in Kotlin/JS? I wonder how that is defined.
@Carter I remember you being a Cloudflare expert. Can you tell me how to access R2, KV, D1 and co from the env variable? Do you have anything open source where I can take a look?
Cloudflare is working on bringing Rust and Python to workers, but they couldn’t care less for Kotlin.
c
I got it working but never made it open source. You’ll have to create a series of external declarations. This would give you an entrypoint to D1.
Copy code
// <https://github.com/cloudflare/workerd/blob/3c85053b83a5200dffc5be5ae26cbe7577cd5ea5/types/defines/d1.d.ts#L26>
external interface D1Database {
    fun prepare(sql: String): D1PreparedStatement

    /**
     * <https://developers.cloudflare.com/d1/how-to/query-databases/#dbbatch>
     *
     * Executes an array of prepared statements in a transaction.
     *
     * @return a promise that resolves to an array of statement results.
     */
    fun batch(statements: Array<D1PreparedStatement>): Promise<Array<D1Result>>
}

object D1DatabaseFactory {
    fun new(
        databaseName: String,
        env: dynamic
    ): D1Database {
        require(env[databaseName] != null) { "Database $databaseName not found in env" }
        @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
        return env[databaseName] as D1Database
    }
}
I found working with the external declarations a pain in terms of having the code look and act like Kotlin, so I ended up writing Kotlin objects to wrap the external declarations so I could hide some of the painful parts. I smurf-named the external declarations as D1* e.g D1Database, then just more basic names like Database for my Kotlin classes around the D1 objects.
Copy code
class Database internal constructor(
    private val externalDatabase: D1Database
) {
    fun prepare(sql: String) = PreparedStatement(externalDatabase.prepare(sql))

    fun prepareAndBind(statement: Statement): PreparedStatement {
        val preparedStatement = prepare(statement.sql)
        return if (statement.args.isEmpty()) {
            preparedStatement
        } else {
            preparedStatement.bind(statement.args)
        }
    }

    fun batch(statements: Array<PreparedStatement>) =
        externalDatabase
            .batch(statements.mapToArray<PreparedStatement, D1PreparedStatement> { it.externalPreparedStatement })
            .then { externalQueryResults -> externalQueryResults.mapToArray<D1Result, QueryResult> { QueryResult.new(it) } }
}

object DatabaseFactory {
    fun new(
        databaseName: String,
        env: dynamic
    ): Database = Database(D1DatabaseFactory.new(databaseName, env))
}
This was all before the JS Plain Object gradle plugin, so that might make some of this less bad.
thank you color 1
s
Thank you 🙏🏻 I feel like it’s a project of its own to write down all these definition files. Reminds me on kotlin-wrappers Maybe that’s really an idea for a library to craft such a complete set of cloudflare-wrappers. Cloudflare will not do it - they focus on running native Python on workers.
c
My plan was to make those a pull request to the worker sample project after they accepted my other PR to modernize it, but since that stalled out I never made the other PR
But yes, I agree this could be its own project.
s
I joined their Discord. Maybe there is something I can do for Kotlin. Will be hard after the product owner of workers said Kotlin isn’t a priority tho… I sent them another PR with just minimal changes. Maybe they are just afraid of those big PRs.