A small, zero-dependency SQLite query builder for Bun with Prisma-style migrations. Supports local SQLite, Cloudflare D1, and Turso.
Most Effective Query Builder and migration manager SQLite supporting D1/Turso and Bun's by default.
v2 now a full sqlite only orm, enjoy and support & contribute!
bun add sqlitebruvDefine your schema in ./bruv/schema.prisma:
modelUser {idString@id@default(uuid())emailString@uniquenameString?ageInt?}modelPost {idString@id@default(uuid())titleStringuserIdString}Initialize the database. Schema is auto-loaded from ./bruv/schema.prisma:
import{SqliteBruv}from"sqlitebruv";constdb=newSqliteBruv({localFile: "./app.db",});Query:
awaitdb.from("users").insert({name: "friday",email: "f@dev.io"});constusers=awaitdb.from("users").where("age > ?",18).get();constone=awaitdb.from("users").where("id = ?",id).getOne();// Cloudflare D1constdb=newSqliteBruv({D1Config: {accountId: process.env.CFAccountId,databaseId: process.env.D1databaseId,apiKey: process.env.CFauthorizationToken,},});// Tursoconstdb=newSqliteBruv({TursoConfig: {url: process.env.TURSO_URL,authToken: process.env.TURSO_AUTH_TOKEN,},});// Local SQLite (default)constdb=newSqliteBruv({localFile: "./app.db",});// Selectdb.from("users").select("id","name").get();// Where chainsdb.from("users").where("age > ?",18).andWhere("country = ?","NG").orWhere("role = ?","admin").get();// Paginationdb.from("users").orderBy("name","ASC").limit(10).offset(20).get();// Single recorddb.from("users").where("id = ?",id).getOne();// Countdb.from("users").where("active = ?",true).count();// Insert (id is auto-generated)db.from("users").insert({name: "friday",email: "f@dev.io"});// Updatedb.from("users").where("id = ?",id).update({name: "saturday"});// Deletedb.from("users").where("id = ?",id).delete();// Raw SQLdb.raw("SELECT * FROM users WHERE id = ?",[id]);constusers=awaitdb.from("users").get({cacheAs: "all-users"});// Laterdb.invalidateCache("all-users");Prisma-style CLI. Schema source of truth is ./bruv/schema.prisma. Migrations live in ./bruv/migrations/.
# Generate + apply migration (uses local dev.db)
npx bruv-cli migrate dev --name add_age_column
# Apply pending migrations to production DB (reads env vars)
npx bruv-cli migrate deploy
# Rollback last migration
npx bruv-cli migrate reset
# Check status
npx bruv-cli migrate status
# Push schema directly, no migration file
npx bruv-cli db pushConnection is resolved from environment variables:
| Env vars | Target |
|---|---|
TURSO_URL + TURSO_AUTH_TOKEN | Turso |
CFAccountId + D1databaseId + CFauthorizationToken | D1 |
DB_FILE | Local file path |
| (none) | ./main.db |
migrate dev always targets ./bruv/dev.db for local iteration.
Migration files use -- --> up / -- --> down markers and run inside transactions:
-- --> upCREATETABLEIF NOT EXISTS users (
id textPRIMARY KEYNOT NULL,
email TEXT UNIQUE NOT NULL,
name TEXT
);
-- --> downDROPTABLE users;For exposing queries over HTTP without writing per-route SQL:
constresult=awaitdb.executeJsonQuery({from: "users",action: "get",where: [{condition: "age > ?",params: [18]}],orderBy: {column: "name",direction: "ASC"},limit: 10,});Actions: get, getOne, insert, update, delete, count.
The query builder rejects dangerous input at the condition level:
- Parameterized queries only — no string interpolation
- Blocked patterns:
; DROP,UNION,DELETE,INSERT,UPDATE,ALTER,EXEC - Whitelisted operators:
=,>,<,>=,<=,LIKE,IN,BETWEEN,IS NULL,IS NOT NULL - Max 100 params per query, max 1000 chars per string param
raw() bypasses all validation — use it for migrations and admin queries only.
// ✅ Safe queriesdb.from("users").where("email LIKE ?","%@example.com")// ✅ Safe.andWhere("role = ?","admin")// ✅ Safe.get();db.from("users").where("age > ?",18).andWhere("status = ?","active").orWhere("role IN (?)",["admin","mod"]);// ❌ These will throw security errors:db.where("1=1; DROP TABLE users;");// Dangerous patterndb.where("col = (SELECT ...)");// Complex subqueries blockeddb.where("name = ?","a".repeat(1001));// String too long- Fork
- Branch (
git checkout -b feature/your thing) - Commit
- PR
MIT