JavaScript / Node
Install from npm. A prebuilt binary for your platform is pulled in as an optional dependency, so there’s no build step:
npm install sqlite-predictIt loads into either the built-in node:sqlite or better-sqlite3, anything with
a loadExtension method:
import * as sqlitePredict from "sqlite-predict";import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync(":memory:", { allowExtension: true });sqlitePredict.load(db);
// did it load?console.log(db.prepare("select predict_version()").get());
db.exec("create table readings(ts text, value real)");const ins = db.prepare("insert into readings values (?, ?)");for (let h = 0; h < 24; h++) { ins.run(`2024-01-01T${String(h).padStart(2, "0")}:00:00`, 50 + h);}
// forecast() is an aggregate like sum(): your statement supplies the// rows, and each group returns one JSON documentconst { doc } = db .prepare("select forecast(ts, value, 6) as doc from readings") .get();console.log(JSON.parse(doc).rows[0]);// { step, forecast_timestamp, forecast, lower_bound, upper_bound }Because it is an aggregate, WHERE, joins, and bound parameters compose, and
GROUP BY city returns one document per city.
Drizzle
Section titled “Drizzle”The same call through the query builder, with a bound parameter and no SQL strings (this is the code the CI smoke test runs):
import Database from "better-sqlite3";import { drizzle } from "drizzle-orm/better-sqlite3";import { sql, eq } from "drizzle-orm";import { sqliteTable, text, real } from "drizzle-orm/sqlite-core";import * as sp from "sqlite-predict";
const sqlite = new Database("app.db");sqlite.loadExtension(sp.getLoadablePath());const db = drizzle(sqlite);
const readings = sqliteTable("readings", { city: text("city"), ts: text("ts"), value: real("value"),});
const out = db .select({ city: readings.city, doc: sql`forecast(${readings.ts}, ${readings.value}, 24)`, }) .from(readings) .where(eq(readings.city, "SF")) // bound parameter, no string SQL .groupBy(readings.city) .all();
const { rows } = JSON.parse(out[0].doc);If you use drizzle-kit, exclude sqlite-predict’s model registry from schema diffs (it lives in your database, and diff tools will otherwise try to drop it):
export default { /* … */ tablesFilter: ["!_predict_*"] };Prisma: the default engine never exposes the raw connection, so extensions
can’t load. Use a driver adapter
(better-sqlite3 or libSQL) and load on the underlying connection as above; and
since _predict_* tables are not in your Prisma schema, prefer migrate diff
reviews over auto-apply so drift detection doesn’t drop them.
With node:sqlite you need --experimental-sqlite on Node 22, or a recent Node
where it is stable. sqlitePredict.getLoadablePath() returns the binary path if
you load it yourself.
Next: Operations.