Featured image for OLAP in JavaScript: Libraries and Practical Examples

OLAP in JavaScript: Libraries and Practical Examples

Published on:

Reading time: 10 min

Topic: Technology

Author: Leandro Valencia

#olap javascript#duckdb wasm#in-browser analytics#apache arrow#analytical databases

How to run OLAP-style analytics in JavaScript: DuckDB-Wasm, Apache Arrow, Arquero, Perspective, SQL.js and TinyBase, with examples and when to use each one.

Table of Contents

The landscape: full engines vs. lightweight tools

Before going library by library, a useful distinction. In JavaScript there are two categories of solution for "I need to aggregate data without hitting my production database":

Real OLAP engines compiled to WebAssembly. They run analytical SQL with columnar storage, vectorization, and a query optimizer, just like ClickHouse or BigQuery, but inside the browser process or Node. DuckDB-Wasm is the main representative.

In-memory transformation and aggregation tools. They are not database engines; they are libraries that manipulate already loaded data structures (arrays, Arrow tables, reactive stores) with operations like groupBy, sum, or pivoting. Arquero, Perspective, and TinyBase fall here, each with a different approach.

SQL.js sits in the middle: it is a real database engine (SQLite), but row-oriented, not columnar, so it does not scale as well on purely analytical loads even though it is widely used as a lightweight substitute.

DuckDB-Wasm: the full OLAP engine in the browser

DuckDB-Wasm is the compilation of DuckDB —the embedded analytical engine we already mentioned in the OLAP guide as the default option for datasets that fit on a single machine— to WebAssembly. It runs both in the browser and in Node, with no server, with the same vectorized columnar engine and the same PostgreSQL-compatible SQL dialect as the native version.

It is the most powerful option on this list because it is not an OLAP simulation: it is a real query optimizer, with support for reading Parquet, CSV, and JSON directly, even over HTTP range requests, without downloading the whole file.

When to use it: when you need to run real SQL queries —aggregations, JOINs, windows— on moderate-size datasets (from a few MB up to a few hundred MB) directly on the client, with no backend infrastructure. It is the natural choice for embedded analytics dashboards that load a data export, client-side data exploration tools, or to avoid sending heavy queries to a server when the file already lives in the user's browser.

A simplified example of what a query against a Parquet file looks like once the engine is initialized and connected:

const result = await connection.query(`
  SELECT pais, SUM(importe) AS ingresos
  FROM read_parquet('ventas.parquet')
  GROUP BY pais
  ORDER BY ingresos DESC
`);

console.table(result.toArray());

Initialization (picking the right WebAssembly bundle, starting the worker, opening the connection) has several steps and changes across versions, so I am not reproducing it here: check the official DuckDB-Wasm documentation for the exact setup of the version you install.

Apache Arrow (JS) + Arquero: columnar format plus dplyr-style transformation

These two libraries often go together, and it is worth understanding them separately.

Apache Arrow is not a query engine: it is an in-memory columnar data format, standardized across languages (there are implementations in Python, R, Java, C++, and JavaScript, among others). Its value is that many data tools —including DuckDB-Wasm— can read and produce Arrow directly, so moving data between them does not require serializing to JSON or rebuilding structures: they share the same memory layout.

Arquero is a data-transformation library for JavaScript explicitly inspired by R's dplyr. It works on columnar tables —arrays, typed arrays, or Arrow columns— and exposes a chainable API with verbs such as filter, group, aggregate, or join tables.

When to use them: when you already have data in Arrow format (for example, exported from a Python pipeline or returned by DuckDB-Wasm) and you want to chain roll-up or aggregation transformations directly in JavaScript, without writing SQL. It is a good option for JS data notebooks, interactive exploration tools, or when the team already thinks in dplyr/pandas terms and prefers that syntax to SQL.

A simplified example of aggregation with Arquero on an already loaded table:

import { table } from 'arquero';

const ventas = table({
  pais: ['ES', 'MX', 'ES', 'AR'],
  importe: [42, 17, 88, 12],
});

const resumen = ventas
  .groupby('pais')
  .rollup({ ingresos: (d) => op.sum(d.importe) });

console.log(resumen.objects());

The exact import API, the names of aggregation functions (op.sum, op.mean, and so on), and how to load an Arrow table with fromArrow() are worth checking in the official Arquero documentation, because they vary across versions and depending on whether you use the bundle that includes Arrow or import it separately.

Perspective: a pivoting engine for real-time dashboards

Perspective is an aggregation and pivoting engine built in C++ and compiled to WebAssembly, with JavaScript bindings. It was born inside J.P. Morgan for internal financial dashboards and is now an open-source project under the FINOS Foundation.

What sets Perspective apart from the other options on this list is that it is designed specifically for data that changes in real time: it supports streaming updates, interactive pivot-table-style pivoting (group rows and columns, apply aggregations, sort) and ships with its own visual components (<perspective-viewer>) ready to drop into a page.

