No description
  • Svelte 72.8%
  • Rust 20.5%
  • Nix 3.8%
  • JavaScript 1.5%
  • HTML 0.8%
  • Other 0.6%
Find a file
2026-07-03 04:35:27 +02:00
.vscode Tauri Surrealdb starter init 2026-04-30 20:51:41 +02:00
scripts Bump dependencies 2026-07-03 04:35:27 +02:00
src Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
src-tauri Bump dependencies 2026-07-03 04:35:27 +02:00
static Tauri Surrealdb starter init 2026-04-30 20:51:41 +02:00
surrealdb-js-sdk@5ce0b84630 Bump dependencies 2026-07-03 04:35:27 +02:00
.envrc Tauri Surrealdb starter init 2026-04-30 20:51:41 +02:00
.gitignore Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
.gitmodules Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
bun.lock Bump dependencies 2026-07-03 04:35:27 +02:00
flake-profile Bump dependencies 2026-07-03 04:35:27 +02:00
flake.lock Bump dependencies 2026-07-03 04:35:27 +02:00
flake.nix Tauri Surrealdb starter init 2026-04-30 20:51:41 +02:00
package-lock.json Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
package.json Bump dependencies 2026-07-03 04:35:27 +02:00
README.md Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
svelte.config.js Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00
tsconfig.json Tauri Surrealdb starter init 2026-04-30 20:51:41 +02:00
vite.config.js Full rewrite using RpcConnection on tauri Channels 2026-05-29 16:42:52 +02:00

Tauri SurrealDB Starter

A cross-platform starter template for building desktop and mobile applications with Tauri and an embedded SurrealDB instance with on-device persistence. The database runs entirely inside the Tauri process — no external server, sidecar, or background processes required — and writes to the device filesystem.

Platform status: tested on Linux and Android.
macOS, iOS, and Windows should in theory also work, but are not yet tested. Please submit any bugs or issues that you find.

What's included

Layer Technology
Frontend SvelteKit + TypeScript
App shell Tauri 2
Database SurrealDB 3 (embedded via kv-surrealkv)
Rust bridge surreal_tauri_bridge crate — sessions, transactions, live streams
JS SDK 0Tick/surrealdb.js-tauri (tauri branch) with a tauri:// engine

The JavaScript SDK is vendored as a git submodule. The starter consumes it through Bun workspaces (surrealdb: workspace:*), so the app imports surrealdb normally while the source lives in surrealdb-js-sdk/.

The goal is to give you a fully working foundation for applications that need the complete SurrealDB feature set (including the experimental File Buckets), with persistence on the device and cross-platform support.

Prerequisites

OR

  • use the provided nix flake

Quick start

# If using nix
nix develop

# 1. Clone this repo with the SDK submodule
git clone --recurse-submodules https://github.com/0Tick/tauri-surrealdb-starter.git
cd tauri-surrealdb-starter

# If you already cloned without submodules:
# git submodule update --init --recursive

# 2. Build the SDK (required on first checkout or when sdk was modified or updated)
./scripts/build-sdk.sh

# 3. Install app dependencies
bun install

# 4. Verify the frontend compiles
bun run check

# 5. Verify the Rust crates compile
cd src-tauri && cargo check && cd ..

# 6. Launch in development mode
bun run tauri dev

The app opens with a built-in test suite that exercises the full SurrealDB feature set: CRUD, relations, transactions, and LIVE subscriptions.

Using the SurrealDB client in your code

This starter follows the same connection flow as the official SurrealDB JavaScript SDK guide (new Surreal() + connect() + signin()/use()).

Create and connect a client

import { Surreal } from 'surrealdb';

const db = new Surreal();

// Connect to the embedded datastore via the Tauri transport
await db.connect('tauri://embedded', {
  reconnect: false,
});

// Optional auth (same pattern as websocket usage)
// await db.signin({ username: 'root', password: 'root' });

await db.use({
  namespace: 'app',
  database: 'app',
});

Select a namespace and database, then query

await db.use({ namespace: 'myapp', database: 'main' });

const users = await db.query('SELECT * FROM user WHERE active = true');

type User = { id: RecordId; name: string; email: string };
const [result] = await db.query<[User[]]>('SELECT * FROM user LIMIT 10');

Create, update, and delete records

import { RecordId } from 'surrealdb';

const post = await db.create('post', { title: 'Hello world', draft: true });
await db.create(new RecordId('post', 'my-slug'), { title: 'Fixed ID post' });
await db.merge(new RecordId('post', 'my-slug'), { draft: false });
await db.delete(new RecordId('post', 'my-slug'));

File Buckets (experimental)

SurrealDB file buckets are currently experimental in SurrealDB 3. Enable them by setting the file bucket feature flag in the Tauri Cargo.toml:

surreal_tauri_bridge = { path = "crates/surreal_tauri_bridge", features = ["file-buckets"] }

A files folder is created under $APPDATA/surrealdb/files and added to the allowlist. From the frontend you can read that allowlist, define a bucket, and store files.

import { FileRef } from 'surrealdb';

const allowlist = await db.getBucketFolderAllowlist();
const bucketFolder = allowlist[0];

