PGlite — полноценный Postgres-сервер на WASM. Работает прямо в браузере и Node.js

Безумные штуки иногда можно найти в интернете. Листая 2024 JavaScript rising stars (https://risingstars.js.org/2024/en#section-all) обнаружил там удивительного зверя - Postgres скомпилированный через emcc в WASM версию, и допиленный до состояния, когда его можно запустить внутри JS-процесса (браузер/Node.js/Bun/etc).

PGlite уже упоминался на Хабре (https://habr.com/ru/companies/postgrespro/articles/828950/), но я решил, что он так крут, что заслуживает отдельной небольшой статьи.

TL;DR: Представьте себе полноценный PostgreSQL, работающий в браузере (или в Node.js, Bun, Deno) без необходимости поднимать отдельный сервер или встраивать Linux-образ. Проект PGlite реализует эту идею - всего лишь 3 МБ (в сжатом виде), и причем, с поддержкой дефолтных популярных расширений, типа pgvector.

Что это такое?

PGlite - это PostgreSQL скомпилированный в WebAssembly и упакованный в простую TypeScript/JavaScript-библиотеку. Его ключевая фишка - отсутствие "линуксового" виртуального окружения, то есть вы не тянете за собой целый образ OS. В результате получаем:

Можно использовать PGlite как обычную in-memory базу (данные хранятся в памяти и пропадают при перезапуске), а можно включить постоянное хранение - IndexedDB в браузере или файловую систему в Node.js/Bun/Deno.

Как это всё вообще работает?

Обычно PostgreSQL работает в многопоточном режиме с помощью процессов: при новом подключении форкается отдельный процесс, чтобы обрабатывать запросы. Но в Emscripten (C->WASM) и в JavaScript у нас нет возможности делать fork().

Однако у Postgres есть single user mode. Он изначально задумывался для рекавери, восстановления БД, но отлично подошёл как раз для PGlite: чтобы не плодить процессы и просто работать в одном потоке. Эту часть и взяли за основу, адаптировав ввод-вывод и окружение под WebAssembly.

В результате PGlite запускается как обычная библиотека, а дальше вы пользуетесь SQL так же, как и в «настоящем» Postgres. Разница лишь в том, что доступен только один коннект и один юзер.

Как пользоваться этим чудом в браузере?

Для начала, надо поставить пакет:

npm install @electric-sql/pglite

или импортировать, если вы в браузере:

import { PGlite } from "https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js";

Далее - классический пример in-memory базы:

import { PGlite } from "@electric-sql/pglite";

const db = new PGlite();
const result = await db.query("SELECT 'Привет Хабр!' AS message;");
console.log(result); 
// -> { rows: [ { message: "Привет Хабр!" } ] }

Чтобы сохранять данные между перезагрузками (в IndexedDB), нужно при инициализации указать путь:

const db = new PGlite("idb://my-pgdata");
// Данные останутся в IndexedDB

Как пользоваться в Node/Bun/Deno?

Тут всё схоже: ставим пакет и подключаем:

npm install @electric-sql/pglite

или (для любителей булочек):

bun install @electric-sql/pglite

или (для любителей секьюрных сред):

deno add npm:@electric-sql/pglite

базовое использование точно такое же, как и в браузере:

import { PGlite } from "@electric-sql/pglite";

const db = new PGlite(); // in-memory
await db.query("SELECT 'Привет, Хабр!' AS message;");

но на сервере у нас есть возможность указать путь к файлу, чтобы сохранять данные:

const db = new PGlite("./path/to/pgdata");

Теперь ваши таблицы и записи будут лежать в локальной файловой системе и не потеряются после перезапуска.

Зачем оно вообще нужно?

Примеры кода

Ниже несколько коротких демок:

// Создаём таблицу
await db.query(`
  CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
  );
`);

// Вставляем данные
await db.query("INSERT INTO users (name) VALUES ('Васян'), ('Лёха')");

// Смотрим, сколько записей
const { rows } = await db.query("SELECT COUNT(*) AS total FROM users");
console.log(rows); 
// -> [ { total: "2" } ]

// А теперь вернём сами записи
const { rows: allUsers } = await db.query("SELECT * FROM users");
console.log(allUsers);
// -> [ { id: 1, name: "Васян" }, { id: 2, name: "Лёха" } ]

И всё это работает прямо в вашем браузере или Node.js. Да, я сказал это уже раз 100, но блин, это же реально просто офигенно :)

Итого

PGlite - это простой и клёвый способ получить всю мощь Postgres в одну строчку кода. Подходит для быстрых экспериментов, учебных задач, легковесных демо и «офлайн-приложений». Если вам нужен «настоящий PostgreSQL», но без виртуальных машин, докеров, и прочих обвесок - это именно оно.

P.S. Сборка Postgres в WebAssembly во многом основана на работе Stas Kelvich из Neon. Заходите в репозиторий, если интересно, как «под капотом» выглядит форк PostgreSQL под WASM.

P.P.S. 2025 год на дворе, как же без рекламы канала? собсна, я тоже сделал тг-канал. там я отвратительно себя веду, много матерюсь, но часто пишу всякое полезное про техно-фаундерство, AI, и прочее: как про мои opensource-либы, так и про то, как я укус за укусом прогрызаю свой путь в этом вашем медиа-пространстве. Подписывайтесь!

@ElKornacio
12.01.2025 19:09 UTC
Первоисточник

Комментарии

@
12.01.2025 14:30 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@ElKornacio
12.01.2025 15:43 UTC
+2

хм, тестил в bun, летало (запросы обрабатывались практически мгновенно, суб-миллисекундно). со sqlite не сравнивал, но я бы не ставил их в один ряд.

всё таки sqlite - это прямо таки решение для работы с данными, полноценная pluggable база данных с локальными файлами.

PGlite - это больше именно способ поиграться локально с постгресом. протестить синтаксис, погонять какие-то проверки, сделать демо-версию приложения с in-memory базой без необходимости адаптировать синтаксис. как полноценную замену SQLite я бы его сейчас не рассматривал (хотя в будущем надеюсь это станет возможным)

12.01.2025 16:59 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
13.01.2025 12:52 UTC
0

Sqlite - это СУБД, решает задачи работы с данными. Postgres - это СУБД, решает те же задачи.

А вот PGlite - это именно "эмулятор" постгреса, возможность запустить Postgres в нестандартной среде, в однопоточном режиме. Postgres никогда не разрабатывался как решение для локальной in-app однопоточной базы данных, все оптимизации, которые есть в Postgres - не только будут в таком режиме бесполезны, но и, скорее всего, вредны.

поэтому мне кажется их некорректно сравнивать - SQLite - это полноценное решение, которое делалось специально как in-app БД.

PGlite создавался для других целей :)