When to use it: when the use case is an interactive dashboard where the end user manipulates the view —drags dimensions, changes aggregations, filters— and the underlying data updates live, for example a trading panel, an operational metrics monitor, or BI embedded inside your product. If you only need to run a one-off query and show a fixed result, it is more engine than you need; DuckDB-Wasm or Arquero fit better for that.

SQL.js: SQLite in WebAssembly, simple but not columnar

SQL.js is a compilation of SQLite to WebAssembly that lets you run a full SQLite database inside the browser, with no backend. It is probably the most veteran library on this list and the easiest to adopt if you already know basic SQL.

Here we have to be honest about its limits: SQLite is a row-oriented database, just like Postgres or MySQL. It has no columnar storage, no vectorization, and no optimizer designed to scan millions of rows and aggregate them. It is not an OLAP engine in the technical sense of the term.

Even so, it is widely used as a lightweight alternative for small analysis on the client: datasets of a few thousand or tens of thousands of rows, where the performance gap versus a real columnar engine is not noticeable, and where the advantage is the simplicity and maturity of the library.

When to use it: for exploratory analysis of small datasets in the browser, quick prototypes, or when you need full SQL (including writes and transactions) more than performance aggregating millions of rows. If your dataset grows beyond that, migrate to DuckDB-Wasm.

TinyBase: a lightweight reactive store, not an OLAP engine

TinyBase is a reactive data store designed for JavaScript application state, with a very small library size. It is not designed as an analytical engine: its main purpose is state synchronization, local persistence, and reactivity (the UI updates on its own when the data changes), closer in spirit to state-management libraries than to an analytical database.

I include it on this list because it does offer simple aggregation utilities —count, sum, average over a table— that in small apps cover needs that would otherwise require standing up a heavier engine.

When to use it: when the "analysis" you need is actually a simple aggregation over data that already lives in your app state —a counter, a total, an average that updates live— and you do not want to add a full SQL engine just for that. I would not pick it for anything that looks like a multidimensional dashboard or ad-hoc queries.

Quick comparison table

Library What it is Real OLAP engine Best for
DuckDB-Wasm DuckDB compiled to WASM Yes, columnar and vectorized Full analytical SQL on client or Node
Apache Arrow (JS) + Arquero Columnar format + dplyr-style transformation Format yes, query engine no Transformation pipelines in JS without SQL
Perspective WASM pivoting engine (FINOS) Yes, oriented to interactive aggregation Real-time dashboards with pivoting
SQL.js SQLite compiled to WASM No (row-oriented) Small analysis, simple SQL on the client
TinyBase Lightweight reactive store No Simple aggregations over app state

How to choose

If you need real SQL over a Parquet or CSV file of respectable size, with no backend: DuckDB-Wasm, no hesitation.

If you already work with data in Arrow format and prefer to chain transformations in JavaScript instead of writing SQL: Arquero.

If you are building an interactive dashboard where the user pivots data that changes live: Perspective.

If your dataset is small and you only want basic SQL with no pretensions of columnar performance: SQL.js.

If all you need is a reactive counter or total inside your app state: TinyBase, and you probably do not even need to think in OLAP terms.

In every case, before writing production code, check the official documentation of the version you install: initialization APIs (especially in DuckDB-Wasm and Perspective, which depend on workers and WebAssembly bundles) change fairly often between versions.

Frequently asked questions

Is there a native columnar OLAP engine in JavaScript, without WebAssembly?

Not in any way comparable to DuckDB or ClickHouse. Real vectorized columnar engines are written in C++ or Rust and reach JavaScript compiled to WebAssembly (DuckDB-Wasm, Perspective). Pure JavaScript does not have an engine of this type with equivalent adoption.

Can I use DuckDB-Wasm in a Node backend, not just in the browser?

Yes. DuckDB-Wasm runs both in the browser and in Node, although for a pure backend there is also the native DuckDB binding for Node, which avoids the WebAssembly layer. Which one is better depends on whether you need to share code between client and server.

Does SQL.js work as a replacement for a real OLAP database?

Not for large loads. SQL.js is row-oriented SQLite; it works well for small analysis on the client, but it does not scale like a columnar engine on datasets of millions of rows.

Does Arquero need Apache Arrow to work?

Not for basic use over ordinary arrays. Arrow becomes relevant when you want to interoperate with data that already comes in that format (for example, from DuckDB-Wasm) or export results to Arrow.

Related Posts

Keep exploring similar content that may interest you

Partnerships

Tools I use every day, on better terms for this community.

Affiliate links. Your price does not change.See all partnerships
Training program

Ready to turn your idea into a real project?

Transforma is the program where you will learn to create, execute and scale your project with clarity and method.

Discover the Transforma Program