Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

Standard Schema V1

Standard Schema V1 compliance for interoperability with the validation ecosystem

What is Standard Schema?

Standard Schema is a universal interface for TypeScript validation libraries. Instead of each library having its own API, Standard Schema provides a common interface:

interface StandardSchemaV1<TInput, TOutput> {
  readonly "~standard": {
    readonly version: 1;
    readonly vendor: string;
    validate(value: unknown): { value: TOutput } | { issues: Issue[] };
  };
}

Why We Comply

Library Integration

Many tools support Standard Schema. VibORM schemas work with:

  • Form validation (React Hook Form, Formik)
  • API validation (tRPC, Hono)
  • AI tool calling (Vercel AI SDK)
  • OpenAPI generation

Interoperability

You can use VibORM schemas alongside Zod or Valibot if needed:

// Both implement the same interface
zodSchema["~standard"].validate(data);
vibSchema["~standard"].validate(data);

JSON Schema Export

VibORM also complies with the JSON Schema specification, allowing you to export schemas for documentation, API specs, or external validation:

const jsonSchema = schema["~standard"].jsonSchema.output({
  target: "draft-07",  // or "openapi-3.0"
});

Circular references are automatically converted to $ref entries.

Accessing Model Schemas

VibORM provides a getSchemas utility to easily access all validation schemas for your models:

import { getSchemas } from "viborm";

const schemas = getSchemas(mySchema);

// Access any model's schemas
schemas.user.core.where    // WHERE clause validation
schemas.user.core.create   // CREATE data validation
schemas.user.args.findMany // Full findMany args validation

Available Schemas

Each model exposes a comprehensive set of schemas:

Core Schemas

Schema Purpose
core.where Full WHERE clause with all operators
core.whereUnique WHERE clause for unique constraints
core.create Data for create operations
core.update Data for update operations
core.select Field selection
core.include Relation inclusion
core.orderBy Sorting configuration

Operation Args

Complete argument schemas for each operation:

Schema Operation
args.findUnique findUnique() arguments
args.findFirst findFirst() arguments
args.findMany findMany() arguments
args.create create() arguments
args.createMany createMany() arguments (including the optional select that makes it return rows)
args.update update() arguments
args.updateMany updateMany() arguments (including the optional select that makes it return rows)
args.delete delete() arguments
args.deleteMany deleteMany() arguments (including the optional select that makes it return rows)
args.upsert upsert() arguments
args.count count() arguments
args.aggregate aggregate() arguments
args.groupBy groupBy() arguments

Building Blocks (Advanced)

Lower-level schemas for custom composition:

schemas.user.core.scalarFilter              // Scalar filters
schemas.user.core.uniqueFilter              // Unique constraint filters
schemas.user.core.relationFilter            // Relation filters
schemas.user.core.compoundConstraintFilter  // Compound constraint filters
schemas.user.core.compoundIdFilter          // Compound ID filters

schemas.user.core.scalarCreate              // Scalar create
schemas.user.core.relationCreate            // Relation nested creates

schemas.user.core.scalarUpdate              // Scalar update
schemas.user.core.relationUpdate            // Relation nested updates

Recursive Schema Handling

The key challenge for Standard Schema compliance is recursive schemas. ORM models reference each other:

// User → Posts → User (circular)
const userSchema = v.object({
  posts: () => v.array(postSchema),  // Thunk
});

Thunks + Lazy Evaluation

VibORM uses thunks (functions that return schemas) combined with lazy evaluation:

  1. During schema construction, thunks are stored but NOT called
  2. During validation, thunks are called to resolve the actual schema
  3. TypeScript infers the return type of thunks at compile time

This approach was inspired by ArkType’s handling of recursive types.

Why Other Libraries Struggled

Library Issue with Recursion
Zod z.lazy() loses type information, returns any
Valibot lazy() wrapper evaluated eagerly, causing infinite loops
ArkType Good inference, but thunks evaluated during construction

VibORM’s approach: thunks are truly lazy - only called at validation time.

Was this page helpful?