Довольно часто на этапе прототипирования (и всегда — в пет-проектах) я не запариваюсь с бэкэндом и поднимаю апи на express с sqlite3. Это легко и довольно удобно для несложной логики, а для сложной есть бэкэндеры с их отдельным миром. Единственный геморрой, который долгое время меня преследовал на этапе написания апи — невозможность быстро заглянуть в базу и отследить изменения. Можно дебажить по памяти, конечно, но это как-то странно, когда можно просто вытащить содержимое по запросу и отобразить его на какой-нибудь страничке. Вот только каждый раз заново парсить json и распихивать его в таблицы по лучшим практикам очередного фреймворка мне не хотелось, поэтому я после недолгих поисков нашёл sqljs и набросал на нём простейший визуализатор.

Про sqljs


Это библиотека, позволяющая создавать sqlite базы, читать, писать и вообще строить любой апи на них. Он построен на wasm, поэтому работает медленнее чем тот же sqlite3, и использует emscripten для сборки. По умолчанию создаётся in-memory база, но можно читать из файла и экспортировать файл из памяти. Демо. В конце концов, sqljs не завязан на node или любое другое окружение и спокойно работает в браузере, но мне было проще запустить его из ноды, всё равно ж использую.

Ссылки

Вот сайт, документация и примеры. Есть легаси-версия с asm.js и web worker версия, все дистрибутивы доступны через CDN и на GitHub.

Использование


0. Подключаем библиотеку

CDN:

  <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.4.0/dist/sql-wasm.js" integrity="sha512-8oJoeo0ykAzuJzQFJDnwz9t4Rr+1xue7LFX+kr0NJMpOHH9QJPC563If+sakheUe3QbLwTTgXIGPC6YZTwp7Iw==" crossorigin="anonymous"></script>

npm:

  npm install sqljs

1. Инициализация библиотеки

  // Используя модуль:
  const initSqlJs = require('sql.js');
  // При использовании в браузере:
  var initSqlJs = window.initSqlJs;

2. Загружаем инстанс

  const SQL = await initSqlJs({
    // Асинхронная загрузка бинарника wasm. Разумеется, его можно хранить и у себя
    // В node не требуется
    locateFile: file => `https://sql.js.org/dist/${file}`
  });

3. Создаём базу, выполняем INSERT/SELECT

  var db = new SQL.Database();
  // Также можно использовать new SQL.Database(data), где
  // data это Uint8Array с файлом sqlite

  sqlstr = "CREATE TABLE hello (a int, b char);";
  sqlstr += "INSERT INTO hello VALUES (0, 'hello');"
  sqlstr += "INSERT INTO hello VALUES (1, 'world');"
  db.run(sqlstr);

  var res = db.exec("SELECT * FROM hello");
  /*
  [
    {columns:['a','b'], values:[[0,'hello'],[1,'world']]}
  ]
  */

4. Формируем смешанный запрос и привязываем переменные к значениям ответа

  var stmt = db.prepare("SELECT * FROM hello WHERE a=:aval AND b=:bval");

  var result = stmt.getAsObject({':aval' : 1, ':bval' : 'world'});
  console.log(result); // {a:1, b:'world'}

  // Освобождаем память выражения
  stmt.free();
  // После освобождения использовать выражение нельзя,
  // но бесконтрольное использование приведёт к утечкам памяти

Демо: https://jsfiddle.net/5f3ahx8o/

Визуализация


Из-за разницы в производительности и не особенно нужного in-memory, возможно и стоило изначально брать проверенный sqlite3, но надо было бы притянуть немного бойлерплейта, да и логично было бы пустить отображение через апи, а мне хотелось иметь отдельно работающий молчаливый сервис. Чтобы не плодить сущности я взял express-handlebars для отображения и прокинул в основную вьюху {{{renderedTables}}}.

Базу забирал из той же директории, но можно прописать любой путь.

  var filebuffer = fs.readFileSync('sqlitedb');
  /* ... */
  initSqlJs().then(function(SQL){
    var db = new SQL.Database(filebuffer);

Данные из всех таблиц получаются в две строчки:

  var schema = db.exec('SELECT name, sql FROM sqlite_master WHERE type="table";')[0].values;
  schema.forEach(t => all[t[0]] = db.exec('SELECT * FROM ' + t[0]));

Дальше идёт скучный рендер таблицы и, наконец, отправка её на страницу. Полный код есть в репо.

Заключение


Меня на протяжении всего этого процесса не покидало ощущение, что я изобретаю какой-то тупейший велосипед и всё уже точно написано до меня, обмазано тестами и схемами, но топорного варианта для себя, «запустил и забыл», так и не обнаружил. Ну, тем забавнее будет, если эта штука кому-то окажется полезна. Можно ещё прикрутить хот-релоад и эти долбаные схемы, но зачем их заводить для mock database на пару таблиц, мне неочевидно.



На правах рекламы


Мощные виртуальные серверы с процессорами AMD EPYC для разработчиков. Частота ядра CPU до 3.4 GHz. Максимальная конфигурация позволит оторваться на полную — 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe.

Комментарии (2)