agentsclimarketplace

Sqldelight

Skill almasumdev/awesome-kotlin-multiplatform-agent-skills/.github/skills/data_and_networking/sqldelight

Curated agent skills, conventions, and workflows for building Kotlin Multiplatform (KMP) apps with AI coding agents.

Install
npx -y skills add almasumdev/awesome-kotlin-multiplatform-agent-skills --skill sqldelight

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Using SQLDelight 2 for typed SQL in KMP — schema, queries, migrations, coroutines-extensions, and per-platform drivers. Use for any multiplatform local database.

SKILL.md

5.9 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it

SQLDelight

Instructions

SQLDelight turns .sq files into generated Kotlin APIs. Unlike Room (Android-only), it runs on every KMP target via platform drivers.

1. Plugin & dependencies

// shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.sqldelight)
}

sqldelight {
    databases {
        create("AppDatabase") {
            packageName.set("com.example.db")
            schemaOutputDirectory.set(file("src/commonMain/sqldelight/databases"))
            verifyMigrations.set(true)
        }
    }
}

kotlin.sourceSets {
    commonMain.dependencies {
        implementation(libs.sqldelight.runtime)
        implementation(libs.sqldelight.coroutines.extensions)
    }
    androidMain.dependencies { implementation(libs.sqldelight.android.driver) }
    iosMain.dependencies    { implementation(libs.sqldelight.native.driver) }
    jvmMain.dependencies    { implementation(libs.sqldelight.sqlite.driver) }
    jsMain.dependencies     { implementation(libs.sqldelight.web.driver) }
}

2. Schema & queries in src/commonMain/sqldelight/com/example/db/Article.sq

CREATE TABLE Article (
    id           INTEGER PRIMARY KEY NOT NULL,
    title        TEXT NOT NULL,
    body         TEXT NOT NULL,
    authorId     INTEGER NOT NULL,
    publishedAt  INTEGER NOT NULL,
    FOREIGN KEY(authorId) REFERENCES Author(id)
);

CREATE INDEX article_published_idx ON Article(publishedAt DESC);

selectAll:
SELECT * FROM Article ORDER BY publishedAt DESC;

selectById:
SELECT * FROM Article WHERE id = :id;

selectByAuthor:
SELECT * FROM Article WHERE authorId = :authorId ORDER BY publishedAt DESC LIMIT :limit OFFSET :offset;

upsert:
INSERT OR REPLACE INTO Article(id, title, body, authorId, publishedAt)
VALUES (:id, :title, :body, :authorId, :publishedAt);

deleteOlderThan:
DELETE FROM Article WHERE publishedAt < :cutoff;

SQLDelight generates AppDatabase, ArticleQueries, and a data class Article.

3. Driver factory

// commonMain
expect class DatabaseDriverFactory { fun create(): SqlDriver }
// androidMain
actual class DatabaseDriverFactory(private val context: Context) {
    actual fun create(): SqlDriver =
        AndroidSqliteDriver(AppDatabase.Schema.synchronous(), context, "app.db")
}
// iosMain
actual class DatabaseDriverFactory {
    actual fun create(): SqlDriver =
        NativeSqliteDriver(AppDatabase.Schema.synchronous(), "app.db")
}
// jvmMain
actual class DatabaseDriverFactory(private val dir: File) {
    actual fun create(): SqlDriver {
        val file = File(dir, "app.db")
        val driver = JdbcSqliteDriver("jdbc:sqlite:${file.absolutePath}")
        if (!file.exists()) AppDatabase.Schema.synchronous().create(driver)
        return driver
    }
}

4. Using queries with coroutines

class ArticleDao(db: AppDatabase) {
    private val queries = db.articleQueries

    fun observeAll(): Flow<List<Article>> =
        queries.selectAll().asFlow().mapToList(Dispatchers.IO)

    suspend fun upsertAll(items: List<Article>) = withContext(Dispatchers.IO) {
        queries.transaction {
            items.forEach { queries.upsert(it.id, it.title, it.body, it.authorId, it.publishedAt) }
        }
    }

    suspend fun byId(id: Long): Article? = withContext(Dispatchers.IO) {
        queries.selectById(id).executeAsOneOrNull()
    }
}
  • asFlow().mapToList(Dispatchers.IO) emits a new list whenever any write invalidates the query.
  • Wrap batch writes in transaction { } — without it each upsert is its own transaction (slow on iOS WAL).

5. Migrations

Bump the schema by adding a .sqm file:

-- 1.sqm — from v1 to v2
ALTER TABLE Article ADD COLUMN excerpt TEXT;
  • Each .sqm file is named after the source version (1.sqm ⇒ v1 → v2).
  • verifyMigrations.set(true) asserts that applying migrations produces the same schema as the current .sq files.
  • Generate verification DB snapshots with ./gradlew generateSqlDelightSchema.

6. Type adapters

For non-primitive columns (enums, Instant, JSON):

CREATE TABLE Article (
    id INTEGER PRIMARY KEY NOT NULL,
    status TEXT AS com.example.domain.ArticleStatus NOT NULL,
    publishedAt INTEGER AS kotlinx.datetime.Instant NOT NULL
);
val db = AppDatabase(
    driver = driver,
    ArticleAdapter = Article.Adapter(
        statusAdapter = EnumColumnAdapter(),
        publishedAtAdapter = InstantColumnAdapter,
    ),
)

object InstantColumnAdapter : ColumnAdapter<Instant, Long> {
    override fun decode(databaseValue: Long) = Instant.fromEpochMilliseconds(databaseValue)
    override fun encode(value: Instant) = value.toEpochMilliseconds()
}

7. Testing

@Test
fun insertsAndObservesArticle() = runTest {
    val driver = inMemoryDriver(AppDatabase.Schema)
    val db = AppDatabase(driver)
    val dao = ArticleDao(db)
    dao.upsertAll(listOf(sampleArticle))
    dao.observeAll().test {
        assertEquals(listOf(sampleArticle), awaitItem())
        cancel()
    }
}

expect fun inMemoryDriver(schema: SqlSchema<QueryResult.Value<Unit>>): SqlDriver

Implement inMemoryDriver per platform (AndroidSqliteDriver(..., name = null), NativeSqliteDriver(..., name = ":memory:"), JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)).

Checklist

  • .sq files live in src/commonMain/sqldelight/<package>/… and build without warnings.
  • Every table with WHERE-clauses has an index.
  • Batch writes use transaction { }.
  • Query flows go through mapToList(Dispatchers.IO) — never raw asFlow().
  • Migrations tested via verifyMigrations.set(true).
  • Type adapters exist for enums and Instant columns.
  • In-memory driver available to commonTest.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 327,132. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.