The ORM We're All Trying to Build
Prisma pioneered elegant APIs but requires code generation and WASM. Drizzle went lightweight but sacrificed elegance. VibORM combines both: Prisma's elegant API, zero code generation, no WASM — pure TypeScript.
The Great ORM Convergence
In 2020, Prisma pioneered object-based queries and relational APIs. Drizzle launched as the "anti-Prisma" — lightweight, SQL-first, no abstractions. Now in 2025, Drizzle's v2 adopts the exact patterns they once dismissed.This isn't copying. It's convergence. Production demands these patterns.
Prisma's Path
- →Elegant, readable schemas
- →Intuitive query API
- →Code generation required
- →WASM engine overhead
Beautiful DX, but heavy toolchain
Drizzle's Path
- →Verbose, SQL-like schemas
- →Relations defined separately
- →Callback-based → object-based
- →Zero code generation
Lightweight, but sacrificed elegance
VibORM
- ✓Prisma's elegant API ✓
- ✓Zero code generation ✓
- ✓No WASM, pure TypeScript ✓
- ✓Database-agnostic features ✓
The best of both worlds
What Production Actually Demands
These patterns aren't arbitrary preferences — they're optimal solutions that emerge when you tackle production-scale problems.
Complete object-based queries
Drizzle v2 adopted object-based patterns but only partially — missing nested selects on relations and other features. VibORM implements the full Prisma-like API.
"where is now object. orderBy is now object."— Drizzle v2 Migration Docs
Schema elegance isn't optional
Drizzle forces you to define relations separately from models. That's not just inconvenient — it's harder to understand and maintain. VibORM keeps relations inline, like Prisma.
"Your schema should be readable at a glance, not scattered across files."— Developer Experience
Typed JSON columns
Other ORMs treat JSON as 'any'. VibORM lets you define JSON schemas with Zod or Valibot — full type inference and runtime validation on your JSON data.
"JSON columns shouldn't be a type safety escape hatch."— Type Safety
No WASM, no complexity
Prisma bundles a Rust engine compiled to WASM — extra binaries, platform issues, slower cold starts. VibORM is pure TypeScript. Nothing to download, nothing to compile.
"The simplest architecture is the best architecture."— Engineering Wisdom
Elegance Matters
Drizzle requires relations to be defined separately from your models. VibORM keeps everything together — like Prisma, but in pure TypeScript.
// Tables defined here...
const users = pgTable("users", {
id: text("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name"),
});
const posts = pgTable("posts", {
id: text("id").primaryKey(),
title: text("title").notNull(),
authorId: text("author_id").references(() => users.id),
});
// ...but relations defined separately 😕
const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));// Everything in one place — clean & elegant ✨
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
name: s.string().nullable(),
posts: s.relation.oneToMany(() => post),
}).map("users");
const post = s.model({
id: s.string().id().ulid(),
title: s.string(),
authorId: s.string(),
author: s.relation
.fields("authorId")
.references("id")
.manyToOne(() => user),
}).map("posts");
// That's it. Relations are part of the model.
// Chainable. Readable. Elegant.✗ Drizzle's Approach
- • Relations defined in separate
relations()calls - • Schema scattered across multiple declarations
- • Harder to understand model structure at a glance
- • More boilerplate, less readable
✓ VibORM's Approach
- • Relations defined inline with the model
- • One model = one complete definition
- • Immediately see the full picture
- • Chainable, fluent, Prisma-like elegance
Your Schema Library, Native Support
Use Zod, Valibot, or ArkType to narrow field validation. Define typed JSON columns with full inference. No other ORM does this.
import { s } from "viborm";
import { z } from "zod";
// Define your JSON structure with Zod
const addressSchema = z.object({
street: z.string(),
city: z.string(),
zip: z.string(),
country: z.string(),
});
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
// Typed JSON column with full inference ✨
address: s.json(addressSchema),
// Nullable typed JSON
preferences: s.json(prefsSchema).nullable(),
}).map("users");
// TypeScript knows address is { street, city, zip, country }
// Runtime validation included — invalid JSON throwsimport { s } from "viborm";
import { z } from "zod";
import * as v from "valibot";
// Use Zod for email validation
const user = s.model({
id: s.string().id().ulid(),
email: s.string().validator(
z.string().email()
),
// Or use Valibot
phone: s.string().validator(
v.pipe(v.string(), v.regex(/^\+[0-9]{10,15}$/))
),
// Or ArkType
age: s.int().validator(type("number > 0 & < 150")),
}).map("users");
// Works with any Standard Schema compliant library
// Same API, your choice of validatorMost popular schema library
Lightweight alternative
Fastest runtime validation
JSON Columns, Finally Type-Safe
Other ORMs treat JSON as any or unknown. VibORM lets you define the exact shape with your favorite schema library — full type inference in queries, runtime validation on writes. No more JSON escape hatches.
Queries That Read Like English
Prisma's query API is beloved for a reason — it's intuitive. VibORM brings that same elegance without the code generation.
// Find admins with their published posts
const admins = await client.user.findMany({
where: { role: "ADMIN" },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
},
},
});
// Create a user with posts in one call
const newUser = await client.user.create({
data: {
email: "alice@example.com",
name: "Alice",
posts: {
create: [
{ title: "Hello World" },
{ title: "Second Post" },
],
},
},
include: { posts: true },
});
// TypeScript knows the exact return type
// No generation. No guessing. Just inference.Simpler Stack, Better Performance
No prisma generate. No WASM engine. No binary downloads. Just pure TypeScript that runs anywhere Node.js runs — faster cold starts, simpler deployments, and the complete Prisma-like query API.
The Full Picture
Prisma's elegance. Drizzle's zero-generation. VibORM combines both — without the compromises.
| Feature | Prisma | Drizzle v1 | Drizzle v2 | VibORM |
|---|---|---|---|---|
| Object-based queries | ✓ | ✗ | Partial | Complete |
| Zero code generation | ✗ | ✓ | ✓ | ✓ |
| Pure TypeScript (no WASM) | ✗ | ✓ | ✓ | ✓ |
| Full type inference | Generated | Partial | Partial | Full |
| Relational queries | ✓ | Limited | ✓ | ✓ |
| Typed JSON columns | ✗ | ✗ | ✗ | ✓ |
| Standard Schema integration | ✗ | ✗ | ✗ | Zod/Valibot/ArkType |
| Exported model schemas | ✗ | ✗ | ✗ | ArkType |
| TypeScript schema | PSL file | ✓ | ✓ | ✓ |
| Relations inline | ✓ | ✗ | ✗ | ✓ |
| Filtering by relations | ✓ | ✗ | ✓ | ✓ |
| Nested select on relations | ✓ | ✗ | Partial | ✓ |
| Scalar arrays on MySQL | ✗ | ✗ | ✗ | ✓ |
| DISTINCT on all databases | Partial | DB-dependent | DB-dependent | ✓ |
| Consistent feature set | DB-dependent | DB-dependent | DB-dependent | Always |
Prisma has elegance but requires generation and WASM. Drizzle is lightweight but sacrificed elegance. VibORM gives you both — beautiful queries, pure TypeScript, no build step.
One API. Every Database. Every Feature.
Other ORMs give you different features depending on your database. VibORM abstracts the limitations away — your code works the same whether you're on PostgreSQL, MySQL, or SQLite.
Scalar Arrays
MySQL doesn't support array columns natively. VibORM emulates them with JSON — same API, same types, any database.
DISTINCT Queries
DISTINCT ON isn't available everywhere. VibORM provides consistent distinct behavior across all supported databases.
Switch Anytime
Start with SQLite for development, deploy to PostgreSQL. Your queries don't change — the ORM handles the translation.
The Real Abstraction
Most ORMs abstract SQL syntax — you still hit database limitations. VibORM abstracts database capabilities. Features that don't exist natively are emulated transparently. You get the same powerful API everywhere.
Features That Survived Convergence
These aren't arbitrary preferences. They're the patterns that every production ORM eventually implements.
Prisma's Elegance
Chainable schemas. Relations defined inline. Queries that read like English. The beautiful DX Prisma pioneered — in pure TypeScript.
Relations Done Right
Defined inline, not scattered. include, nested select, relation filters — Prisma's complete relational API without the separate relations() boilerplate.
Full Type Safety
Every query, filter, and result is typed. No any, no guessing, no runtime surprises.
Pure TypeScript
No code generation, no WASM engine, no binary downloads. Just TypeScript — simpler workflow, faster cold starts, works everywhere.
Exported Model Schemas
ArkType schemas auto-generated from your models. Use them for API validation, form validation — anywhere you need runtime checks outside the ORM.
Standard Schema Integration
Use Zod, Valibot, or ArkType to narrow field validation and type JSON columns. Your favorite schema library, native support.
True Database Abstraction
Scalar arrays on MySQL. DISTINCT on SQLite. Every feature works everywhere — the ORM abstracts database limitations, not just syntax.
Skip the Convergence. Start at the Destination.
Why wait for other ORMs to implement what you need? VibORM has the patterns that production demands — today.