Skip to content
NewFrom Mongoose to SQL6 min read

Bun Native SQL

Under Bun, uql-orm/bun-sql drives Bun’s own SQL client, so you install no pg or mysql2. Entities, queries and migrations stay the same.

Terminal window
bun add uql-orm

BunSqlQuerierPool takes Bun’s SQL.Options and picks the dialect from them:

import { BunSqlQuerierPool } from 'uql-orm/bun-sql';
export const pool = new BunSqlQuerierPool({
url: 'postgres://localhost:5432/app',
});
URL scheme or adapter Dialect UQL emits
postgres://, postgresql:// PostgreSQL
mysql://, mysql2:// MySQL
mariadb:// MariaDB
cockroachdb:// CockroachDB

UQL emits each engine’s own SQL; only parameter binding is adapted for Bun. CockroachDB connects through Bun’s Postgres adapter while UQL keeps emitting CockroachDB SQL.

Each querier reserves a dedicated connection from Bun’s pool and releases it when the querier is released. Inside a unit of work, run raw SQL with querier.all and querier.run. Outside one, pool.sql is Bun’s own client.

This pool does not drive SQLite. Use SqliteQuerierPool, which uses bun:sqlite under Bun with nothing to install, and also streams, loads extensions and prepares statements:

import { SqliteQuerierPool } from 'uql-orm/sqlite';
const pool = new SqliteQuerierPool(':memory:');

Bun’s client has no cursor API, so on Postgres and CockroachDB findManyStream pages rows through a SQL cursor (DECLARE / FETCH). Memory stays flat, and the cursor closes when the loop ends, breaks or throws. A cursor needs a transaction, so a stream outside one opens its own.

On MySQL and MariaDB there is no cursor to reach, so the whole result is fetched first and then yielded row by row. To stream a large table there, use mysql2 or mariadb. See Streaming.