After a few weeks of using a database client, you accumulate useful queries. The SELECT that joins three tables to pull out the top customers, the maintenance UPDATE you rerun every quarter, the debugging query you retype from memory every single time. Without a library, these queries end up in text files, Markdown notes, or even in the terminal history. QoreDB includes a dedicated library to keep them within reach, organized and accessible from the SQL editor.
QoreDB's Query Library is deliberately simple: folders, items, tags, a favorites toggle, and JSON export/import. No cloud sync, no real-time sharing, no version history. Its scope fits in a few hundred lines of TypeScript, and this frugality is a design choice.
A minimal data model
The structure comes down to two entities. A QueryFolder carries a name, an id, and timestamps. A QueryLibraryItem contains a title, the query text, an optional folder, a list of normalized tags, a favorite flag, and optionally the originating driver and database to pin down the context. That is all. No variable system, no typed parameters, no internal versioning. A query is a piece of text with classification metadata.
Tags are normalized to lowercase, deduplicated, and capped at twelve per item. This normalization avoids the usual drift of free-form tag systems where Reporting, reporting, and reporting silently coexist. The library also enforces a global cap: 300 items and 100 folders. Beyond that, the application refuses to add more so it stays fast to browse and exportable as a single file.
Why localStorage as the source of truth
The primary storage is localStorage. On each read, the library deserializes a JSON key; on each write, it rewrites the entire object. This approach eliminates any layer of shared state between components: there is no global Redux or Zustand store for the Query Library, just function calls that read and write a key.
The choice is explained by the nature of the content. A personal library of a few hundred items amounts to a few tens of kilobytes. The deserialization cost is negligible, the risk of corruption is bounded by a silent fallback to an empty state, and the absence of shared state avoids any desynchronization between the SQL editor, the library modal, and the global search. When a component needs the up-to-date list, it calls listItems() and receives the current snapshot.
The two-tier setup: localStorage and workspace file
QoreDB can work in two modes: with the default configuration, or with a workspace file that materializes the entire working context into a portable folder. When a workspace file is active, the library additionally triggers a debounced sync to .qoredb/queries/library.json via the Tauri command ws_save_query_library. The 1-second delay avoids rewriting the file on every keystroke during a rapid edit.
localStorage remains the source of truth during the session, and the disk file is the persistent image. When a workspace file is opened, the content of library.json is reloaded into localStorage. This separation reflects a practical reality: localStorage is synchronous, immediate, perfect for the UI; the filesystem is slower but necessary as soon as you want to version the library in Git or carry it from one machine to another.
JSON export and import for manual sharing
Sharing between developers or between machines goes through an explicit JSON export. The format is versioned from the very first iteration: QueryLibraryExportV1 contains the version number, the export timestamp, and the two collections, folders and items. This simple versioning opens the door to future migrations without locking down the current format.
The export offers a redaction option that runs each query through redactQuery, the same function that cleans the internal logs. Suspicious literals (tokens, emails, connection strings) are replaced by placeholders. This option is enabled by default in the UI: accidentally sharing a query that contains a plaintext password is an easy mistake to make, and redacting at export time reduces the risk.
The import is tolerant. Folders that are already present (compared by case-insensitive name) are merged, not duplicated. Items keep their association to a folder through a mapping rebuilt on the fly. Invalid items or folders are silently ignored. This robustness comes from the sharing context: a file received by email or through a Git repository must not break the local library if it contains a malformed entry.
Everyday use
From the SQL editor, an icon opens the library modal. The filterable list offers a text search (over the title and the query content), a filter by folder, a filter by exact tag, and a favorites toggle. Selecting an item loads the query into the current editor, ready to run. The Global Search (Cmd+K) also indexes the library items, which lets you get back to a query without opening the modal.
Saving a query from the editor opens a mini-form that prefills the driver and the current database name. These two fields are optional but useful for future auto-completion: the same query will rarely run against both Postgres and MongoDB, and knowing that it was written for MySQL helps to classify it.
For team sharing, the common idiom is to commit the workspace file to an internal Git repository. .qoredb/queries/library.json becomes a resource that is diffable, reviewable, and historized. Merge conflicts are resolved by hand like any JSON file, with no magical merge logic to figure out.
A library that stays in its lane
The role of the Query Library is to help you find a query again, not to become a knowledge management system. Anything beyond that intent stays out of scope: no parameterized templates, no scheduling, no real-time sharing. The result is a predictable, fast component that aligns with the local-first stance of the rest of QoreDB. The data lives on the user's machine, sharing goes through explicit files, and the library keeps working with no network, no account, no backend.
Stay updated on new releases
Subscribe to get product releases, new drivers notifications, and technical tutorials.

