<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thanh Sơn Nguyễn</title>
    <description>The latest articles on DEV Community by Thanh Sơn Nguyễn (@thanh_snnguyn_02472b8a).</description>
    <link>https://dev.to/thanh_snnguyn_02472b8a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3824028%2F5b7ccb5a-3e00-499f-92eb-3ab65b8878d3.jpg</url>
      <title>DEV Community: Thanh Sơn Nguyễn</title>
      <link>https://dev.to/thanh_snnguyn_02472b8a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thanh_snnguyn_02472b8a"/>
    <language>en</language>
    <item>
      <title>How can I run PostgreSQL in the browser or Node.js using in an ultra-lightweight way, similar to SQLite? PGLite!</title>
      <dc:creator>Thanh Sơn Nguyễn</dc:creator>
      <pubDate>Sun, 15 Mar 2026 13:23:37 +0000</pubDate>
      <link>https://dev.to/thanh_snnguyn_02472b8a/how-can-i-run-postgresql-in-the-browser-or-nodejs-using-in-an-ultra-lightweight-way-similar-to-1ljm</link>
      <guid>https://dev.to/thanh_snnguyn_02472b8a/how-can-i-run-postgresql-in-the-browser-or-nodejs-using-in-an-ultra-lightweight-way-similar-to-1ljm</guid>
      <description>&lt;p&gt;I'm experimenting with building an embedded SQL engine for Node.js and Bun.&lt;/p&gt;

&lt;p&gt;The idea is similar to SQLite, but the engine aims to support a large subset of PostgreSQL syntax such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;joins&lt;/li&gt;
&lt;li&gt;CTEs&lt;/li&gt;
&lt;li&gt;window functions&lt;/li&gt;
&lt;li&gt;transactions&lt;/li&gt;
&lt;li&gt;JSON operators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine runs directly inside the application process and stores data in a local file, without requiring a PostgreSQL server.&lt;/p&gt;

&lt;p&gt;One challenge I'm facing is how to design the SQL parser and execution layer to support PostgreSQL-style queries while still keeping the engine lightweight.&lt;/p&gt;

&lt;p&gt;For example, SQLite has its own grammar and execution model, while PostgreSQL has a much more complex planner.&lt;/p&gt;

&lt;p&gt;My questions are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What are the common approaches used by embedded databases to support PostgreSQL-compatible syntax?&lt;/li&gt;
&lt;li&gt;Are there open-source projects or references for implementing a PostgreSQL-like query planner in a lightweight engine?&lt;/li&gt;
&lt;li&gt;Is it realistic to support a meaningful subset of PostgreSQL features without implementing the full PostgreSQL architecture?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For context, I'm experimenting with an open-source prototype here (I'm the author):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pglite/pglite" rel="noopener noreferrer"&gt;https://github.com/pglite/pglite&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>javascript</category>
      <category>node</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Introducing Postgres Lite: A Pure JS Embedded Engine for Node.js, Bun, and the Browser</title>
      <dc:creator>Thanh Sơn Nguyễn</dc:creator>
      <pubDate>Sat, 14 Mar 2026 13:41:58 +0000</pubDate>
      <link>https://dev.to/thanh_snnguyn_02472b8a/introducing-postgres-lite-a-pure-js-embedded-engine-for-nodejs-bun-and-the-browser-3idp</link>
      <guid>https://dev.to/thanh_snnguyn_02472b8a/introducing-postgres-lite-a-pure-js-embedded-engine-for-nodejs-bun-and-the-browser-3idp</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Introducing Postgres Lite: A Pure JS Embedded Engine for Node.js, Bun, and the Browser
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is arguably the most loved database in the world. But when it comes to local-first development, edge computing, or browser-based apps, we usually reach for SQLite. &lt;/p&gt;

&lt;p&gt;Why? Because running a full Postgres instance in the browser or as a zero-dependency embedded process in Node/Bun has traditionally been hard—requiring heavy WASM binaries or network proxies.&lt;/p&gt;

&lt;p&gt;Today, I'm excited to introduce &lt;strong&gt;Postgres Lite (PGLite)&lt;/strong&gt;: An ultra-lightweight, high-performance, &lt;strong&gt;in-process&lt;/strong&gt; PostgreSQL engine built natively for the JavaScript ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 What is Postgres Lite?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Postgres Lite&lt;/strong&gt; is a custom-built SQL engine that implements the PostgreSQL dialect using pure TypeScript/JavaScript. It’s designed to be a robust &lt;strong&gt;SQLite alternative&lt;/strong&gt; that speaks "Postgres," allowing you to use the same schemas, queries, and logic from your backend directly in the frontend or local environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Emulation:&lt;/strong&gt; No WASM overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Dependency:&lt;/strong&gt; Just pure JS/TS logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Universal:&lt;/strong&gt; Runs in Bun, Node.js, and Modern Browsers (via IndexedDB).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗 The Engineering Behind the Performance
&lt;/h2&gt;

