Cube
An open-source, headless semantic layer: define metrics, joins and access rules once in YAML, query them over SQL, REST or GraphQL, and serve repeated queries from rollups it builds ahead of time.
- What it is
- Semantic layers,Static read models
- Type
- Open source
- Made by
- Cube Dev, Inc.
- License
- Apache 2.0 (Cube Core backend), MIT (client libraries)
- Facts checked
- 9 October 2026
What it is
Cube sits between a SQL data source and everything that reads from it. You describe the data in cubes (entities such as orders, with their measures, dimensions and joins) and views (curated datasets built from cubes), in YAML or JavaScript files kept in git. Consumers query those names through a Postgres-compatible SQL API, REST (JSON) or GraphQL, and AI agents through an MCP server. Cube checks each query against the model, applies access policies, generates SQL in the source’s dialect and returns the result. It connects to warehouses such as Snowflake, BigQuery, Databricks and ClickHouse, query engines such as Athena, Trino and DuckDB, and application databases such as PostgreSQL and MySQL. It has no UI of its own: that is what “headless” means here.
Its cache is where it becomes a static read model. Pre-aggregations are rollup tables declared in the data model, for example revenue and order count by status and month. A refresh worker builds them in the background and stores them in Cube Store, a distributed engine written in Rust on Apache Arrow and DataFusion that keeps the data as Parquet files in S3, Google Cloud Storage or MinIO. When a query arrives, Cube’s aggregate awareness finds a matching rollup and answers from it instead of the warehouse. Each rollup has a refresh key, every hour by default, or a cheap SQL check such as SELECT MAX(created_at) FROM orders that triggers a rebuild only when the data changed. Cube Core is the open-source project; Cube, the company’s commercial product, adds workbooks, dashboards, an analytics chat, hosting and more on the same model.
Why teams pick it
- One model for every consumer: SQL, REST, GraphQL and MCP interfaces mean BI tools, apps and AI agents get the same metric definitions and access rules
- Pre-aggregations answer matching queries from Cube Store without touching the warehouse, and a rollup-only mode lets the warehouse stay suspended between refreshes
- Incremental, partitioned rollups refresh only recent partitions (say the last three months), which cuts rebuild time and cost on per-scan warehouses such as BigQuery and Athena
- Code-first and open: Apache 2.0 backend, models in git, and a data model that runs unchanged in Cube Core and the commercial Cube
Watch out for
- Development mode is an authentication bypass: with
CUBEJS_DEV_MODE=truethe REST and GraphQL APIs accept requests without a token, so it must never be reachable from the internet - Rollups only help queries they match; anything else goes to the source, or fails in rollup-only mode on a single node, so the set of pre-aggregations needs ongoing tuning
- Rollups are only as fresh as their refresh key, and builds queue when there are too many partitions: Cube’s guidance is to avoid more than 64, and fewer when a rollup has extra indexes
- It needs a SQL source: Cube isn’t built to read files or REST APIs directly (use DuckDB for Parquet on S3), and some drivers need write access to build rollups in the source first
- Several conveniences, such as importing dbt models and scheduled dashboard refreshes, are Premium features of the commercial Cube rather than part of Cube Core
How it fits with other technologies
What Cube uses
- Cubecompiles metric queries to SQL forSnowflake
- Cubecompiles metric queries to SQL forGoogle BigQuery
- Cubecompiles metric queries to SQL forDatabricks
- Cubecompiles metric queries to SQL forClickHouse
- Cubecompiles metric queries to SQL forPostgreSQL
- Cubecompiles metric queries to SQL forAmazon Athena
- Cubecompiles metric queries to SQL forTrino
- Cubequeries Parquet and CSV files throughDuckDB
- Cubekeeps Cube Store rollup files inAmazon S3
- Cubestores pre-aggregations asApache Parquet
model/cubes/orders.yml: a cube with a monthly rollup that matching queries are served from · yaml
cubes:
- name: orders
sql_table: orders
measures:
- name: count
type: count
- name: revenue
sql: amount
type: sum
dimensions:
- name: id
sql: id
type: number
primary_key: true
- name: status
sql: status
type: string
- name: created_at
sql: created_at
type: time
pre_aggregations:
- name: revenue_by_status_month
measures: [revenue, count]
dimensions: [status]
time_dimension: created_at
granularity: month
refresh_key:
every: 1 hour
# Query it (dev mode needs no token):
# curl -G localhost:4000/cubejs-api/v1/load --data-urlencode 'query={"measures":["orders.revenue"],
# "dimensions":["orders.status"],"timeDimensions":[{"dimension":"orders.created_at","granularity":"month"}]}'
# The response lists dev_pre_aggregations.orders_revenue_by_status_month under usedPreAggregations:
# the answer came from the rollup in Cube Store, not from the orders table.Cube’s two cache levels#
Both are on the read path; only the second is computed ahead of time.
| In-memory cache | Pre-aggregations | |
|---|---|---|
| What it stores | Results of queries already run, keyed by the generated SQL | Rollup tables declared in the data model, stored as Parquet in Cube Store |
| When it’s filled | On the first request for a query | Ahead of time, by a background refresh worker |
| When it’s refreshed | When the cube’s refresh key changes; checked every 2 minutes on Athena, BigQuery, Databricks, Firebolt, Presto and Snowflake, every 10 seconds elsewhere | Every hour by default, or when a refresh SQL check returns a new value; partitioned rollups can refresh only a recent window |
| Set up | On by default | Declared per cube under pre_aggregations |
See it in a real stack
No architecture uses Cube yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from2016–2022
Artyom Keydunov and Pavel Tiunov founded Statsbot in 2016, a business intelligence tool that pulled reports into Slack. To power it they built an engine that generated SQL, cached results and handled security between databases and the front end. Statsbot’s users kept asking whether they could use that engine for their own internal or customer-facing analytics, so the founders released it on its own as Cube.js, an open-source analytics framework, in 2019, launching it on Hacker News.
Cube.js already sped up queries with pre-aggregations, but they lived in the source database or a separate PostgreSQL or MySQL instance, and that store became the bottleneck for concurrency and latency. In April 2021 Cube Store became generally available: a pre-aggregation store written entirely in Rust on Arrow, DataFusion and Parquet. By August 2022 more of the code was Rust than JavaScript, distribution had moved from npm to Docker, and the company dropped the “.js”: Cube.js became Cube, a headless BI platform.
The company now sells Cube as an agentic analytics platform with workbooks, dashboards and an AI chat built on the same model, and maintains the open-source part as Cube Core. Its pitch for a semantic layer has shifted with it: agents that write SQL against raw tables produce inconsistent metrics, while agents that query a governed model get consistent ones.
Why it’s built this way
- Headless: APIs instead of a UI
- Statsbot’s users wanted the engine behind their own apps and dashboards, so Cube exposes SQL, REST and GraphQL and leaves the visualisation to whatever sits on top.
- A dedicated store for rollups
- Keeping pre-aggregations in PostgreSQL or MySQL limited concurrency and latency and lacked features such as approximate distinct counts, so Cube built Cube Store as a drop-in replacement that scales out with more workers.
- Rust, Arrow and Parquet
- The team wanted low-level performance for the store and built on existing open-source pieces: Parquet for data at rest, Arrow in memory and DataFusion to execute queries.
- Models as code
- Data models, configuration and access policies are files in version control, so changes go through review and CI, and AI tools can propose edits through the same workflow.
Timeline
- 2016
Keydunov and Tiunov found Statsbot and build the engine behind it
- 2019
Cube.js released as an open-source analytics framework
- 2020
Cube Dev raises a $6.2M seed round led by Bain Capital Ventures
- 2021
Cube Store, written in Rust, becomes generally available in April
- 2022
Cube.js is renamed Cube in August
- TechCrunch: Cube Dev raises $6.2M for its open-source data platform (Sept 2020; Statsbot origin)
- Pulse 2.0: interview with Cube CEO Artyom Keydunov (Cube Core launch on Hacker News in 2019)
- Cube blog: Introducing Cube Store (Apr 2021)
- Cube blog: Cube.js is now Cube (Aug 2022)
- Cube docs: Introduction (Cube Core, code-first, semantic layer for AI agents)
Sources9 · facts checked 9 October 2026
- GitHub: cube-js/cube README (Cube Core, data sources, dev mode, licences)
- Cube docs: Introduction (cubes and views, access control, pre-aggregations, SQL, REST, GraphQL and MCP)
- Cube docs: Pre-aggregations overview (in-memory cache, refresh keys and defaults)
- Cube docs: Getting started with pre-aggregations
- Cube docs: Using pre-aggregations (rollup-only mode, refresh keys, partitions, best practices)
- Cube docs: Cube Store architecture (Rust, Arrow, DataFusion, Parquet in S3, GCS or MinIO)
- Cube docs: Data sources (warehouses, query engines, databases; no direct file or API access)
- Cube docs: dbt integration (Premium and above plans)
- Cube docs: Scheduled refreshes of dashboards (Premium and Enterprise plans)