VibORM

L9 - Client

The orm.model.operation() interface providing type-safe queries with zero code generation

Location: src/client/

Why This Layer Exists

VibORM's type safety comes from inferring types from schema definitions. The client layer makes this work:

const orm = client(schema);

// Fully typed - no codegen!
const users = await orm.user.findMany({
  where: { email: { contains: "@example.com" } },
  select: { id: true, email: true, posts: { select: { title: true } } }
});
// Type: Array<{ id: string; email: string; posts: Array<{ title: string }> }>

How It Works

Recursive Proxy Pattern

The client uses JavaScript Proxy to handle dynamic model access:

orm.user     // Proxy intercepts "user" property access
   .findMany // Proxy intercepts "findMany" method access
   (args)    // Calls actual findMany implementation

This enables orm.<anyModel>.<anyOperation> without defining every combination.

Type Inference Chain

Types flow through a chain:

Schema Definition

Model extracts field types

Client infers model names from schema

Operations infer args/return from model

Select/Include modifies return type

No step uses code generation - TypeScript infers at each level.

Select/Include Aware Results

Return types change based on what you select:

// Full model (all fields)
orm.user.findFirst({ where: { id: "1" } });
// Type: { id, email, name, createdAt, ... } | null

// Selected fields only
orm.user.findFirst({ 
  where: { id: "1" },
  select: { id: true, email: true } 
});
// Type: { id, email } | null

// With relations
orm.user.findFirst({
  where: { id: "1" },
  include: { posts: true }
});
// Type: { id, email, ..., posts: Post[] } | null

Available Operations

OperationDescription
findManyGet multiple records
findFirstGet first matching record
findUniqueGet record by unique constraint
createCreate single record
createManyCreate multiple records
updateUpdate matching records
updateManyUpdate multiple records
deleteDelete matching records
deleteManyDelete multiple records
countCount matching records
aggregateAggregate functions (SUM, AVG, etc.)

Why Zero Codegen?

Code generation ORMs (like Prisma) require a build step:

prisma generate  # Must run after schema changes

VibORM infers types directly from your schema definition:

const schema = s.schema({
  user: s.model({ ... }),
  post: s.model({ ... }),
});

const orm = client(schema);  // Types inferred immediately

Benefits:

  • Instant feedback: Types update on save
  • No build step: Simpler CI/CD
  • No generated files: Cleaner repository

Connection to Other Layers

  • L2-L4 (Schema): Client infers types from schema definitions
  • L3 (Query Schemas): Client validates args at runtime
  • L6 (Query Engine): Client routes operations to query engine
  • L8 (Drivers): Query results flow back through client

On this page