Визуализация sqlite в node.js



    Довольно часто на этапе прототипирования (и всегда — в пет-проектах) я не запариваюсь с бэкэндом и поднимаю апи на 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.

    VDSina.ru
    Серверы в Москве и Амстердаме

    Похожие публикации

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

      +2
      есть куча нормального софта для работы с sqlite, просто вы не захотели озадачится поиском
        0
        Меня терзают смутные сомнения, что вы описываете ручное тестирование. Почему вы не используете Jest/Mocha+Sinon?

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое