Skip to content

Overall Analysis: What 760 Tests Say

Benchmark · Analysis · July 2026

Part of the PostgreSQL REST API Benchmark, July 2026 series. Methodology and fairness changes are covered in the introduction; every number below is reproducible from results.csv.

Five findings

  1. Go won everything that measures the HTTP server itself — all three minimal-baseline combinations, all three params combinations, most of large-payload and POST: 17 gold medals of 38 combinations, more than twice anyone else. On 25 MB of average memory.
  2. FastAPI won the headline scenario — first place in comprehensive data-type serialization (perf-test) at every concurrency level, after finishing bottom-tier in January. Nothing about FastAPI changed; its deployment did (1 worker → 8). The single largest ranking movement in the benchmark's history is a configuration diff.
  3. NpgsqlRest's SQL file source is the fastest zero-code path ever measured here — +33% over the routine source on small payloads, #2 and #3 in the field on perf-test, ahead of hand-written same-driver .NET code by >50%. Full analysis in the deep dive.
  4. Sibling frameworks are statistically identical. Actix vs Axum: +0.8%. Fastify vs Express: −1.4%. EF Core vs Dapper (perf-test): +1.1%. Bun vs Deno (minimal): +2.5%. Once deployment and pools are equalized, the driver and the runtime decide; the framework layer above them is noise at this workload.
  5. Swoole is the efficiency phenomenon: 14 gold medals — every heavy perf-test combination — on the smallest memory footprint of the entire field (44 MB peak).

Winners by category

Reference combos per scenario; medals count across all 38 combinations follows.

Category🥇🥈🥉
Minimal baseline (100 VU)Go 16,882Deno 16,436Fastify 16,243
perf-test (100 VU, 1 rec)FastAPI 5,109NpgsqlRest Files AOT 3,524NpgsqlRest Files JIT 3,467
perf-test heavy (100 VU, 500 rec)Swoole 45Go 39FastAPI 38
POST body (50 VU, 10 rec)Go 7,598Bun 7,461Deno 7,238
Nested JSON (50 VU, d1)Go 2,106Swoole 2,092FastAPI 2,030
Large payload (25 VU, 100 KB)Go 1,427Deno 1,418FastAPI 1,416
Many params (50 VU)Go 15,449Bun 15,156Deno 14,797

Podium totals across all 38 combinations:

Service🥇🥈🥉
Go 1.2617133
Swoole PHP 6.2.11480
FastAPI 0.139.04316
Deno 2.9.2236
Bun 1.3.14156
NpgsqlRest SQL Files (AOT/JIT)055

Two services own 31 of 38 golds — but in disjoint territories. Go rules everything server-bound; Swoole rules everything payload-bound. They are almost never in each other's way.

The headline ranking, with tiers

perf-test at 100 VU, 1 record — the closest thing to "a typical API lookup under load":

TierServicereq/s
LeaderFastAPI5,109
ChasersNpgsqlRest SQL Files AOT / JIT3,524 / 3,467
The pack (within 20%)Deno · Bun · Go · Spring Boot · NpgsqlRest Routine ×4 · Fastify · Express · Swoole3,047 – 2,431
TrailingAxum · .NET Dapper · Actix · .NET EF2,263 – 2,230
BottomDjango · PostgREST1,911 / 1,322

Three observations a ranking alone hides:

  • The pack is dense: positions 4 through 14 span 25%. Small implementation choices (driver pipelining, JSON pass-through) matter more than language here.
  • The Rust pair trailing Go, Bun and even Spring Boot will surprise readers — but at 1-record requests this is a driver round-trip benchmark, and tokio-postgres's path through this workload is simply slower than pgx's or Npgsql's. The Actix and Axum pages dig in, including their remarkable CPU efficiency (the lowest in the field — they do these numbers half-asleep).
  • PostgREST last despite this round fixing its connection pool handicap — see its page for the honest breakdown.

Scaling behavior

Requests/s at 1 record as concurrency grows, with the 1→200 VU multiplier:

Service1 VU50 VU100 VU200 VUFactor
FastAPI8545,0485,1095,0536.0×
NpgsqlRest Files AOT6583,5683,5243,4815.4×
NpgsqlRest Files JIT7533,6213,4673,4534.8×
Deno6763,0653,0473,1204.6×
Bun7383,0683,0163,1404.3×
Go9273,0492,9482,9433.3×

Two patterns. First, everyone saturates by 50 VU and holds flat to 200 — no service in the field degrades under 4× more concurrency than its saturation point, which is a collective compliment to 2026's server runtimes. Second, the scaling factor mostly measures the inverse of single-request speed: Go's "low" 3.3× reflects the field's best 1 VU baseline (927), not a scaling problem.

Latency: the p99 story

