Gerson

Gerson

Passionate developer specializing in web development, cloud architecture, and system design.

TypeScriptReactNext.jsPythonFastAPISQLNode.jsAWS

Bun.sql and the Full-Stack Runtime: Do You Still Need a Postgres Client?

Bun ships a built-in Postgres client and a zero-config full-stack dev server. Here is what Bun.sql actually does, how it compares to postgres.js and Drizzle, and when the batteries-included runtime is enough.

Gersonhttps://bun.sh/blog/bun-v1.2
Server racks representing a JavaScript runtime and database

Bun's whole thesis is that the batteries should come in the box. It already bundled a package manager, a test runner, and a bundler into the runtime; with the 1.2 line it added a built-in Postgres client, Bun.sql, alongside its existing SQLite support, and by 1.3 it added a zero-config full-stack dev server. The question worth asking is a practical one: with a Postgres client in the runtime itself, do you still reach for postgres.js or an ORM?

What Bun.sql gives you

It is a tagged-template SQL client. You write queries as template literals, interpolate JavaScript values as parameters, and Bun handles the rest: it uses prepared statements and escaping so interpolated values cannot become SQL injection, and it adds automatic prepared statements, query pipelining, and connection pooling under the hood. Bun claims roughly a 50% improvement on row-read throughput versus popular Node Postgres clients, which tracks with pushing the hot path into native code.

db.ts — Bun.sql tagged-template queries

import { sql } from "bun";

// values are parameterized, not string-concatenated
async function findUser(email: string) {
  const [user] = await sql`
    select id, name, email
    from users
    where email = ${email}
    limit 1
  `;
  return user;
}

// composition works too
async function recentPosts(limit: number) {
  return sql`select * from posts order by created_at desc limit ${limit}`;
}

Notice there is no client to import, no pool to construct, no connection string plumbing in your code — Bun reads the standard Postgres environment variables and manages the pool for you.

The full-stack angle

The 1.3 release pushed Bun past "fast Node" into full-stack territory. You can run an HTML file directly and get a dev server with hot module replacement and React Fast Refresh, no separate frontend tool to configure. Combined with Bun.sql and the built-in server APIs, you can stand up a small full-stack app — frontend, backend routes, and database access — inside a single runtime with essentially no build config.

Terminal — a zero-config full-stack start

# run an HTML entry point with HMR + React Fast Refresh
bun ./index.html

# the text-based lockfile is human-readable and diff-friendly
cat bun.lock

# and the Postgres env is read automatically by Bun.sql
# POSTGRES_URL=postgres://... bun run server.ts

Does it replace postgres.js or Drizzle?

For raw queries, Bun.sql genuinely competes with postgres.js — the API is similar, the safety story is sound, and one fewer dependency is a real win. Where it does not compete is with what an ORM gives you on top of the driver: schema definitions, migrations, relational query builders, and end-to-end types from your tables to your results. If you value the type safety and migration workflow of Drizzle or Prisma, Bun.sql is not that — it is the layer those tools would sit on. In fact the cleanest setup may be both: an ORM for typed schema and migrations, with the option to drop to Bun.sql for the occasional hand-tuned query.

The honest take

Bun.sql is a great fit for small services, scripts, and edges of an app where pulling in a driver plus an ORM is overkill — a webhook handler, a cron job, an internal tool. For a large application with a real schema, keep your ORM and enjoy having a fast native client available when you need to bypass it. The broader trend is the interesting part: runtimes are absorbing what used to be your dependency list, and "the database client is built in" is one more line item that may not need to live in your package.json anymore.