Vex · guides
How to do things with Vex.
Each guide is a file you can run. Most of these ask a model, so this site checks their types and does not run them, and their pages say what comes back. Those that need no model are run, and their pages show what they printed.
First steps.
A Postgres starts in the process, one read is stored under a name, and the file asks for it by that name with a night as its value.
pnpm add @niscorp/vex @niscorp/prism @niscorp/strata zod @electric-sql/pglitenpx tsx first.mtsimport { PGlite } from '@electric-sql/pglite';
import { createPostgresAdapter, createQueryEngine, seedCache } from '@niscorp/vex';
import { createPglitePool } from '@niscorp/vex/pglite';
const db = new PGlite();
await db.exec(`
CREATE TABLE bookings (id text PRIMARY KEY, night text, member text, seats integer);
INSERT INTO bookings VALUES
('B-2040', '2026-10-24', 'Ada', 1),
('B-2041', '2026-10-24', 'Grace', 2),
('B-2043', '2026-10-25', 'Alan', 3);
`);
const engine = createQueryEngine({ adapter: createPostgresAdapter({ pool: createPglitePool(db) }) });
await engine.introspect();
await seedCache(engine.cache, [{
fingerprint: 'boxoffice/coming',
intent: 'Who is coming on a given night, and how many seats each has',
shape: [{ member: '', seats: 0 }],
dsl: {
from: ['bookings'],
fields: ['bookings.member', 'bookings.seats'],
filter: { eq: ['bookings.night', { $context: 'night' }] },
sort: [{ field: 'bookings.member', dir: 'asc' }],
},
}]);
const answer = await engine.execute({ fingerprint: 'boxoffice/coming', context: { night: '2026-10-24' } });
console.log(answer.result);[ { member: 'Ada', seats: 1 }, { member: 'Grace', seats: 2 } ]Nothing here asks a model: the read was seeded. In an app Vex is given a generateDsl hook, a model writes a read once from an intent and a shape, and from then on it is replayed by name, as here. On PGlite Vex warns once that it cannot set a statement timeout.
Then, one at a time.
Each is a file of its own and adds one thing. Read them in order, or go to the one you need.