QoreDB LogoQoreDB
Back to blog
ArchitectureTechnical journal

Why choose Rust for the backend of a database client

A desktop database client isn't just an interface sitting on top of a SQL driver. It's a process that keeps several connection pools open, that receives streaming results from several engines at once, that manipulates sometimes large buffers, that communicates constantly with the…

Raphaël – Creator of QoreDBRaphaël – Creator of QoreDB
6 min readUpdated Jul 18, 2026
Why choose Rust for the backend of a database client

A desktop database client isn't just an interface sitting on top of a SQL driver. It's a process that keeps several connection pools open, that receives streaming results from several engines at once, that manipulates sometimes large buffers, that communicates constantly with the UI, and that must stay responsive when the user clicks Cancel in the middle of a COUNT(*) over a hundred million rows.

This kind of workload is systems code. Writing a frontend in TypeScript and someday calling it an application backend isn't enough. You need a language built for performance under memory constraints, multi-connection concurrency, and native integration with the low-level libraries of database drivers. That's why QoreDB's backend is written in Rust, with Tauri 2 as the desktop runtime.

Memory safety without a garbage collector

A database client spends its life allocating, transforming and freeing result buffers. On a query that returns five million rows, every allocation counts. A garbage-collected language (Go, Java, C#, Node) offers a simple model, but introduces unpredictable GC pauses at the worst moment: while a cursor is streaming, while a server-side sort finishes, while a Parquet export compresses.

Rust removes the problem without a GC. The compiler tracks the ownership (ownership) and the lifetime (lifetime) of every value, and frees the memory as soon as the value goes out of scope. The result: no runtime pause, no memory leak from forgetting to free, no access to an already-freed region. For software that will run open eight hours a day with several active connections, this predictability is decisive.

The other, more subtle effect is that safety invariants surface at compile time. A pool shared between threads must implement Send and Sync, otherwise the code doesn't compile. You don't discover that kind of bug in production on a Monday morning.

Native async suited to database I/O

A database client spends almost all of its time waiting on the network: waiting for the SQL server, waiting for the SSH tunnel, waiting for Mongo's BSON serialization. Rust's async model, based on tokio, is designed for exactly this: thousands of lightweight tasks waiting on I/O, scheduled on a thread pool, without the per-task memory cost of an OS thread.

QoreDB relies directly on this ecosystem. sqlx provides an async pool for PostgreSQL, MySQL and SQLite. The official mongodb driver is natively async. tiberius handles SQL Server via bb8 for pooling. The Redis driver has a tokio-comp mode. QoreDB's DataEngine trait unifies all these drivers behind async methods, which makes it possible to run several queries on several engines in parallel from the same Tauri command, without juggling threads manually.

Rust's async/await model isn't a nice-to-have add-on: it's the model that structures half of the backend ecosystem. Every database driver has a natively async version that is actively maintained, with primitives like streams to handle cursors. For software whose main job is to orchestrate database I/O, this is the expected foundation.

FFI and low-level integration

Some engines don't have a pure-Rust driver. DuckDB, used by QoreDB as its federation engine, exposes a C library. SQLite with its extensions also requires an FFI call. The system keychain on macOS and Windows goes through native APIs. Every SSH tunnel relies on low-level primitives.

Rust has a stable, predictable and bidirectional FFI with C. The duckdb crate with the bundled feature compiles DuckDB from source and exposes it as a normal Rust module. keyring wraps the Security.framework, DPAPI and Secret Service APIs behind a unified API. This closeness to C lets QoreDB use the best available libraries, without wedging a managed runtime between them.

Conversely, a Go or JVM backend forces a choice between costly CGO bindings, pure-language drivers that are often less mature, or a multi-process architecture that complicates error handling.

Measurable performance on typical workloads

A database client's critical tasks fall into four categories: result streaming, serialization to the UI, large-volume export, compression. Rust compiles to optimized native code (LTO, codegen-units=1 in release builds at QoreDB), without VM indirection. For a full scan of several million rows rendered on the fly, the absence of an intermediate layer is felt.

The second gain, less visible, is the allocator. QoreDB embeds mimalloc in release builds, which improves allocation latency under heavy concurrency compared to the system allocator on macOS and Windows. This kind of targeted optimization is possible because Rust exposes low-level choices that most managed runtimes hide.

For Parquet export, conversion via arrow is wired directly onto the result stream. Moving rows from the driver into a RecordBatch and then into the compressed file goes through no unnecessary intermediate serialization.

Integration with Tauri

The choice of Rust is also driven by the choice of desktop runtime. Tauri 2 uses Rust as its host language, and exposes async commands to the React frontend through a typed bridge. Using another language for the business logic would add an extra bridge (RPC to a Node or Go binary), with the classic problems: managing the subprocess lifecycle, double serialization, harder debugging.

With Rust inside Tauri, an invoke('run_query', ...) call on the frontend directly triggers an async Rust function, which can stream the results via Tauri events. No subprocess, no local socket, no exchange format to negotiate. The final binary stays compact, which matters for a desktop application distributed with auto-update.

The accepted learning curve

Rust demands more rigor than a GC language. The borrow checker takes time to tame, and some patterns (self-reference, callbacks with shared state) require extra thought. For a project whose backend is a critical component read and modified over years, this investment pays off: refactorings are safe, regressions are rare, and code written two years ago still compiles today with the same guarantees.

On a database client project, the compiler becomes a review partner: it rejects non-thread-safe connection sharing, it forces you to handle error cases, it prevents you from ignoring a Result. This safety net is especially valuable when several drivers coexist in the same process.

In practice in QoreDB

QoreDB's Cargo workspace is organized into specialized crates: qore-core defines the DataEngine trait and the shared types, qore-drivers implements each engine, qore-sql handles SQL generation and validation, qore-service carries the vault, governance and session context. This split is possible because Cargo natively manages workspaces with internal dependencies, without an external tool.

The result on the user side is a backend that keeps a stable memory footprint even with several active connections and result sets of several million rows open, that responds in under a millisecond on short commands, and that introduces no visible pauses during heavy exports.

Conclusion

Rust for QoreDB is not a trendy engineering choice. It's a choice aligned with the exact nature of the product: long-running desktop software, multi-driver, sensitive to latency and memory, distributed as a native binary. Memory safety without a GC, native async suited to database I/O, FFI with existing C libraries and direct integration with Tauri combine to give a solid, predictable and maintainable foundation. The price to pay is a steeper learning curve, largely offset by the robustness of the result over time.

Newsletter

Stay updated on new releases

Subscribe to get product releases, new drivers notifications, and technical tutorials.

🎁 Bonus: Get our free SQL Performance Cheat Sheet — 9 pages, PostgreSQL / MySQL / SQLite (PDF)!
Share
Why choose Rust for the backend of a database client - Blog - QoreDB