All 760 tests recorded full latency distributions (median/p90/p95/p99/max — in the dataset). The benchmark's latency objective was p99 < 1 s per test. Results:

  • 657 of 760 tests met it. The 103 that did not are the same combinations for every service: 100+ records at 50+ VU — where a 100-record, 23-column JSON response at 200 concurrent users is simply more bytes than 4 cores serialize in under a second at p99. Physics, not framework failures.
  • Held per service: 11 of 16 perf-test combinations for eighteen services; Bun and Swoole held 12 — the only two to keep p99 under 1 s at 50 VU × 500 records.
  • At sane payloads the field is tight: on minimal at 100 VU, every service's p99 sits between 21 ms (Go) and 43 ms except Django and PostgREST.
  • Zero failed requests, anywhere. 20-for-20 services survived every load level without a single 5xx or dropped connection — the restart: unless-stopped safety nets went unused.

Resource efficiency

Peak/average memory and CPU, measured only during each service's active test windows:

ServicePeak MBAvg MBAvg CPU
Swoole PHP442466%
Go592590%
PostgREST11257103%
Axum1452549%
NpgsqlRest AOT (best of 3)15562110%
Actix-web1675958%
.NET 10 EF214124150%
NpgsqlRest JIT (best of 3)225124129%
.NET 10 Dapper244102128%
FastAPI282237200%
Bun340229110%
Django671281230%
Deno740500105%
Spring Boot75640498%
Express (8 workers)1,094577109%
Fastify (8 workers)1,17760092%

The fairness overhaul's price tag is visible here: equal cores cost the interpreted runtimes their memory story. Eight Node processes cost Fastify and Express over a gigabyte at peak; FastAPI's crown came with the field's second-highest CPU burn. Meanwhile Swoole delivers 14 golds from 44 MB, and the Rust pair posts mid-table throughput at half the CPU of anyone else — the two most efficient architectures in the field by throughput-per-resource, one interpreted, one compiled.

Bun deserves a line: 340 MB for 8 processes versus Deno's 740 MB and Node's ~1,100 MB for the same work is the clearest runtime-efficiency signal in the JavaScript quartet.

Performance per line of code

Lines of application code (or configuration, where no code exists) to implement the identical six endpoints, against headline throughput:

ServiceLinesperf-test 100 VU/1 rec
PostgREST12 (config)1,322
NpgsqlRest Routine21 (config)2,617
Fastify / Express102 / 1032,583 / 2,578
.NET EF / FastAPI / Dapper105 / 123 / 1292,230 / 5,109 / 2,255
NpgsqlRest SQL Files140 (config + the SQL itself)3,467–3,524
Bun / Deno140 / 1433,016 / 3,047
Spring Boot / Django / Swoole165 / 183 / 1982,710 / 1,911 / 2,431
Axum / Actix / Go248 / 256 / 3032,263 / 2,246 / 2,948

The pattern from January holds and sharpens: the top of the throughput chart and the bottom of the effort chart are the same neighborhood. NpgsqlRest's 21-line configuration outruns Go's 303 hand-written lines on this scenario; its SQL-file variant — where the only "code" is the SQL you'd write anyway — sits #2 in the entire field. Go remains the honest counter-example: when you do pay the 303 lines, you buy categories nothing else wins.

January → July: ranking movements

Absolute numbers are not comparable across rounds (see methodology); positions are:

ServiceJanuaryJulyWhy
FastAPIBottom tier#1 perf-testworkers 1 → 8
SwooleLarge-payload championMid-pack on large, still heavy-perf-test kingworkers cpu×2 → cpu
NpgsqlRest#1 perf-test (routine)#2–3 (SQL files)New source; FastAPI's fix outpaced it
Bun"Single-threaded champion"Pack memberEveryone got workers
PostgRESTBottom tierBottom tierPool fix helped less than hoped
GoMinimal/params/POST kingUnchangedNever depended on configuration

The honest summary: about half of January's ranking signal was deployment configuration, not framework capability. This round removed that variable. What remains — driver efficiency, serialization architecture, runtime memory models — is what these pages analyze.

Limitations, stated plainly

  • One run per combination; no variance bars. The 30-second quiet gaps, per-test warmups, and paused-idle isolation minimize drift, but a delta under ~3% between adjacent services should be read as a tie.
  • 4 dedicated cores per service. Rankings could shift at 16+ cores, particularly for the JVM.
  • Closed-loop load (k6 VUs) measures max throughput well but understates latency under a fixed arrival rate; an open-model scenario is on the wishlist for next round.
  • The workload is deliberately database-shaped (function call in, JSON out). CPU-bound application logic would favor the compiled languages far more than these tables do.

Series: Introduction · NpgsqlRest Deep Dive · Raw Results

Next: Raw Results →

Comments