if (!bucketFolder) {
  throw new Error('No allowlisted bucket folder configured');
}

const escapedBucketFolder = bucketFolder.replace(/\\/g, '\\\\').replace(/'/g, "\\'");

await db.query(`DEFINE BUCKET IF NOT EXISTS uploads BACKEND 'folder' PATH '${escapedBucketFolder}';`);
await db.query('f"uploads:/hello.txt".put("Hello from SurrealDB buckets");');

const [text] = await db.query<[string]>('RETURN <string>f"uploads:/hello.txt".get();');

await db.create('asset').content({
  name: 'welcome-text',
  file: new FileRef('uploads', '/hello.txt'),
});

See the DEFINE BUCKET docs for backend and capability details.

Transactions

import { RecordId } from 'surrealdb';

const txn = await db.beginTransaction();

try {
  const alice = await txn.select<{ balance: number }>(new RecordId('account', 'alice'));
  const bob = await txn.select<{ balance: number }>(new RecordId('account', 'bob'));

  if (!alice || !bob) {
    throw new Error('Account not found');
  }

  if (alice.balance < 100) {
    throw new Error('Insufficient funds');
  }

  await txn.update(new RecordId('account', 'alice')).merge({ balance: alice.balance - 100 });
  await txn.update(new RecordId('account', 'bob')).merge({ balance: bob.balance + 100 });

  await txn.commit();
} catch (error) {
  await txn.cancel();
  throw error;
}

LIVE queries

import { type LiveSubscription } from 'surrealdb';

const subscription: LiveSubscription = await db.live('post', (action, record) => {
  console.log(action, record); // 'CREATE' | 'UPDATE' | 'DELETE', typed record
});

subscription.kill();

Closing the connection

await db.close();

Project structure

.gitmodules                      Submodule config for the SDK fork
src/                             SvelteKit frontend and built-in test console
src-tauri/
  crates/
    surreal_tauri_bridge/        Embedded SurrealDB, sessions, transactions,
                                 live notifications, and Tauri IPC commands
  src/                           Tauri app entry (`lib.rs`, `main.rs`)

surrealdb-js-sdk/                Git submodule → 0Tick/surrealdb.js-tauri (`tauri` branch)
  packages/
    sdk/                         `surrealdb` npm package consumed by this app
    sqon/                        `@surrealdb/sqon` value types and codecs

package.json                     Bun workspaces + `surrealdb: workspace:*`

At install time, Bun links node_modules/surrealdb to surrealdb-js-sdk/packages/sdk.

SDK submodule

Setting Value
Path surrealdb-js-sdk/
Remote https://github.com/0Tick/surrealdb.js-tauri.git
Branch tauri
Upstream base surrealdb/surrealdb.js

The tauri branch contains the embedded transport and related API wiring. The fork's upstream remote points at the official surrealdb.js repository for merging new releases.

Updating the SurrealDB SDK

Pull the latest tauri branch

From the repo root:

git submodule update --init --remote surrealdb-js-sdk
./scripts/build-sdk.sh
bun install
bun run check
git add surrealdb-js-sdk

Or from inside the submodule:

cd surrealdb-js-sdk
git pull origin tauri
cd ..
./scripts/build-sdk.sh
bun install
git add surrealdb-js-sdk

git submodule update --remote follows the branch configured in .gitmodules (branch = tauri).

Merge upstream surrealdb.js into the fork

To bring official SDK changes onto the Tauri branch:

cd surrealdb-js-sdk
git fetch upstream
git merge upstream/main   # or rebase, as you prefer
# resolve conflicts, rebuild, test
git push origin tauri
cd ..
git submodule update --init --remote surrealdb-js-sdk
git add surrealdb-js-sdk

Tauri-specific files on the tauri branch

Path Purpose
packages/sdk/src/engine/tauri.ts Tauri invoke + channel RPC engine
packages/sdk/src/engine/index.ts Registers the tauri: engine in createRemoteEngines()
packages/sdk/src/controller/index.ts getBucketFolderAllowlist() controller hook
packages/sdk/src/api/surreal.ts Public getBucketFolderAllowlist() API
packages/sdk/build.ts Marks @tauri-apps/api/core as an external bundle dependency
packages/sdk/package.json Dev + optional peer dependency on @tauri-apps/api (needed to build declarations)
packages/sqon/src/value/uuid.ts Guards SharedArrayBuffer usage for Tauri webviews

After changing SDK source, always rebuild before running the app:

./scripts/build-sdk.sh

Restart the dev server after rebuilding (bun run tauri dev).

Notes

  • Connect with tauri://embedded (or any tauri://… URL); the host and path are ignored.
  • @tauri-apps/api must be installed in the app (listed in root package.json).
  • On a fresh clone, build the SDK before bun run dev or bun run tauri dev — the submodule ships source, not prebuilt dist/ artifacts.

Debug logging

Enable verbose transport logs in the webview console:

// Runtime (current session only)
window.__SURREAL_TAURI_DEBUG = true;

// Persistent across reloads
localStorage.setItem('surreal.tauri.debug', '1');

// Disable persistent logging
localStorage.removeItem('surreal.tauri.debug');