@nin-jin
12.01.2025 16:43 UTC
+3

Я просто оставлю это здесь для тех, кто наслушался Ситника...

PGLite:

code-size: 3.9 MB
init: 2516 ms
insert: 92 ms
select: 83 ms
db-size: 2 MB

$mol_db:

code-size: 2.3 KB
init: 71 ms
insert: 1.25 ms
select: 0.43 ms
db-size: 1 KB

@Djaler
12.01.2025 17:54 UTC
+3

Так люди хотят именно Postgres в браузере, а не какую-то другую поделку, которая даже не про SQL.

12.01.2025 18:06 UTC
+3

Я, как пользователь, не хочу postgres в браузере такой ценой. Да, IndexedDB не про SQL, она не позволяет выстрелить себе в ногу неожиданным fullscan-ом.

@supercat1337
13.01.2025 13:58 UTC
0

mol_db на базе indexdb работает?

13.01.2025 14:59 UTC
0

Тонкая типизированная обёртка

@e_u_g
12.01.2025 20:07 UTC
0

Вопрос: Можно ли загрузить экземпляр изменённой базы из браузера обратно на сервер и там новые данные приобщить к остальным?

@artptr86
13.01.2025 15:01 UTC
0

Очевидно, сравнить две базы каким-то образом и объединить данные

@qertis
13.01.2025 05:56 UTC
0

В 22 версии NodeJS встроили поддержку SQLite работающую через флаг. Использую ее как раз ради тестовых запросов и хранилища в памяти. Но здесь можно использовать и в браузере, поэтому модуль имеет смысл, надо будет попробовать.

@Nik_Otin
13.01.2025 08:17 UTC
0

Не очень понятно накой нужен локальный сервер. Смысл сервера как раз в многопользовательском режиме. Разве что для каких-то промежуточных данных или программ индивидуального пользования.

@ponikrf
13.01.2025 09:19 UTC
+1

Пример у меня был не так давно. Я сливал таблицы 2х разных типов баз, сравнивал их, делал нужные выборки, и выполнял определенные операции. Делать это на чистых объектах - очень сложно. Проще взять локальную базу данных, загнать туда данные и сделать нужные выборки.

Не скажу что это приложение было для индивидуального пользования. Бывает так, что полноценная база ненужна для сервиса, а вот какое то встраиваемое решение для определенной задачи, может пригодится.

Я бы даже больше сказал, что в последнее время я больше прихожу именно к встраиванию базы в приложение.

Так что для меня решение интересное, темболее что с PG я знаком хорошо.

@gmini
13.01.2025 12:38 UTC
0

ну для многопользовательского режима надо туда еще pgbouncer вкрутить - и можно впрод ))

@stgunholy
13.01.2025 08:23 UTC
0

а для тестов подойдет?