Skip to content

Deno 2.9.2 (npm:postgres) ​

Benchmark · Deno · July 2026

Part of the PostgreSQL REST API Benchmark, July 2026 series.

At a glance ​

VersionDeno 2.9.2 — new entrant this round, completing the JS-runtime trio
PostgreSQL driverpostgres.js 3.4.9 via npm: specifier — 12 connections per worker, ~96 aggregate
Concurrency model8 self-spawned processes sharing port 3105 via SO_REUSEPORT (--unstable-net)
Lines of code143
Podiums🥇 2 · 🥈 3 · 🥉 6 — of 38 combinations
Sourcedeno-app-v2.9.2

The verdict in one sentence: a first-round entrant that finished #2 on the minimal baseline — ahead of Bun and both Node frameworks, behind only Go — running the same npm driver as Bun, unmodified, through node-compat.

Implementation ​

No framework — Deno.serve with manual dispatch, structurally identical to the Bun app. Two Deno-specific details are worth recording. First, npm:postgres@3.4.9 worked without a single code change through Deno's Node compatibility layer. Second, reusePort in Deno.serve requires the --unstable-net flag — a real deployment note from this round, since without it the 8-worker layout silently cannot share the port (app.ts):

ts
ts
import postgres from 'npm:postgres@3.4.9';

// --unstable-net is required for SO_REUSEPORT (reusePort) in Deno.serve
const PERMISSIONS = ['--unstable-net', '--allow-net', '--allow-env', '--allow-read', '--allow-sys'];

if (WORKERS > 1 && !Deno.env.get('DENO_WORKER')) {
    const children = [];
    for (let i = 0; i < WORKERS; i++) {
        children.push(new Deno.Command(Deno.execPath(), {
            args: ['run', ...PERMISSIONS, new URL(import.meta.url).pathname],
            env: { DENO_WORKER: '1' },
        }).spawn());
    }
    await Promise.all(children.map((c) => c.status));
    Deno.exit(0);
}

Deno.serve({ port: 3105, hostname: '0.0.0.0', reusePort: true }, async (request) => {
    /* dispatch on pathname, return Response */
});

143 lines — three more than Bun, the difference being the permissions plumbing above.

Results ​

Where it medaled ​

Deno's eleven podiums span four of the six scenarios, with the minimal baseline as the headline:

ScenarioResult
Minimal baseline🥈 16,436 at 100 VU and 🥈 13,487 at 500 VU, 🥉 15,140 at 200 VU — best of the JS quartet, behind only Go everywhere
POST, 100-record bodies🥇 1,347 (50 VU) and 🥇 1,315 (100 VU) — outright wins over the whole field
POST, 50 VU / 10 rec🥉 7,238 — vs Fastify 4,111 / Express 4,097 with the same JSON body
Many parameters🥉 at all three levels: 14,797 / 14,010 / 13,086
Large payload🥈 1,418 at 25 VU / 100 KB

The 100 VU minimal number deserves emphasis: 16,436 req/s beat Bun (16,106), Fastify (16,243), and Express (16,140) — a debut entrant taking the JS crown, 2.6% behind Go's 16,882.

Where it didn't ​

The same postgres.js row-scanning weakness that caps Bun caps Deno: on perf-test's wide typed rows it falls to 164 (#20) at 50 VU / 100 records and 1,391 (#16) at 200 VU / 10 records, and nested JSON at depth 3 lands #19–#20. At 1 VU it is mid-field (676, #10, on perf-test 1 record). Where serialization weight dominates, the trio's driver choice cuts both ways.

Latency ​

p99 of 22 ms on minimal at 100 VU and 14 ms on params at 50 VU — indistinguishable from the rest of the quartet where it counts. Deno held 11 of 16 perf-test combinations under 1 s p99, missing Bun's field-best 12 by a single cell: 50 VU / 500 records came in at 1,061 ms where Bun squeaked under at 991 ms. At the very heaviest load its tail is the better of the pair — 4,220 ms p99 at 200 VU / 500 records vs Bun's 5,184 ms.

Resource usage ​

Peak memoryAvg memoryAvg CPU
740 MB500 MB105%

Middle of the quartet: well under the V8 clusters (Fastify 1,177 MB, Express 1,094 MB) but 2.2× Bun's 340 MB peak for the same 8-process layout and driver. Memory is the one axis where Deno's otherwise winning quartet scorecard clearly loses to Bun.

Analysis ​

Deno completes the quartet experiment, and its debut is what makes the conclusion decisive: four near-identical apps across two JS engines (V8 and JavaScriptCore) and two drivers converged within ~2% on the minimal baseline. The JS runtime wars are effectively a three-way tie on throughput. Deno edges Bun by 2.5% on minimal at 200 VU (15,140 vs 14,777) and takes the quartet's only outright golds (POST at 100-record bodies), but pays double Bun's memory; the Node pair matches both on the baseline and loses ~45% on POST because of node-postgres, not because of Node. Choose on ecosystem, memory budget, and tooling — the built-in TypeScript, the permission model, the npm: compatibility that made this 143-line app possible — because req/s will not decide it for you.

Two operational notes for anyone reproducing the layout: the --unstable-net requirement for reusePort means the multi-process pattern is not yet on Deno's stable surface, and the self-spawn via Deno.Command needs --allow-run in the container's CMD.

January → July movement: none — Deno is new this round. The standing caveat applies regardless: the fairness overhaul changed absolute numbers for every framework, so only this round's rankings are meaningful for future comparisons.

Explore on GitHub ​

Everything this page claims can be checked against the running code and the raw output:


Series: Introduction · Overall Analysis · Raw Results

Next framework: Actix-web →

Comments