Skip to content

API reference · Core

sql()

Compiles one SQL SELECT over your tables and datasets into a lazy query.

NodeBrowserEdge

sql() parses a SELECT statement, resolves its table names against the object you pass, and compiles it to the same lazy Query the JavaScript API builds, so SQL gets every optimization of the planner, on tables in memory and on datasets bigger than memory. Subqueries become joins, so a whole statement is one plan. The grammar and functions are listed in the SQL guide.

import { sql } from 'vectrojs';             // Node
import { sql } from 'vectrojs/browser';     // browsers
import { sql } from 'vectrojs/portable';    // edge runtimes

sql(text, tables?, options?)

Returns a Query; run it with toRows(), collect(), agg(), count() or their …Async twins, or inspect it with explain(). Parsing and compiling happen in this call, so a syntax error, an unknown table or column, or something not supported throws here, as a SyntaxError whose message starts with vectro SQL:.

tables maps names to Tables or Datasets: { orders, customers }. Table names are found ignoring case; a name may also be schema-qualified ("pg_catalog.pg_class"), and an unqualified name also finds a pg_catalog. one. Names in the SQL that aren’t plain identifiers go in double quotes.

Name Type Default Meaning
types boolean false Gives the query a columns property: [{ name, type }], type one of 'int', 'float', 'bool', 'text', 'date', 'day' (dates without a time). Worked out from the tables’ columns before anything runs.
foldCase boolean false Unquoted names in lower case, as PostgreSQL reads them.
functions object none More scalar functions: { name: { out, f } }, out 'text' or 'number', f(...values) a JavaScript function. It runs once per distinct combination of argument values, not per row; missing text arrives as null.
session object none Values for session functions such as version() and current_user.
database string none A schema name that refers to the tables themselves, as public does.

The servers in vectrojs/serve use types, foldCase, session and database to answer as PostgreSQL would; most programs need none of them.

SQL here only reads: there are no INSERT, UPDATE or DDL statements, and no ? or $1 parameters (the PostgreSQL server binds those for its clients). To use a value from your program, put it in the text with care or filter with the JavaScript API, whose params are bound safely.

import { Engine, sql } from 'vectrojs';

const engine = new Engine();
const orders = engine.table({
  cust: [1, 2, 1, 3, 2, 1],
  region: ['EU', 'US', 'EU', 'APAC', 'US', 'EU'],
  price: [120, 80, 45, 300, 99, 15],
  ts: ['2024-01-05', '2024-01-20', '2024-02-02', '2024-02-10', '2024-03-01', '2024-03-15'],
}, { dates: ['ts'] });
const customers = engine.table({ cust: [1, 2, 3], tier: ['gold', 'basic', 'gold'] });

const q = sql(`
  SELECT c.tier, DATE_TRUNC('month', o.ts) AS month, COUNT(*) AS n, SUM(o.price) AS revenue
  FROM orders o JOIN customers c ON o.cust = c.cust
  WHERE o.ts >= DATE '2024-01-01'
  GROUP BY 1, 2 ORDER BY month, revenue DESC`, { orders, customers });

console.log(q.toRows());
console.log(await sql('SELECT region FROM orders WHERE price > 100', { orders }).countAsync());
await engine.close();
import { Engine, sql } from 'vectrojs';

const engine = new Engine();
const sales = engine.table({ region: ['EU', 'US', 'EU', 'US', 'EU'], amount: [10, 20, 30, 40, 50] });

const ranked = sql(`
  SELECT region, amount,
         RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS r,
         SUM(amount) OVER (PARTITION BY region) AS region_total
  FROM sales ORDER BY region, r`, { sales }, { types: true });
console.log(ranked.columns);
console.log(ranked.toRows());

const shout = { out: 'text', f: (s) => (s === null ? null : `${s}!`) };
console.log(sql('SELECT shout(region) AS loud, MEDIAN(amount) AS mid FROM sales GROUP BY 1', { sales }, { functions: { shout } }).toRows());
await engine.close();