Tauri SQLite Database Tutorial - Build a Contacts Manager App | React + Rust
Video: Tauri SQLite Database Tutorial - Build a Contacts Manager App | React + Rust by Taught by Celeste AI - AI Coding Coach
Watch full page →Tauri SQLite Database Tutorial - Build a Contacts Manager App | React + Rust
This tutorial demonstrates how to integrate a SQLite database into a Tauri desktop app using Rust and React. You'll learn to set up SQLite with the tauri-plugin-sql, configure database migrations in Rust, and implement full CRUD operations from a React frontend.
Code
// Rust backend: src-tauri/src/lib.rs
use tauri_plugin_sql::{Migration, MigrationKind, SqlPlugin};
#[tauri::command]
async fn add_contact(db: tauri_plugin_sql::Database, name: String, email: String) -> Result<(), String> {
db.execute("INSERT INTO contacts (name, email) VALUES (?, ?)", &[&name, &email])
.await
.map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
async fn get_contacts(db: tauri_plugin_sql::Database) -> Result, String> {
db.select("SELECT id, name, email FROM contacts", &[])
.await
.map_err(|e| e.to_string())
}
// Define database migrations
fn migrations() -> Vec {
vec![Migration {
version: 1,
description: "Create contacts table",
sql: "CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL);",
kind: MigrationKind::Up,
}]
}
fn main() {
tauri::Builder::default()
.plugin(SqlPlugin::new(migrations()))
.invoke_handler(tauri::generate_handler![add_contact, get_contacts])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
// React frontend: src/App.tsx
import React, { useState, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
interface Contact {
id: number;
name: string;
email: string;
}
export default function App() {
const [contacts, setContacts] = useState([]);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
// Load contacts from SQLite via Tauri backend
async function loadContacts() {
const result = await invoke('get_contacts');
setContacts(result || []);
}
// Add a new contact
async function addContact() {
if (!name || !email) return;
await invoke('add_contact', { name, email });
setName('');
setEmail('');
loadContacts();
}
useEffect(() => {
loadContacts();
}, []);
return (
<div>
<h1>Contacts Manager</h1>
<input placeholder="Name" value={name} onChange={e => setName(e.target.value)} />
<input placeholder="Email" value={email} onChange={e => setEmail(e.target.value)} />
<button onClick={addContact}>Add Contact</button>
<ul>
{contacts.map(c => (
<li key={c.id}>{c.name} - {c.email}</li>
))}
</ul>
</div>
);
}
Key Points
- Use the tauri-plugin-sql crate to add SQLite support and manage migrations in Rust.
- Define database schema changes as migrations to ensure consistent setup across app launches.
- Use parameterized queries with placeholders to securely insert and query data.
- Invoke Rust backend commands from React to perform asynchronous CRUD operations on the database.
- Manage React state to reflect real-time updates from the SQLite database in the UI.