
Enables type-safe SQLite database access with auto-generated code from SQL files, comment-based annotations for code control, and no need for IDE plugins, focusing on SQLite-specific optimizations.
A Kotlin Multiplatform library for type-safe, SQLite database access, inspired by SQLDelight. Unlike other popular frameworks (such as Room) - it is full SQL-first framework. Write your queries in SQL files, get type-safe Kotlin code generated automatically. And no runtime annotations overhead, everything is generated at compile time.
Sync-Ready: SQLiteNow includes a complete synchronization system for multi-device applications, allowing seamless data sync across devices with conflict resolution and offline-first capabilities.
SQLiteNow generates Kotlin code from your SQL files, giving you full control over your queries while maintaining type safety. Unlike SQLDelight, which supports multiple database engines, SQLiteNow is focused exclusively on SQLite, allowing for deeper integration and SQLite-specific optimizations.
Full documentation is available in the https://mobiletoly.github.io/sqlitenow-kmp/ pages.
While we have added includes few sample projects to this repository, but for a very simple end-to-end walkthrough, follow the Mood Tracker tutorial series and browse the accompanying sample project.
-- @@{ annotations }
comments in your SQL.Client-side framework components:
Server-side components:
It is important to mention that you can use SQLiteNow Generator and SQLiteNow Library without using OverSqlite for synchronization. And vice versa - you can use OverSqlite for synchronization of SQLite database with PostgreSQL without using SQLiteNow Generator and SQLiteNow Library.
Even if you don't care about multi-device synchronization, SQLiteNow has few key differences from SQLDelight:
First of all, I wanted to target specifically SQLite in Kotlin Multiplatform environment, it is my platform of choice as of now for mobile development. SQLiteNow built on top of using multiplatform SQLite driver from AndroidX.
Second, since we use comment-based annotations, no plugin is required (like in case of SQLDelight), so you can easily use just a regular SQL files that will be validated by your IDE or other external tools for correctness.
Third, I wanted to have a more flexible and extensible code generation system that can be easily extended and customized. I use hexagonal architecture in my code, but sometimes converting between multiple layers is tiresome, so I wanted to have a way to generate code that will be very close to my domain layer, without sacrificing the ability to write pure SQL queries.
Here is the brief example:
-- @@{ queryResult=PersonWithAddresses }
SELECT p.id,
p.first_name,
p.last_name,
p.email,
p.created_at,
a.address_type,
a.postal_code,
a.country,
a.street,
a.city,
a.state
/* @@{ dynamicField=addresses,
mappingType=collection,
propertyType=List<Address>,
sourceTable=a,
collectionKey=address_id } */
FROM Person p
LEFT JOIN PersonAddress a ON p.id = a.person_id
ORDER BY p.id, a.address_type
LIMIT :limit OFFSET :offset
This will generate PersonWithAddresses data class. This class has addresses: List<Address>
property that contains all home addresses for the person. Another class
PersonQuery.SelectAllWithAddresses.Params will be generated as well with limit and offset
parameters to pass parameters to the query.
And you can define your own adapters to convert between SQLite and your domain types and register them for seamless integration. We provide few built-in adapters as well, such as converting from TEXT to Kotlin's date/time etc.
You can always shape your data even more with mapTo annotation.
.sql filesFull examples is available in the /sample-kmp directory.
SQLiteNow includes a complete synchronization system for building multi-device applications.
Simply annotate your tables with enableSync=true and the sync system handles the rest:
-- Enable sync for this table
-- @@{ enableSync=true }
CREATE TABLE person (
id TEXT PRIMARY KEY NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);Then use the generated sync client in your application:
// Create authenticated HTTP client with JWT token refresh and base URL
val httpClient = HttpClient {
install(Auth) {
bearer {
loadTokens { /* load saved token */ }
refreshTokens { /* refresh when expired */ }
}
}
defaultRequest {
url("https://api.myapp.com")
}
}
// Create sync client
val syncClient = db.newOversqliteClient(
schema = "myapp",
httpClient = httpClient,
resolver = ServerWinsResolver
)
// Bootstrap new device
syncClient.bootstrap(userId = "user123", sourceId = "device456")
// Perform full sync (upload local changes, download remote changes)
val uploadResult = syncClient.uploadOnce()
val downloadResult = syncClient.downloadOnce(limit = 500)The sync system automatically handles:
Full example is available in the /samplesync-kmp directory.
Full documentation is available in the https://mobiletoly.github.io/sqlitenow-kmp/
A Kotlin Multiplatform library for type-safe, SQLite database access, inspired by SQLDelight. Unlike other popular frameworks (such as Room) - it is full SQL-first framework. Write your queries in SQL files, get type-safe Kotlin code generated automatically. And no runtime annotations overhead, everything is generated at compile time.
Sync-Ready: SQLiteNow includes a complete synchronization system for multi-device applications, allowing seamless data sync across devices with conflict resolution and offline-first capabilities.
SQLiteNow generates Kotlin code from your SQL files, giving you full control over your queries while maintaining type safety. Unlike SQLDelight, which supports multiple database engines, SQLiteNow is focused exclusively on SQLite, allowing for deeper integration and SQLite-specific optimizations.
Full documentation is available in the https://mobiletoly.github.io/sqlitenow-kmp/ pages.
While we have added includes few sample projects to this repository, but for a very simple end-to-end walkthrough, follow the Mood Tracker tutorial series and browse the accompanying sample project.
-- @@{ annotations }
comments in your SQL.Client-side framework components:
Server-side components:
It is important to mention that you can use SQLiteNow Generator and SQLiteNow Library without using OverSqlite for synchronization. And vice versa - you can use OverSqlite for synchronization of SQLite database with PostgreSQL without using SQLiteNow Generator and SQLiteNow Library.
Even if you don't care about multi-device synchronization, SQLiteNow has few key differences from SQLDelight:
First of all, I wanted to target specifically SQLite in Kotlin Multiplatform environment, it is my platform of choice as of now for mobile development. SQLiteNow built on top of using multiplatform SQLite driver from AndroidX.
Second, since we use comment-based annotations, no plugin is required (like in case of SQLDelight), so you can easily use just a regular SQL files that will be validated by your IDE or other external tools for correctness.
Third, I wanted to have a more flexible and extensible code generation system that can be easily extended and customized. I use hexagonal architecture in my code, but sometimes converting between multiple layers is tiresome, so I wanted to have a way to generate code that will be very close to my domain layer, without sacrificing the ability to write pure SQL queries.
Here is the brief example:
-- @@{ queryResult=PersonWithAddresses }
SELECT p.id,
p.first_name,
p.last_name,
p.email,
p.created_at,
a.address_type,
a.postal_code,
a.country,
a.street,
a.city,
a.state
/* @@{ dynamicField=addresses,
mappingType=collection,
propertyType=List<Address>,
sourceTable=a,
collectionKey=address_id } */
FROM Person p
LEFT JOIN PersonAddress a ON p.id = a.person_id
ORDER BY p.id, a.address_type
LIMIT :limit OFFSET :offset
This will generate PersonWithAddresses data class. This class has addresses: List<Address>
property that contains all home addresses for the person. Another class
PersonQuery.SelectAllWithAddresses.Params will be generated as well with limit and offset
parameters to pass parameters to the query.
And you can define your own adapters to convert between SQLite and your domain types and register them for seamless integration. We provide few built-in adapters as well, such as converting from TEXT to Kotlin's date/time etc.
You can always shape your data even more with mapTo annotation.
.sql filesFull examples is available in the /sample-kmp directory.
SQLiteNow includes a complete synchronization system for building multi-device applications.
Simply annotate your tables with enableSync=true and the sync system handles the rest:
-- Enable sync for this table
-- @@{ enableSync=true }
CREATE TABLE person (
id TEXT PRIMARY KEY NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT UNIQUE,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);Then use the generated sync client in your application:
// Create authenticated HTTP client with JWT token refresh and base URL
val httpClient = HttpClient {
install(Auth) {
bearer {
loadTokens { /* load saved token */ }
refreshTokens { /* refresh when expired */ }
}
}
defaultRequest {
url("https://api.myapp.com")
}
}
// Create sync client
val syncClient = db.newOversqliteClient(
schema = "myapp",
httpClient = httpClient,
resolver = ServerWinsResolver
)
// Bootstrap new device
syncClient.bootstrap(userId = "user123", sourceId = "device456")
// Perform full sync (upload local changes, download remote changes)
val uploadResult = syncClient.uploadOnce()
val downloadResult = syncClient.downloadOnce(limit = 500)The sync system automatically handles:
Full example is available in the /samplesync-kmp directory.
Full documentation is available in the https://mobiletoly.github.io/sqlitenow-kmp/