Skip to content

sqlx4k

A coroutine-first SQL toolkit with compile-time query validations for Kotlin Multiplatform. PostgreSQL, MySQL/MariaDB, and SQLite are supported.

sqlx4k

sqlx4k is not an ORM. Instead, it provides a comprehensive toolkit of primitives and utilities to communicate directly with your database. The focus is on giving you control while catching errors early through compile-time query validation, preventing runtime surprises before they happen (see SQL syntax and schema validation).

The library is designed to be extensible, with a growing ecosystem of tools and extensions like PGMQ (PostgreSQL Message Queue), SQLDelight integration, and more.

Quick look

The suggested way to use sqlx4k: map your tables with @Table, declare your queries in a @Repository interface, and let the code generator write the implementation (with the CRUD operations), the row mappers, and validate every query while it builds:

@Table("users")
data class User(
    @Id
    val id: Long = 0, // Generated by the database, and read back on insert.
    val name: String,
    @Column(name = "mail_address")
    val email: String,
)

@Repository
interface UserRepository : ContextCrudRepository<User> {
    @Query("SELECT * FROM users WHERE mail_address = :email")
    context(context: QueryExecutor)
    suspend fun findOneByEmail(email: String): Result<User?>
}

// The transaction returns the value of its block: here, the updated user.
val updated: User = db.transaction {
    val alice = UserRepositoryImpl.insert(User(name = "Alice", email = "alice@example.com")).getOrThrow()
    UserRepositoryImpl.update(alice.copy(name = "Alice Smith")).getOrThrow()
}

with(db) {
    val alice: User? = UserRepositoryImpl.findOneByEmail("alice@example.com").getOrThrow()
}

See Repositories for the details.

Or run your SQL directly with the driver:

val db = PostgreSQL(
    url = "postgresql://localhost:15432/test",
    username = "postgres",
    password = "postgres",
)

db.execute("insert into users(id, name) values (1, 'Alice');").getOrThrow()

db.transaction {
    execute("update users set name = 'Bob' where id = 1;").getOrThrow()
    fetchAll("select * from users;").getOrThrow().forEach { println(it) }
}

Features

  • Supported databases

    PostgreSQL, MySQL, MariaDB, SQLite and SQLCipher (encrypted SQLite).

  • Coroutines everywhere

    Every query API is a suspend function returning a Result, on non-blocking I/O on every platform.

  • Gradle plugin

    One sqlx4k { } block adds the driver, the extensions and the code generator.

  • Code generation

    CRUD and @Repository implementations, row mappers, batch operations, optimistic locking.

  • Compile-time validation

    @Query SQL is checked for syntax, columns, tables and statement kind during the build.

  • In-memory repositories

    Generated, thread-safe in-memory implementations of your repositories for unit tests.

  • Transactions

    Block-scoped transactions, savepoints and transaction propagation through the coroutine context.

  • Migrations

    Run pending migrations and validate the ones already applied.

Supported databases

  • MariaDB
  • MySQL
  • PostgreSQL
  • SQLite
  • SQLCipher (encrypted SQLite, sqlx4k-sqlite-cipher)

sqlx4k runs on the JVM, Android, iOS, macOS, Linux and Windows: see Supported targets.

Articles

Short deep-dive posts covering Kotlin/Native, FFI, and Rust ↔ Kotlin interop used in sqlx4k, published on the author's homepage (under construction):

  • Introduction to the Kotlin Native and FFI: [Part 1], [Part 2]
  • Interoperability between Kotlin and Rust, using FFI: [Part 1], (Part 2 soon)

License

MIT, see LICENSE.