Estoy muy emocionado porque después de tanto tiempo y gracias al Origin Private File System podremos tener SQLite3 en los navegadores, con todas sus ventajas y todo contenido desde una página web.
Si quieres puedes ver el hola mundo sin Svelte, es decir, con JS puro y aprender cómo usar SQLite3 en el navegador web sin servidores.
Ya había leído eso hace tiempo pero tenías que hacer unas cosas “experimentales”.
El punto es que ando aprendiendo un poco de Svelte con Tailwind haciendo una app de notas (a las que les agregaré encriptación) y me dio curiosidad por ver si SQLite3 ya estaba “portado” totalmente, porque si bien ya existía con WebAssembly no había manera de crear archivos binarios en el navegador web.
Entonces encontré a: https://developer.chrome.com/blog/sqlite-wasm-in-the-browser-backed-by-the-origin-private-file-system/
Y vi que ya era posible. Lo siguiente era hacerlo funcionar con SvelteKit que usa a su vez Vite para todo su entorno.
Te contaré cómo lo hice y de dónde me he guiado.
Lo primero que tenemos que hacer es instalar el paquete con:
npm install @sqlite.org/sqlite-wasm
Ahora vamos a configurar el “servidor” de desarrollo para que agregue unos encabezados y también excluya al paquete de optimizeDeps
. Mi vite.config.js queda así:
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
optimizeDeps: {
exclude: ['@sqlite.org/sqlite-wasm'],
},
plugins: [
sveltekit(),
{
name: 'configure-response-headers',
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
next()
})
},
},
]
});
Lo importante es optimizeDeps
y el configure-response-headers
.
Nota: cuando la aplicación sea compilada para producción será necesario agregar esos encabezados en el lugar donde la vayamos a servir. Aquí lo hacemos de ese modo porque Vite se encarga de hacerlo, pero si después la alojamos en un servidor Apache o similares debemos configurar lo necesario.
Luego hice el worker.js colocado en src/worker.js. Quedó así con mis modificaciones (solo puse el hola e hice que los errores y registros se imprimieran en la consola):
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
console.log("Hola")
const log = (...args) => { console.log(...args) };
const error = (...args) => { console.log(...args) };
const start = function (sqlite3) {
log('Running SQLite3 version', sqlite3.version.libVersion);
let db;
if ('opfs' in sqlite3) {
db = new sqlite3.oo1.OpfsDb('/mydb.sqlite3');
log('OPFS is available, created persisted database at', db.filename);
} else {
db = new sqlite3.oo1.DB('/mydb.sqlite3', 'ct');
log('OPFS is not available, created transient database', db.filename);
}
try {
log('Creating a table...');
db.exec('CREATE TABLE IF NOT EXISTS t(a,b)');
log('Insert some data using exec()...');
for (let i = 20; i <= 25; ++i) {
db.exec({
sql: 'INSERT INTO t(a,b) VALUES (?,?)',
bind: [i, i * 2],
});
}
log('Query data with exec()...');
db.exec({
sql: 'SELECT a FROM t ORDER BY a LIMIT 3',
callback: (row) => {
log(row);
},
});
} finally {
db.close();
}
};
log('Loading and initializing SQLite3 module...');
sqlite3InitModule({
print: log,
printErr: error,
}).then((sqlite3) => {
log('Done initializing. Running demo...');
try {
start(sqlite3);
} catch (err) {
error(err.name, err.message);
}
});
Finalmente en mi componente cargué al Worker.
Aquí quiero dejar claro que, por alguna razón, al hacer un new Worker
, me decía que “Worker
” no está definido (debe ser alguna cuestión de la arquitectura que desconozco porque apenas estoy iniciando) así que lo tuve que cargar con import
dentro del onMount
guiándome de https://medium.com/geekculture/sveltekit-web-worker-8cfc0c86abf6
let syncWorker = null;
const loadWorker = async () => {
const SyncWorker = await import('../../worker?worker');
syncWorker = new SyncWorker.default();
};
En este caso worker
es el nombre del archivo. Los ../
son para referirme a la carpeta padre. Bien podría quedar así:
import('../../worker.js?worker')
Lo importante también es agregar el ?worker
ya que el nombre del archivo puede ir sin la extensión. Y finalmente en el onMount
:
onMount(() => {
loadWorker();
agregarElementoALista();
});
Obviamente faltan muchas cosas a partir de aquí, pero ya es el “hola mundo” de SQLite3 “nativamente” en la web, usando todo lo existente de esa librería. Podemos crear tablas, consultar con varias condiciones, crear índices, unir tablas y todo lo que podemos hacer con SQLite3.
A mí me agrada bastante porque al fin tenemos algo robusto para almacenar datos en el navegador, con todo lo que SQL ofrece. Ya no será necesario usar localStorage con JSON.stringify ni librerías como PouchDB (aunque claro, sirven bastante bien y no por eso valen menos).
Espero poder integrar y terminar todo esto para luego publicar un ejemplo con JavaScript puro.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.