&lt;p&gt;Building a database engine from scratch requires more than just parsing SQL. Postgres Lite implements several advanced database engineering techniques to ensure it can handle &lt;strong&gt;1M+ records&lt;/strong&gt; with ease:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Slotted Page Layout
&lt;/h3&gt;

&lt;p&gt;Unlike simple JSON stores, PGLite organizes data into fixed &lt;strong&gt;4KB pages&lt;/strong&gt; using a slotted-page architecture. This prevents fragmentation and allows for efficient management of variable-length records (like &lt;code&gt;JSONB&lt;/code&gt; or &lt;code&gt;TEXT&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. B-Tree Indexing &amp;amp; O(log n) Lookups
&lt;/h3&gt;

&lt;p&gt;Primary keys and unique constraints are backed by a persistent B-Tree implementation. This ensures that even as your dataset grows to millions of rows, point lookups remain lightning-fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Write-Ahead Logging (WAL) &amp;amp; ACID Compliance
&lt;/h3&gt;

&lt;p&gt;Data integrity is non-negotiable. PGLite implements &lt;strong&gt;WAL&lt;/strong&gt;. Every mutation is logged to a persistent &lt;code&gt;.wal&lt;/code&gt; file before being applied to the main storage. If your process crashes, the engine automatically replays the WAL on the next boot.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Volcano Execution Model
&lt;/h3&gt;

&lt;p&gt;PGLite uses an iterator-based processing model. Rows are "pulled" through the execution plan one by one. This means a &lt;code&gt;SELECT *&lt;/code&gt; on a million-row table uses a constant and minimal memory footprint instead of loading everything into RAM.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. LRU Buffer Pool
&lt;/h3&gt;

&lt;p&gt;A sophisticated &lt;strong&gt;Least-Recently-Used (LRU)&lt;/strong&gt; cache manages memory, keeping frequently accessed pages hot and minimizing physical I/O to the disk or IndexedDB.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚦 Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @pglite/core
&lt;span class="c"&gt;# or&lt;/span&gt;
bun add @pglite/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage in Node.js/Bun
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PGLite&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pglite/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NodeFSAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pglite/core/node-fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PGLite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app.db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NodeFSAdapter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    metadata JSONB
  )
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO users (name, metadata) VALUES ($1, $2)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM users WHERE name = $1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage in the Browser
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PGLite&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pglite/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserFSAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pglite/core/browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PGLite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browser_storage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BrowserFSAdapter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Everything else is the same!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡️ Supported Features
&lt;/h2&gt;

&lt;p&gt;Despite its "Lite" name, the engine supports a vast subset of Postgres syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex Joins:&lt;/strong&gt; &lt;code&gt;INNER&lt;/code&gt;, &lt;code&gt;LEFT&lt;/code&gt;, &lt;code&gt;LATERAL&lt;/code&gt;, and &lt;code&gt;CROSS JOIN&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Querying:&lt;/strong&gt; CTEs (&lt;code&gt;WITH&lt;/code&gt;), &lt;code&gt;UNION&lt;/code&gt;, &lt;code&gt;INTERSECT&lt;/code&gt;, and Subqueries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window Functions:&lt;/strong&gt; &lt;code&gt;ROW_NUMBER()&lt;/code&gt;, &lt;code&gt;RANK()&lt;/code&gt; via &lt;code&gt;OVER (PARTITION BY...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON Power:&lt;/strong&gt; Full support for &lt;code&gt;JSONB&lt;/code&gt; operators (&lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;@&amp;gt;&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upserts:&lt;/strong&gt; &lt;code&gt;ON CONFLICT (col) DO UPDATE SET ...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📊 Performance Benchmark
&lt;/h2&gt;

&lt;p&gt;In our internal tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Point Lookups (PK):&lt;/strong&gt; ~0.2ms - 0.5ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequential Scan:&lt;/strong&gt; 100k rows in &amp;lt; 40ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Usage:&lt;/strong&gt; Constant under heavy query load thanks to the Volcano model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are a database enthusiast or a JS developer looking for a better way to handle local data, check out the project on GitHub!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/pglite/pglite" rel="noopener noreferrer"&gt;https://github.com/pglite/pglite&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more deep dives into systems programming with JavaScript!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  postgres #javascript #typescript #database #bun #webdev #programming
&lt;/h1&gt;

</description>
      <category>database</category>
      <category>javascript</category>
      <category>postgres</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
