Offline Sync Public Beta

Jamie BartonJamie Barton
Pekka EnbergPekka Enberg
Cover image for Offline Sync Public Beta

We're excited to announce that Turso Offline Sync is now available in public beta!

Your applications can continue functioning seamlessly, even when disconnected from the internet. Local database operations can proceed normally, with automatic sync occurring once connectivity is restored.

Historically, SQLite has been a database that excels at running local, embedded databases, because the database is just a file. For mobile devices, this means on-device databases.

Turso takes advantage of this with Embedded Replicas — your local embedded databases, on-device or server can now be kept in sync with your Turso Cloud database, and any changes are propagated to all replicas.

Until today, our sync was unidirectional: while you can always read from the database, even if offline, writes happen directly to the Cloud, and are propagated later.

This has two consequences:

  • Writes were always network-slow, since they have to contact the server for every request
  • Writes could not work offline

With today's announcement, the local database will be able to accept writes that are as fast as a file, work offline, and later sync to Turso Cloud.

#Use Cases That Just Got Easier

One of the things Offline Sync unlocks is the ability to create on-device local-first applications (in-browser is planned for the future, with "the Limbo Project"). Local-first architectures allow for fast and responsive applications that are resilient to network failures.

Compared to other local-first applications, Turso's architecture allows for a simpler solution because it always syncs the full database. With Turso's multitenant architecture, you can control which data goes into which database (e.g., per user or per tenant), and then transfer the entire database to the device.

As well as on-device local-first applications, Offline Sync also simplifies many other use cases:

  • Mobile Apps — create truly offline-capable mobile experiences, including Expo-based React Native applications.
  • Point-of-Sale Systems — process transactions regardless of internet connectivity
  • Field Data Collection — gather data in remote locations without worrying about connectivity
  • IoT Applications — Maintain local data storage with periodic cloud sync with Turso Cloud

#What's available in the public beta?

The following features are part of the public beta:

  • Bi-directional sync (push local changes to remote and pull remote changes)
  • Remote write support for embedded replicas
  • WAL sync checkpointing
  • Conflict detection (but resolution is not yet implemented)

#Getting Started

You are now invited to try Turso Offline Sync. The beta currently includes support for TypeScript, and Rust.

Make sure to create a new database in your preferred AWS location:

# Create a group in your preferred AWS location
turso group create --location aws-us-east-1 offline

# Create your offline-capable database
turso db create --group offline offline

#TypeScript

Make sure to install the latest @libsql/client package:

npm install @libsql/client

Then inside your project, make sure to set offline: true to enable offline mode:

import { createClient } from '@libsql/client';

const client = createClient({
  url: 'file:local.db',
  syncUrl: process.env.TURSO_SYNC_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

// Function to save a note that works offline
async function saveNote(content) {
  await client.execute(`
    CREATE TABLE IF NOT EXISTS notes (
      id INTEGER PRIMARY KEY,
      content TEXT,
      created_at TEXT
    )
  `);

  // Add the note - works even without internet
  await client.execute({
    sql: `INSERT INTO notes (content, created_at) VALUES (?, datetime('now'))`,
    args: [content],
  });

  // Try to sync changes with the remote database
  try {
    await client.sync();
    console.log('Note synced to cloud');
  } catch (error) {
    console.log('Note saved locally, will sync later');
  }
}

// Read notes function that works offline
async function getNotes() {
  const result = await client.execute(
    'SELECT * FROM notes ORDER BY created_at DESC',
  );
  return result.rows;
}

#Rust

Make sure to install the latest libsql crate using Cargo:

cargo add libsql

Here's how you can use it in your Rust applications

use libsql::{Builder, Database, ResultSet, params};
use std::env;

async fn setup_db() -> Result<Database, Box<dyn std::error::Error>> {
    let db_path = env::var("LOCAL_DB_PATH").unwrap_or("local.db".to_string());
    let sync_url = env::var("TURSO_SYNC_URL").expect("TURSO_SYNC_URL must be set");
    let auth_token = env::var("TURSO_AUTH_TOKEN").expect("TURSO_AUTH_TOKEN must be set");

    // Create a synced database with offline capabilities
    let db = Builder::new_synced_database(db_path, sync_url, auth_token)
        .build()
        .await?;

    let conn = db.connect()?;
    conn.execute(
        "CREATE TABLE IF NOT EXISTS field_data (
            id INTEGER PRIMARY KEY,
            location TEXT,
            reading REAL,
            notes TEXT,
            timestamp TEXT
        )",
        ()
    ).await?;

    Ok(db)
}

async fn record_data(db: &Database, location: &str, reading: f64, notes: &str) -> Result<(), Box<dyn std::error::Error>> {
    let conn = db.connect()?;

    // Insert data locally - works offline
    conn.execute(
        "INSERT INTO field_data (location, reading, notes, timestamp)
         VALUES (?, ?, ?, datetime('now'))",
        params![location, reading, notes]
    ).await?;

    println!("Data recorded locally");

    // Try to sync with server if possible
    match db.sync().await {
        Ok(_) => println!("Data synchronized with central database"),
        Err(e) => println!("Working offline - data will sync later: {}", e),
    }

    Ok(())
}

#Mobile Apps with Expo

For those building local-first offline apps, Expo works great with Turso. You can get started with expo-sqlite:

bunx create-expo-app -e with-libsql my-libsql-app
import { SQLiteProvider, useSQLiteContext } from 'expo-sqlite';

export default function App() {
  return (
    <SQLiteProvider
      databaseName="local.db"
      onInit={
        // Run some optional migration
      }
      options={{
        libSQLOptions: {
          url: '...',
          authToken: '...',
        },
      }}
    >
      <Main />
    </SQLiteProvider>
  );
}

function Main() {
  const db = useSQLiteContext();

  return <View>{/* Your offline-first Expo app */}</View>;
}

#What's next?

With your support, we're working hard on the following features:

  • Automatic and manual conflict resolution
  • Sync protocol bandwidth optimization
  • Encryption at rest

You can follow the progress over on GitHub.

#Thank you

We want to extend our heartfelt gratitude to the Turso community members who participated in the private beta. Your feedback, bug reports, and feature suggestions have been invaluable in shaping this release.

Turso Offline Sync is currently in beta quality, and not yet recommended for production use. During the beta period, there are no durability guarantees, which means data loss is possible.

Make sure to join us on Discord to stay updated on what's new with the beta, and to provide feedback.

scarf