Quick Start
Get Zerobase running in under a minute. No server, no config, no database engine — just SQL over plain JSON files.
Installation
$ npm install zerobase-cli # or globally $ npm install -g zerobase-cli
Initialize
Run inside your project directory. Creates ./storage/ with an empty
schema.json. Safe to run multiple times.
$ zerobase init ✓ Zerobase initialized → ./storage/ › Open the SQL shell: zerobase query › Or use in Node.js: const db = require('zerobase-cli')
Your first query
$ zerobase query zerobase› CREATE TABLE users (id INT PRIMARY KEY, name TEXT, age INT); ✓ Table users created (3 columns, 2ms) zerobase› INSERT INTO users (name, age) VALUES ('Kunal', 20); ✓ 1 row inserted into users · id: 1 (1ms) zerobase› SELECT * FROM users; ┌────┬───────┬─────┐ │ id │ name │ age │ ├────┼───────┼─────┤ │ 1 │ Kunal │ 20 │ └────┴───────┴─────┘ ✓ 1 row(s) returned · 1ms
CLI Commands
The Zerobase CLI gives you a full set of commands for managing storage and running SQL from any terminal.
Initialize storage in the current directory. Creates ./storage/
and schema.json.
Open the interactive SQL shell with ↑↓ history, multi-line
buffering, and dot-commands.
List all tables with column count and primary key info.
Show column names, types, constraints, and the current row count for a table.
Drop a table and permanently delete its JSON file. Equivalent to DROP TABLE in SQL.
Shell meta-commands
Inside the zerobase query shell, these commands work without SQL syntax:
| Command | Description |
|---|---|
.tables |
List all tables |
.describe <table> |
Show schema and row count |
.drop <table> |
Drop a table |
.history |
Show last 20 queries from history file |
.help |
Show all available commands |
.exit |
Exit the shell (also: exit, quit) |
Query History
The shell persists your query history to ~/.zerobase_history (up to 100
entries). Use ↑ and ↓ arrow keys to navigate. Run .history to see recent entries inline.
SQL Reference
A precise, complete subset of SQL. Every statement is parsed by a regex-based engine — lightweight, fast, and fully auditable.
CREATE TABLE
Define a new table with typed columns. Declare one column as PRIMARY KEY
and Zerobase will auto-increment it on every insert.
CREATE TABLE tableName ( colName TYPE [PRIMARY KEY], colName TYPE ); -- Concrete example CREATE TABLE products ( id INT PRIMARY KEY, name TEXT, price FLOAT, active BOOL );
./storage/products.json (empty array) and registers the schema in schema.json.DROP TABLE
Permanently removes the table's schema entry and deletes its JSON data file.
DROP TABLE products;
INSERT INTO
Insert a row. Values are matched positionally to columns. String values must be single-quoted. Omit the primary key column — Zerobase assigns the next integer automatically.
INSERT INTO tableName (col1, col2) VALUES (val1, val2); -- Strings must be single-quoted INSERT INTO users (name, age) VALUES ('Kunal', 20); -- id auto-assigned → 1 -- Multiple types INSERT INTO products (name, price, active) VALUES ('Widget', 9.99, 1);
SELECT
Query rows from a table. Supports column projection, WHERE conditions, ORDER BY, LIMIT, and aggregate functions.
-- All columns SELECT * FROM users; -- Specific columns SELECT name, age FROM users; -- Column alias SELECT name AS full_name FROM users;
WHERE & Logic
Filter rows with one or more conditions. Chain with AND or OR.
-- Single condition SELECT * FROM users WHERE age > 18; -- AND SELECT * FROM users WHERE age > 18 AND name = 'Kunal'; -- OR SELECT * FROM users WHERE age < 18 OR age > 65; -- Supported operators: = != > < >= <=
ORDER BY & LIMIT
SELECT * FROM products ORDER BY price DESC; SELECT * FROM users ORDER BY name ASC LIMIT 10; -- Combined with WHERE SELECT * FROM products WHERE active = 1 ORDER BY price DESC LIMIT 5;
Aggregates
Compute statistics across filtered rows. Combine multiple aggregates in one SELECT. Use AS for readable column names in results.
SELECT COUNT(*) FROM users; SELECT SUM(price) FROM products; SELECT MIN(age), MAX(age), AVG(age) FROM users; -- Aliases + WHERE SELECT COUNT(*) AS total, AVG(price) AS avg_price, MAX(price) AS highest FROM products WHERE active = 1;
UPDATE
Update columns on matching rows. WHERE is required — Zerobase rejects UPDATE without a condition to prevent accidental full-table overwrites.
UPDATE users SET age = 25 WHERE id = 1; -- Multiple columns UPDATE users SET age = 25, name = 'Kunal V' WHERE id = 1;
DELETE
Remove matching rows. Without WHERE, all rows are deleted (the table and its schema remain).
DELETE FROM users WHERE id = 3; -- Clear all rows (table stays) DELETE FROM users;
Node.js SDK
Use Zerobase directly in your app with require('zerobase-cli'). The
SDK runs the same SQL engine as the CLI — same storage, same results.
zerobase init has been run in your project root before using the SDK.
The SDK reads from ./storage/ relative to wherever Node.js is invoked.db.query(sql)
Runs any SQL statement and returns a Promise resolving to a result object. The shape of the result depends on the query type.
const db = require('zerobase-cli'); // INSERT → { message, inserted, primaryKey } const r1 = await db.query("INSERT INTO users (name, age) VALUES ('Priya', 22)"); console.log(r1.inserted); // { id: 2, name: 'Priya', age: 22 } // SELECT → { rows: Array, count: number } const r2 = await db.query("SELECT * FROM users WHERE age > 18"); console.log(r2.rows); // [{ id:1, name:'Kunal', age:20 }, ...] console.log(r2.count); // 2 // UPDATE → { message, updatedCount } const r3 = await db.query("UPDATE users SET age = 23 WHERE id = 1"); console.log(r3.updatedCount); // 1 // DELETE → { message, deletedCount } const r4 = await db.query("DELETE FROM users WHERE id = 3"); console.log(r4.deletedCount); // 1
db.select(sql)
Shorthand for SELECT queries — returns the rows array directly instead of a result wrapper object.
// Returns Array directly (not { rows, count }) const rows = await db.select("SELECT * FROM users ORDER BY age DESC LIMIT 5"); console.log(rows[0].name); // 'Priya'
Reference
Data Types
| SQL Type | JS Type | Notes |
|---|---|---|
| INT | number |
Integers. Good for IDs, ages, counts. |
| FLOAT | number |
Decimal values. Good for prices, ratios. |
| TEXT | string |
Any string. Must be single-quoted in SQL. |
| BOOL | boolean |
Use 1 / 0 in SQL. Stored as true/false in JSON. |
WHERE Operators
| Op | Meaning | Works with |
|---|---|---|
= |
Equal to | INT, FLOAT, TEXT, BOOL |
!= |
Not equal to | INT, FLOAT, TEXT, BOOL |
> |
Greater than | INT, FLOAT |
< |
Less than | INT, FLOAT |
>= |
Greater than or equal | INT, FLOAT |
<= |
Less than or equal | INT, FLOAT |
Storage Format
All data lives in ./storage/ relative to your project root. Zerobase
finds this folder by walking up from the current directory — so zerobase query works
from any subdirectory, like src/routes/ or tests/.
./storage/ schema.json ← table definitions + column types users.json ← row data for "users" products.json ← row data for "products"
{
"users": {
"columns": {
"id": "number",
"name": "string",
"age": "number"
},
"primaryKey": "id"
}
}
Error Reference
Every error includes the problem, expected format, and a working example.
Hint: Did you mean: DROP TABLE tableName;
Available columns: id, name, age
Hint: Numbers should not be quoted — use 20, not '20'.
Example: UPDATE users SET age = 25 WHERE id = 1;
Limitations
- No JOIN — queries are single-table only
- No concurrent write safety — one process at a time
- No transactions or rollbacks
- No nested WHERE groups — AND/OR chains only
- No subqueries or nested SELECT
- No GROUP BY — use WHERE to pre-filter then aggregate
- No indexes — queries scan the full JSON array linearly
- Practical limit ~10,000 rows per table before performance degrades