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 implementationThis 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 typeNo 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[] } | nullAvailable Operations
| Operation | Description |
|---|---|
findMany | Get multiple records |
findFirst | Get first matching record |
findUnique | Get record by unique constraint |
create | Create single record |
createMany | Create multiple records |
update | Update matching records |
updateMany | Update multiple records |
delete | Delete matching records |
deleteMany | Delete multiple records |
count | Count matching records |
aggregate | Aggregate functions (SUM, AVG, etc.) |
Why Zero Codegen?
Code generation ORMs (like Prisma) require a build step:
prisma generate # Must run after schema changesVibORM infers types directly from your schema definition:
const schema = s.schema({
user: s.model({ ... }),
post: s.model({ ... }),
});
const orm = client(schema); // Types inferred immediatelyBenefits:
- 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