tauri-plugin-sql

repository·v1·Indexed 19 days ago

https://github.com/tauri-apps/tauri-plugin-sql

A Tauri plugin providing a bridge to interface with SQLite, MySQL, and PostgreSQL databases using the sqlx library. It enables database operations from both Rust and JavaScript/TypeScript environments, featuring support for schema migrations, connection pool management via load() and close(), and query execution through select() and execute().

Tokens
3.8K
Snippets
17
Records
20
Agent score
63%

What's inside tauri-plugin-sql

  1. Define and apply database migrations

    v1

    The plugin supports schema evolution through migrations defined in Rust. Migrations are applied automatically when the plugin is initialized against the specified connection string.

    Defining a Migration

    Use the Migration struct. Each migration requires:

    • version: A unique integer.
    • description: A string describing the change.
    • sql: The SQL statement to execute.
    • kind: Either MigrationKind::Up or MigrationKind::Down.

    Registering Migrations

    Use the .add_migrations(connection_string, migrations) method on the tauri_plugin_sql::Builder during plugin initialization.

    Best Practices

    • Version Control: Ensure every migration has a unique version number to maintain correct execution order.
    • Idempotency: Write SQL that is safe to run multiple times without causing errors.
    • Testing: Verify migrations thoroughly to prevent database corruption.
    use tauri_plugin_sql::{Builder, Migration, MigrationKind};
    
    fn main() {
        let migrations = vec![
            Migration {
                version: 1,
                description: "create_initial_tables",
                sql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);",
                kind: MigrationKind::Up,
            }
        ];
    
        tauri::Builder::default()
            .plugin(
                tauri_plugin_sql::Builder::default()
                    .add_migrations("sqlite:mydatabase.db", migrations)
                    .build(),
            )
            .run(tauri::generate_context())
            .expect("error while running tauri application");
    }
  2. Install tauri-plugin-sql

    v1

    To use this plugin, you must have a Rust version of at least 1.80. Installation involves two steps: adding the core plugin to your Rust backend and adding the JavaScript guest bindings to your frontend.

    1. Install Core Plugin (Rust)

    Add the plugin to your src-tauri/Cargo.toml. You must enable a driver feature (sqlite, postgres, or mysql) to use that specific database type.

    2. Install JavaScript Guest Bindings

    Since the plugin is part of a monorepo, use the provided mirrors to install via your package manager:

    pnpm add https://github.com/tauri-apps/tauri-plugin-sql#v1
    # or
    npm add https://github.com/tauri-apps/tauri-plugin-sql#v1
    # or
    yarn add https://github.com/tauri-apps/tauri-plugin-sql#v1
    [dependencies.tauri-plugin-sql]
    git = "https://github.com/tauri-apps/plugins-workspace"
    branch = "v1"
    features = ["sqlite"] # or "postgres", or "mysql"
  3. Use tauri-plugin-sql in JavaScript

    v1

    After registering the plugin in your Rust main.rs, you can interact with databases using the Database class from tauri-plugin-sql-api.

    Connection Strings

    • SQLite: sqlite:test.db (The path is relative to tauri::api::path::BaseDirectory::App).
    • MySQL: mysql://user:pass@host/database
    • Postgres: postgres://postgres:password@localhost/test

    Query Syntax

    The plugin uses sqlx under the hood, so query parameter syntax depends on the driver:

    • sqlite and postgres: Use $# (e.g., $1, $2).
    • mysql: Use ?.
    import Database from "tauri-plugin-sql-api";
    
    // Load the database
    const db = await Database.load("sqlite:test.db");
    
    // Execute a query (SQLite/Postgres syntax)
    await db.execute(
      "INSERT into todos (id, title, status) VALUES ($1, $2, $3)",
      [todos.id, todos.title, todos.status],
    );
    
    // Execute a query (MySQL syntax)
    await db.execute(
      "INSERT into todos (id, title, status) VALUES (?, ?, ?)",
      [todos.id, todos.title, todos.status],
    );
  4. Register the SQL plugin in Rust

    v1

    You must register the core plugin in your Tauri application's entry point (usually src-tauri/src/main.rs) using the tauri_plugin_sql::Builder.

    fn main() {
        tauri::Builder::default()
            .plugin(tauri_plugin_sql::Builder::default().build())
            .run(tauri::generate_context!())
            .expect("error while running tauri application");
    }
  5. Configure the SQL plugin via PluginConfig

    v1

    The PluginConfig allows you to specify a list of databases to be preloaded when the plugin is initialized. Preloading a database automatically handles directory creation (for SQLite), database creation if it doesn't exist, and running any registered migrations.

    Use the preload key in your configuration to provide an array of database connection strings.

    {
      "preload": ["sqlite:my_db.db", "postgres://user:pass@localhost/db"]
    }
  6. Handle SQL plugin errors

    v1

    The plugin returns errors that can be serialized to strings. Common error scenarios include:

    • Sql: Errors originating from the underlying database driver (e.g., syntax errors).
    • Migration: Errors occurring during the application of migrations.
    • DatabaseNotLoaded: Attempting to execute a command against a database that hasn't been loaded via load() or preload.
    • UnsupportedDatatype: Encountering a data type that cannot be processed.
  7. Manage database connections with load() and close()

    v1

    The plugin provides manual control over database connection pools.

    • load(db): Manually initializes a connection pool for the specified db connection string. If migrations are registered for this string, they will be applied.
    • close(db?): Closes connection pools. If a db string is provided, only that pool is closed. If no string is provided, all active connection pools are shut down.
  8. Define and add migrations using the Builder

    v1

    You can define schema migrations in Rust and attach them to specific database connection strings using the Builder.

    Each migration requires a version (i64), a description, the sql statement, and a kind (MigrationKind::Up or MigrationKind::Down).

    Use add_migrations on the Builder to map a database URL to a list of Migration objects.

    use tauri_plugin_sql::{Builder, Migration, MigrationKind};
    
    let builder = Builder::default().add_migrations(
        "sqlite:app.db",
        vec![
            Migration {
                version: 1,
                description: "create table",
                sql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);",
                kind: MigrationKind::Up,
            }
        ],
    );
    
    // Then use builder.build() in your Tauri setup
  9. Initialize a database connection with Database.load()

    v1

    Use the static load method to asynchronously connect to an underlying database. This method returns a Database instance once the connection is established.

    For SQLite, the path must start with sqlite: and is relative to the tauri::api::path::BaseDirectory::App directory.

    const db = await Database.load("sqlite:test.db");
  10. Initialize a database connection with Database.load() or Database.get()

    v1

    To interact with a database, you must first obtain a Database instance. You can use two different initialization patterns:

    1. Database.load(path): An asynchronous static method that connects to the underlying database and returns a Database instance once the connection is established.
    2. Database.get(path): A synchronous static method that returns a Database instance immediately, deferring the actual connection until the first query is executed.

    Sqlite Path Requirements: For SQLite, the path must start with sqlite: and is relative to tauri::api::path::BaseDirectory::App.

    Example:

    // Asynchronous connection
    const db = await Database.load("sqlite:test.db");
    
    // Synchronous instance retrieval (connection deferred)
    const db = Database.get("sqlite:test.db");
    const db = await Database.load("sqlite:test.db");
  11. Retrieve data with Database.select()

    v1

    Use select<T>(query, bindValues?) to run SELECT queries. It returns a Promise<T>, where T is the expected shape of the resulting rows.

    Binding Syntax by Driver:

    • Sqlite & Postgres: Use $1, $2, etc.
    • MySQL: Use ?.

    Example (Sqlite/Postgres):

    const result = await db.select(
      "SELECT * from todos WHERE id = $1", id
    );

    Example (MySQL):

    const result = await db.select(
      "SELECT * from todos WHERE id = ?", id
    );
  12. Execute SQL commands with execute()

    v1

    The execute command is used for running SQL statements that modify data (INSERT, UPDATE, DELETE, CREATE, etc.).

    Parameters:

    • db: The database connection string identifier.
    • query: The SQL string to execute.
    • values: An array of JSON values to bind to the query placeholders.

    Returns: A tuple containing rows_affected (u64) and last_insert_id (the type of which depends on the driver: i64 for SQLite, u64 for others, or 0 for Postgres).

    // Example: Inserting a row
    // SQL: INSERT INTO users (name) VALUES (?)
    const [rowsAffected, lastId] = await execute(
      "sqlite:app.db",
      "INSERT INTO users (name) VALUES (?)",
      ["Alice"]
    );