This page collects "recipes" and tips for implementing common application-level features, such as...
Importing and Exporing Databases
A commonly-useful capability is uploading and downloading databases.
Exporting an in-browser Database to the local Filesystem
Downloading is straightforward and easy to implement given just an opened database handle. Getting the raw bytes of the database is the first (and easiest) step:
const byteArray = sqlite3.capi.sqlite3_js_db_export(myDb);
Then we need a good deal more code to export that byte array out of the browser. Here's one approach:
const blob = new Blob([byteArray.buffer],
{type:"application/x-sqlite3"});
const a = document.createElement('a');
document.body.appendChild(a);
a.href = window.URL.createObjectURL(blob);
a.download = (myDb.filename.split('/').pop() || "my.sqlite3");
a.addEventListener('click',function(){
setTimeout(function(){
console.log("Exported (possibly auto-downloaded) database");
window.URL.revokeObjectURL(a.href);
a.remove();
},500);
});
a.click();
Importing a Database into the Browser
Forewarning: this will not work with WAL-mode databases. The WASM environment lacks the shared-memory APIs required by WASM, so the WASM build cannot read WAL-mode databases.
Importing a database into the browser is trickier than exporting
because the client has to be able to store the database somewhere, and
such storage is sqlite3_vfs
-dependent. The first step, getting the
database bytes, is fairly generic, and two approaches are demonstrated below.
First, we'll show how to upload a local database file into the browser. For that we need a UI element with which to fetch the database:
<input type='file' id='load-db'/>
Then we need to listen to events which will transfer uploaded files into our JS code. Ignoring such details as a load progress indicator, that looks something like...
const eUploadDb = document.querySelector('#load-db');
eUploadDb.addEventListener('change',function(){
const f = this.files[0];
if(!f) return;
const r = new FileReader();
r.addEventListener('load', function(){
// this.result is an ArrayBuffer with the file's contents
});
r.readAsArrayBuffer(f);
});
Similarly, a database can be loaded using fetch()
, either from the local
web server or a remote one:
fetch( '...url to the db...' )
.then(res=>res.arrayBuffer)
.then(function(arrayBuffer){
// the database's bytes are in arrayBuffer
});
Once that ArrayBuffer is loaded, we need to decide what to do with it. We have at least two options. Most simply, we can load it into an in-memory database:
// assuming arrayBuffer contains the result of the above operation...
const p = sqlite3.wasm.allocFromTypedArray(arrayBuffer);
const db = new sqlite3.oo1.DB();
const rc = sqlite3.capi.sqlite3_deserialize(
db.pointer, 'main', p, arrayBuffer.byteLength, arrayBuffer.byteLength,
sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE
// Optionally:
// | sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE
);
db.checkRc(rc);
The last argument to sqlite3_deserialize()
is significant here for
reasons discussed in the docs for
sqlite3_deserialize()
.
A fetched database can be written to browser-side storage, but how exactly that is done varies depending on the VFS:
- For the "opfs" VFS, see the OpfsDb docs.
- For the "opfs-sahpool" VFS, see that VFS's docs.
- For the default VFS ("unix" and its variants), see
sqlite3_js_posix_create_file()
. - The
"kvvfs"
VFS docs demonstrate how to import another database into kvvfs usingVACUUM INTO
.
Replace the Library's Logging Routines
In order to help those who support the library gather details from
users reporting problems, it may log a small amount of information to
the JS dev console. It can be made completely silent by swapping out
the logging routines with ones compatible with the console.log()
family of functions. To install those before the library is loaded do:
globalThis.sqlite3ApiConfig = {
// define any or all of these:
warn: ()=>{},
error: ()=>{},
debug: ()=>{},
log: ()=>{}
};
// Then load the library using your preferred approach, then
// delete the temporary config object:
delete globalThis.sqlite3ApiConfig /* automatically done as of 3.46.0 */;
The routines must be interface-compatible with console.log()
but the
library does not have any dependency on what they actually do with
their arguments, so eliding them or sending them to alternate logging
channels are both legal options. They must not throw any exceptions or
the library may misbehave.
They can also be modified after the library is loaded, but they may have already emitted output during the library initialization phase, e.g. to warn if OPFS is not available, so that will be too late to squelch all possible output:
sqlite3.config.log =
sqlite3.config.error =
sqlite3.config.warn =
sqlite3.config.debug = ()=>{};
Note that overridding all of them is not necessary. As of this
writing, the library will emit a warning in some rare cases and an
error in a few cases, but should never emit log()
or debug()
messages except during its own development.