Search KetJS

Search titles, descriptions, and section headings.

    Diagram

    100%
    Drag to pan · Scroll or pinch to zoom · + / − to zoom · Arrow keys to pan · 0 to fit · 1 to reset
    Browse documentation
    Docs/Verify and deploy

    Benchmarks

    Compare KetJS database execution, HTTP server and SSR performance with repeatable workloads, exact versions and HTML charts.

    Database execution

    Actual DB operations on 50,000 indexed rows. KetJS adapters and their raw drivers use the same engine, SQL and durability settings. Higher is better.

    SQLite — Indexed tenant read: 20 rows

    • KetJS adapter0.2.0 · range 53,386–59,478 /s56,771 operations/s
    • Raw driver baselinev24.14.1 · range 52,810–58,640 /s57,156 operations/s

    SQLite — Transaction: two balance updates

    • KetJS adapter0.2.0 · range 4,594–21,973 /s21,061 transactions/s
    • Raw driver baselinev24.14.1 · range 21,432–23,910 /s22,713 transactions/s

    PostgreSQL — Indexed tenant read: 20 rows

    • KetJS adapter0.2.0 · range 884–1,083 /s928 operations/s
    • Raw driver baseline3.4.9 · range 923–1,124 /s963 operations/s

    PostgreSQL — Transaction: two balance updates

    • KetJS adapter0.2.0 · range 215–286 /s278 transactions/s
    • Raw driver baseline3.4.9 · range 89–265 /s182 transactions/s
    More database workloads

    SQLite — Primary-key lookup

    • KetJS adapter0.2.0 · range 164,904–176,349 /s169,577 operations/s
    • Raw driver baselinev24.14.1 · range 145,194–190,669 /s174,264 operations/s

    SQLite — Single committed insert

    • KetJS adapter0.2.0 · range 15,023–16,675 /s16,240 operations/s
    • Raw driver baselinev24.14.1 · range 16,372–17,134 /s16,956 operations/s

    SQLite — Transaction: 25 inserts

    • KetJS adapter0.2.0 · range 5,323–6,277 /s5,637 transactions/s
    • Raw driver baselinev24.14.1 · range 4,253–6,907 /s5,890 transactions/s

    PostgreSQL — Primary-key lookup

    • KetJS adapter0.2.0 · range 821–954 /s949 operations/s
    • Raw driver baseline3.4.9 · range 897–1,096 /s999 operations/s

    PostgreSQL — Single committed insert

    • KetJS adapter0.2.0 · range 228–427 /s310 operations/s
    • Raw driver baseline3.4.9 · range 215–419 /s318 operations/s

    PostgreSQL — Transaction: 25 inserts

    • KetJS adapter0.2.0 · range 29–38 /s34 transactions/s
    • Raw driver baseline3.4.9 · range 29–36 /s33 transactions/s

    HTTP server and database

    HTTP/1.1 over loopback, 16 keep-alive clients. Each server uses the same KetJS adapter and SQL. Higher is better; this is not a production capacity test.

    SQLite — HTTP + 20-row indexed read

    • KetJS0.2.0 · range 11,880–12,421 /s12,299 requests/s
    • Node HTTP baselinev24.14.1 · range 11,971–15,187 /s14,101 requests/s
    • Express5.2.1 · range 11,235–12,489 /s12,443 requests/s
    • Fastify5.12.5 · range 11,860–14,155 /s13,579 requests/s

    PostgreSQL — HTTP + 20-row indexed read

    • KetJS0.2.0 · range 843–1,188 /s918 requests/s
    • Node HTTP baselinev24.14.1 · range 789–1,011 /s931 requests/s
    • Express5.2.1 · range 833–1,190 /s1,005 requests/s
    • Fastify5.12.5 · range 813–1,007 /s953 requests/s
    More server workloads

    SQLite — HTTP JSON response

    • KetJS0.2.0 · range 16,632–21,543 /s20,759 requests/s
    • Node HTTP baselinev24.14.1 · range 12,564–25,956 /s23,815 requests/s
    • Express5.2.1 · range 13,941–19,290 /s17,534 requests/s
    • Fastify5.12.5 · range 17,919–22,962 /s19,270 requests/s

    SQLite — HTTP + primary-key lookup

    • KetJS0.2.0 · range 15,882–18,504 /s17,125 requests/s
    • Node HTTP baselinev24.14.1 · range 14,166–21,195 /s18,823 requests/s
    • Express5.2.1 · range 15,370–16,465 /s15,698 requests/s
    • Fastify5.12.5 · range 15,729–18,031 /s17,497 requests/s

    PostgreSQL — HTTP JSON response

    • KetJS0.2.0 · range 15,918–22,100 /s20,159 requests/s
    • Node HTTP baselinev24.14.1 · range 18,761–27,878 /s23,475 requests/s
    • Express5.2.1 · range 14,704–17,777 /s17,207 requests/s
    • Fastify5.12.5 · range 15,830–20,772 /s19,814 requests/s

    PostgreSQL — HTTP + primary-key lookup

    • KetJS0.2.0 · range 823–1,188 /s950 requests/s
    • Node HTTP baselinev24.14.1 · range 904–1,170 /s1,111 requests/s
    • Express5.2.1 · range 839–1,122 /s1,111 requests/s
    • Fastify5.12.5 · range 958–1,234 /s1,189 requests/s

    Server-side rendering

    Production-mode public render APIs, including element creation and HTML escaping. Equivalent product markup is verified. Higher is better.

    SSR: 50 products

    • ketjs-view0.2.027,644 renders/s
    • React19.3.027,087 renders/s
    • Preact11.0.0 / renderer 6.8.035,569 renders/s
    • Vue3.5.4321,294 renders/s

    SSR: 1000 products

    • ketjs-view0.2.01,341 renders/s
    • React19.3.01,314 renders/s
    • Preact11.0.0 / renderer 6.8.01,685 renders/s
    • Vue3.5.431,196 renders/s

    How to read the comparisons#

    The HTML charts above use maintained summaries of the measured runs, with database execution first. Every bar is a median across independent Node processes with balanced framework order; each chart states how many. Bigger bars mean more completed operations per second. These measurements are workload-specific: they do not rank entire frameworks or prove production capacity.

    Database workloads#

    bench/ssr-comparison/database.mjs creates 50,000 products, a primary key and a composite (tenant, value, id) index. It measures an actual primary-key lookup, a tenant-filtered 20-row indexed read, an individually committed insert, a transaction containing 25 inserts and a two-update balance transfer. The unit for transactions is transactions/s, not individual statements/s. Seed data and migrations are outside timing; output rows, insert counts, total balances and rollback correctness are checked.

    SQLite uses a temporary on-disk file, WAL and synchronous=FULL (2). Both its KetJS adapter and raw node:sqlite baseline prepare each statement, so neither gets an artificial prepared-statement-cache advantage. PostgreSQL uses an owned, disposable PostgreSQL 17.10 Docker container with synchronous_commit=on and a one-connection pool for both the KetJS adapter and raw postgres.js baseline. Engine and driver versions appear beside the chart values.

    The raw driver is a lower-level baseline, not another business framework. These adapter operations do not include model validation, authorization, tenant resolution or a domain-function call. SQLite runs natively on macOS; PostgreSQL includes the Docker VM and local TCP path. Compare the two bars within one engine and workload, not SQLite versus PostgreSQL as a universal database ranking. Concurrent writes, lock contention, tenant fleets and production-sized datasets need separate fixtures.

    HTTP server workloads#

    bench/ssr-comparison/server.mjs compares KetJS, Express 5.2.1, Fastify 5.12.5 and a native Node HTTP baseline. All four use the same KetJS adapter, schema, SQL and JSON payload; this isolates routing/response overhead from ORM differences. The native KetJS server is created through its public createKetServer() API. Routes return a small JSON response, one product, or 20 indexed products.

    Each run uses 16 keep-alive HTTP/1.1 clients, 160 warm-up requests and 1,600 timed requests per route, on loopback. Every response status and result is checked. Timing includes the local load generator, JSON parsing and assertions. The charts report median throughput and observed throughput ranges. The PostgreSQL pool of one connection intentionally bounds concurrency; this is not a saturated production load test. Authentication, permissions, domain functions, TLS, proxies, compression, network distance and startup time are outside this fixture.

    SSR renderer workloads#

    bench/ssr-comparison/run.mjs compares ketjs-view 0.2.0, React 19.3.0, Svelte 5.57.1, Vue 3.5.43 and Astro 7.3.5. Each renders the same 50-row or 1,000-row product list with text escaping. ketjs-view, React and Vue create elements through their runtime element functions inside the timer. Svelte and Astro compile components/ProductList.svelte and components/ProductList.astro once before timing, as their builds would, so their template work is partly done ahead of time. The timed loop calls Svelte's render() from svelte/server and Astro's public Container API, renderToString(), which also runs Astro's component-rendering pipeline. The awaited public render API and encoding every result to bytes are included; imports are warm. Encoding keeps a renderer from returning an unflattened string whose cost would land after the timer. Output equivalence ignores hydration comments and the equivalent >/> text encoding. There is no HTTP, hydration or browser work in this comparison. Five processes each start the sequence at a different framework, on the same machine with Node.js 26.7.0.

    The SSR chart and the figures in this paragraph predate the Svelte and Astro harness. They were measured with its previous revision, which compared Preact 11.0.0 (renderer 6.8.0) instead, across four processes on a development machine; a measurement of the current harness on an isolated server will replace them. The charts report versions and medians. ketjs-view 0.2.0 is level with React and behind Preact in that measurement. Its JSX runtime caches each element shape, its server writer compiles each template once into static markup between holes, and escaping copies clean runs in one pass. The harness calls the runtime jsx() for every element, so it measures neither html templates nor the opt-in JSX compiler. Its independent dependency surface and island model are separate properties, not a speed claim inferred from this benchmark.

    Measurement environment#

    Raw run files and generated reports are not checked into the repository or served by this site. Chart summaries remain in this page's Markdown frontmatter. Re-run the harnesses below to inspect current raw results locally under .artifacts/benchmarks/.

    Measured 2026-10-07, using the KetJS 0.2.0 source: Node.js 24.14.1, macOS Darwin 25.2.0, Apple M1 Pro, arm64, 10 logical CPUs and 32 GiB RAM. Generated environment/digest records are local artifacts rather than published downloads.

    Node workloads ran three independent processes, sequentially. Tables report the median of those runs. Every workload validates its result before reporting it. The generated local reports record the source revision and digest. The database and HTTP code paths measured here are the same in 0.2.0.

    These are local microbenchmarks and regression baselines. The new comparisons below cover specific local workloads; they do not establish production HTTP capacity or universal framework speed ratios. Earlier competitor and KetSuite-domain tables have been removed because their old harnesses are no longer in this framework repository and were not rerun.

    Published npm footprint#

    Fresh isolated consumers install the exact published versions with installation scripts disabled, using node tools/benchmark-footprint.mjs. The 0.2.0 footprint is measured after that version is published; View Tools 0.2.0 adds acorn and acorn-jsx to its dependencies, and standalone View still has none.

    KTL rendering and query compilation#

    KTL renders fifty products with the same pre-created currency formatter on every render. Templates are compiled before timing, and output is checked byte-for-byte against a known result. Query timing includes construction of two predicates, ordering and a limit, then generation of SQLite SQL and parameters. It does not execute the query.

    Operation Iterations per run Median throughput
    KTL: 50 products with cached money filter 20,000 33,040/s
    Query: two predicates, order, limit and SQLite SQL 200,000 506,370/s

    This fixture makes no EJS, Liquid, Knex or Drizzle comparison; earlier speed ratios are not carried forward.

    Localization hot paths#

    Each process warms every path before timing. Inputs and iteration counts are fixed in bench/i18n.bench.ts; the date bucket uses Asia/Ho_Chi_Minh.

    Operation Median throughput
    plain translation 19,448,600 ops/s
    placeholder translation 4,056,011 ops/s
    plural translation 1,744,804 ops/s
    cached date/time format 676,641 ops/s
    timezone date bucket 502,159 ops/s

    No uncached baseline was measured during this run, so the table claims no cache improvement ratio.

    Module discovery startup#

    A catalogue contains 250 candidate modules; the selected dependency closure contains 40. Unselected modules throw if executed, and every measured resolution asserts the selected module count. The fixture now exports public defineModule() declarations instead of manually copying an old internal object shape.

    Cold means the first catalogue resolution in an already running process whose framework imports are loaded. Each process then performs 25 warm resolutions. The warm p95 below is the median of the three processes' internal p95 values, not a pooled percentile.

    Measure Median across three processes
    First resolution 34.95 ms
    Warm median 12.95 ms
    Warm p95 15.91 ms

    This is catalogue startup cost, not request throughput.

    SQLite queue across tenant databases#

    The public worker and tenant APIs handle 32 physical databases, 100 jobs per database and 8 concurrent workers. Notification shortcuts are disabled. Every run asserts all 3,200 jobs complete in the expected tenant, with migrations and runtime preparation outside the measured queue regions.

    Measure Median across three processes
    Enqueue time 473.4 ms
    Enqueue throughput 6,760.0 jobs/s
    Execution time 1,545.5 ms
    Execution throughput 2,071.0 jobs/s
    First-job spread across tenants 130.0 ms

    PostgreSQL was not measured in this run; its historical throughput is not presented as current. The fixture's jobs have minimal handlers, so these numbers do not represent a real domain workload.

    Real browser DOM updates#

    Chromium 154.0.8037.95, 1440 × 1000, light theme. The native TSX fixture mounts 1,000 keyed rows in a real DOM, performs five warmup rounds and thirty measured rounds, and checks the final row count and changed text. Timings include rendering and DOM API calls, excluding layout and paint. Browser timer resolution and background activity affect very short samples.

    Operation Median p95
    Mount 1,000 rows 4.70 ms 5.90 ms
    Update one row 1.10 ms 1.80 ms
    Unchanged render 1.00 ms 2.00 ms
    Swap two rows 1.00 ms 2.40 ms
    Remove and re-add one row (two renders) 2.30 ms 3.60 ms

    The old lit comparison used a different fixture and environment; no current cross-framework ratio is claimed.

    Renderer host operations#

    A separate counting-host fixture verifies the mutation cost independent of browser time. This fixture is not a DOM performance measurement.

    Change to 1,000 keyed rows Host operations
    Update one text field 1 setText
    Unchanged render 0
    Prepend one row 15
    Remove one row 3 removals
    Swap two rows 2 moves

    The former island-vs-whole-tree timings are not included: that fixture is absent, and counting operations does not recreate its timing claim.

    Reproduce#

    The Node harnesses write generated reports and raw runs to the ignored .artifacts/benchmarks/ directory. They do not update or publish the maintained Markdown summaries automatically.

    The Node harness needs emitted framework artifacts. Its build compiles the five-package workspace; it does not run the repository's test suites. Benchmark deployments are the synthetic headless catalogue fixture and queue_benchmark, not a KetSuite product deployment.

    # Run from: repository root
    npm run build
    node tools/benchmark-report.mjs
    node tools/benchmark-footprint.mjs
    node tools/benchmark-browser.mjs
    # Separate terminal for the comparative fixtures:
    npm ci --prefix bench/ssr-comparison --ignore-scripts
    node tools/benchmark-ssr.mjs
    # Docker must already have postgres:17.10-alpine; no shared service is modified.
    node tools/benchmark-server-db.mjs

    Open http://127.0.0.1:3701/ and press Run benchmark for the real browser fixture. Save its JSON results and browser environment locally under .artifacts/benchmarks/ before closing the benchmark page. The footprint command installs only the three named npm consumers into disposable directories.

    Not measured here#

    • Current EJS, Liquid, Knex, Drizzle and lit comparisons need pinned competitor versions and equivalent maintained fixtures.
    • PostgreSQL queue throughput needs an isolated database service and a separately recorded run.
    • Product, pricing, stock, hospitality and storage workloads belong to KetSuite or its deployment repositories. Their old numbers no longer appear as KetJS-framework benchmarks.
    • Production HTTP capacity, concurrency/lock contention, layout/paint and full-page versus island timings need dedicated workloads beyond these local fixtures.

    Those gaps are explicit; there are no reused historical numbers standing in for fresh measurements.

    View source on GitHub ↗KetJS 0.2.0 preview