TL;DR
Bun.sql is the right driver for Bun-only apps that want zero-dependency Postgres with the fastest possible cold start. postgres.js is the cross-runtime default driver — works on Node, Bun, Deno, edge runtimes, and is the single most-used low-level Postgres library in modern TypeScript. Drizzle sits on top of either of those: it's a query builder + migration tool, not a driver. These tools live at different layers, so the real question isn't "which one" but "which combination". For 2026 the most common answers: Bun + Bun.sql + Drizzle (Bun-first apps), Node + postgres.js + Drizzle (most apps), or postgres.js alone if you want to stay close to SQL.
Quick Verdict
| Bun.sql | postgres.js | Drizzle ORM | |
|---|---|---|---|
| Layer | Driver | Driver | Query builder + ORM-lite |
| Runtime | Bun only | Node, Bun, Deno, edge | Any (uses a driver below it) |
| Native bindings | Yes (Bun core) | No (pure JS) | N/A |
| Tagged-template SQL | Yes | Yes | No (TypeScript builder) |
| Type-safe schema | DIY | DIY | Yes (compile-time) |
| Migrations | DIY | DIY | drizzle-kit |
| Connection pooling | Built-in | Built-in | Inherits from driver |
| Best for | Bun-only apps wanting min deps | Cross-runtime driver | Type-safe schema + queries |
Key Takeaways
- They are not interchangeable. Bun.sql and postgres.js are drivers (they speak the Postgres wire protocol). Drizzle is a query builder (it generates SQL strings that a driver executes).
- Bun.sql ships with Bun. No
npm install. Zero dependencies. Native code paths. Worth choosing if and only if you've committed to Bun. - postgres.js is the safest cross-runtime default. It works everywhere TypeScript runs and is exceptionally fast for a pure-JS driver.
- Drizzle's value is the schema layer. If you want compile-time-checked queries, drizzle-kit migrations, and a cleaner mental model than raw SQL, you adopt Drizzle. The driver underneath is a separate decision.
Architecture: Three Layers, Not Three Tools
For Postgres-in-TypeScript in 2026 you almost always have three layers:
- Driver — speaks the wire protocol. (
pg,postgres.js,Bun.sql,@neondatabase/serverless) - Query layer — composes SQL safely. Optional. (
drizzle-orm,kysely,prisma client, raw template strings) - Schema/migration tool — manages DDL. Optional. (
drizzle-kit,prisma migrate,dbmate)
Bun.sql and postgres.js are layer 1. Drizzle is layers 2 and 3. You pair one of layer 1 with (optionally) one of layer 2/3.
What Each One Actually Is
Bun.sql
Built into the Bun runtime. Tagged-template SQL with parameter binding handled natively. No imports beyond the runtime itself.
import { sql } from "bun";
const users = await sql`SELECT * FROM users WHERE active = ${true} LIMIT 10`;
Pros: zero install, fast, integrated with Bun's native networking. Cons: Bun-only — your code can't run on Node or in a Lambda without Bun support.
postgres.js
The most-recommended pure-JS Postgres driver in modern TypeScript. Tagged-template syntax, lazy connection, prepared statements, bigint handling, JSON support, listen/notify.
import postgres from "postgres";
const sql = postgres(process.env.DATABASE_URL!);
const users = await sql`SELECT * FROM users WHERE active = ${true} LIMIT 10`;
Cross-runtime portability is the real value. Drizzle works on top of it; so do raw queries when you want to stay close to SQL. The de facto reference driver in 2026.
Drizzle ORM
A TypeScript-first query builder with a schema definition that compiles to migrations.
import { drizzle } from "drizzle-orm/postgres-js";
import { eq } from "drizzle-orm";
import postgres from "postgres";
import { users } from "./schema";
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client);
const activeUsers = await db.select().from(users).where(eq(users.active, true)).limit(10);
The schema you define in schema.ts is the single source of truth: it powers query types, drizzle-kit migrations, and (optionally) seed data. For a comparison with full-fat ORMs, see Drizzle ORM v1 vs Prisma 6 vs Kysely.
Decision Map
| If you... | Pick |
|---|---|
| Run Bun and want zero driver dependencies | Bun.sql as the driver |
| Need to run the same code on Node and edge | postgres.js as the driver |
| Want compile-time-checked queries and migrations | Drizzle on top of either driver |
| Prefer raw SQL with tagged templates | postgres.js alone (or Bun.sql alone on Bun) |
| Need a heavier ORM (relations, eager loading sugar) | Prisma 6 — see comparison link above |
| Run on Neon, Supabase serverless, Cloudflare Workers | postgres.js or Neon's serverless driver |
Performance Reality
A single benchmark number for "fastest Postgres driver" is misleading because workload shape dominates. Order-of-magnitude expectations for 2026:
- Bun.sql on Bun: highest raw throughput on small, query-heavy workloads. Native code paths help.
- postgres.js on Bun: very close — pure-JS drivers are no longer dramatically slower.
- postgres.js on Node: still excellent. The pure-JS overhead is small relative to network latency.
- node-postgres (
pg): solid but generally edged out by postgres.js on the same workload.
If you're paying real money for a Bun.sql vs postgres.js choice, benchmark your workload. Most apps are network-latency-bound, not driver-bound. For broader driver context, see pg vs postgres.js vs Neon serverless.
Connection Pooling, Edge & Serverless
- Long-lived servers (traditional Node/Bun): use a connection pool (built into both drivers).
- Serverless / edge (Lambda, Workers, Vercel functions): each invocation is a cold connection. Use a pooler like PgBouncer / pgcat / Supavisor or a serverless-aware driver like Neon's HTTP driver.
- Bun.sql on traditional servers: pooled by default, ergonomic.
- postgres.js on edge: works, but commonly paired with
@neondatabase/serverlessover HTTP for very-short-lived environments.
Who Should Pick What
- Bun-first solo dev or small startup: Bun.sql + Drizzle. Zero driver install, type-safe schema, drizzle-kit migrations.
- Cross-runtime team (Node prod, Bun for scripts, edge for some endpoints): postgres.js + Drizzle. Single driver everywhere.
- Team that hates ORMs and loves SQL: postgres.js (or Bun.sql) alone, with
drizzle-kitonly for migration management if you want. - Existing Prisma shop: don't migrate just for fashion. Prisma 6 is fine. Drizzle's pull is real but the cost is non-trivial — see the drizzle-orm-v1-vs-prisma-6 comparison.
- Heavy serverless app on Neon: pair Neon's serverless driver with Drizzle — postgres.js works too but the HTTP-based driver wins on cold-start latency.
Common Anti-Patterns
- Treating Bun.sql vs postgres.js as a competition with Drizzle. They're at different layers. You pick a driver and a query layer.
- Using
pgin 2026 because "it's the standard". postgres.js has won the modern-driver slot. Stick withpgonly if you have an existing codebase you don't want to migrate. - Adopting Drizzle but writing raw SQL for everything. If you don't use Drizzle's schema and query builder, you've added a dependency for migrations only — fine, but use drizzle-kit narrowly.
- Connecting directly from edge functions without a pooler. You will exhaust connections during traffic spikes. Use a pooler or HTTP driver.
Verdict
For new 2026 TypeScript apps on Postgres: Drizzle on top of postgres.js is the most common right answer. Move the driver to Bun.sql if you've gone all-in on Bun and want one fewer dependency. Drop Drizzle if you genuinely prefer SQL templates and don't want a query builder. The decision isn't "which one" — it's "which layers do I want", and the answers compose.