Introduction
One Prisma-shaped query API. Six databases. No codegen.
A small, Prisma-shaped data layer for MongoDB, PostgreSQL, MySQL, SQLite, DuckDB and SQL Server. You write your models once in plain TypeScript and the same query code runs against any of the six databases. There is no code generation step, no Rust query engine, and no framework to adopt — just readable TypeScript over the official drivers, organised one adapter per database.
npm install forge-orm📖 The full documentation reads far better as a website: johnsonfash.github.io/forge-orm — same content, with a sidebar, search, and one page per topic instead of three thousand lines of scroll.
| 🐘 PostgreSQL | 🐬 MySQL / MariaDB | 🪶 SQLite |
| 🍃 MongoDB | 🦆 DuckDB | 🟦 SQL Server |
| 🌐 Browser (sqlite-wasm) | 💾 Browser (IndexedDB) | ⚡ PGlite (embedded PG) |
import { createDb, f, model } from 'forge-orm';
const User = model('users', {
id: f.id(),
email: f.string().unique(),
name: f.string(),
});
const db = await createDb({ url: process.env.DATABASE_URL!, schema: { user: User } });
const alice = await db.user.create({ data: { email: 'a@x.co', name: 'Alice' } }); // no id needed
const users = await db.user.findMany({ where: { name: { contains: 'Ali' } }, take: 10 });The same code works whether DATABASE_URL is a Postgres, MySQL, SQLite, DuckDB, SQL Server, or Mongo connection string. forge picks the right driver from the URL prefix (postgres:, pglite:, mysql:, sqlite:, duckdb:, mssql:, mongodb:).
Beyond the basics, forge ships first-class typed support for the things you usually have to drop to raw SQL for:
- Geo —
f.geoPoint()+near/nearTo/withinPolygon, compiling to PostGIS / MySQL spatial / SpatiaLite / DuckDB spatial / MSSQLGEOGRAPHY/ Mongo2dsphere. App-side Haversine fallback when no spatial extension is installed. - Vector similarity —
f.vector(1536, { metric: 'cosine' })+ the samenear/nearTovocabulary, compiling to pgvector / DuckDB vss HNSW / MSSQLVECTOR_DISTANCE/ MySQL 9DISTANCE/ sqlite-vec / Mongo Atlas$vectorSearch. - JSON path queries —
where: { meta: { path: 'profile.age', gte: 18 } }on anyf.json()/f.embed()/f.embedMany()/ array column, compiling to PG->/->>, MySQLJSON_EXTRACT, SQLite / DuckDBjson_extract, MSSQLJSON_VALUE, Mongo dotted-key form. - Full-text search —
f.text().searchable()builds the right index per dialect (Postgres GIN tsvector, MySQLFULLTEXT, SQLite FTS5 with shadow-table triggers, Mongotext, DuckDBfts) and thesearchoperator queries it.