VibORM
Fields

JSON Field

JSON field type for structured data — untyped or typed with a StandardSchema validator

Basic Usage

import { s } from "viborm";

s.json(); // Required JSON (unknown type)
s.json().nullable(); // JSON | null

Typed JSON

Provide a StandardSchema (Valibot, Zod, etc.) for type-safe JSON using the .schema() method:

import { object, enum_, boolean, string } from "valibot";

const settingsSchema = object({
  theme: enum_(["light", "dark"]),
  notifications: boolean(),
  language: string(),
});

s.json().schema(settingsSchema); // Typed as { theme: "light" | "dark"; ... }

Chainable Methods

s.json()
  .nullable() // Allow NULL
  .schema(schema) // Custom StandardSchema validator
  .default({}) // Static default
  .default(() => ({})) // Runtime default
  .map("column_name"); // Custom column name

Native Database Types

import { TYPES } from "viborm";

s.json(TYPES.PG.JSON.JSON) // JSON (text-based)
s.json(TYPES.PG.JSON.JSONB) // JSONB (binary, indexable, default)
import { TYPES } from "viborm";

s.json(TYPES.MYSQL.JSON.JSON)  // JSON
import { TYPES } from "viborm";

s.json(TYPES.SQLITE.JSON.TEXT) // TEXT (stored as string)

Type Mapping

ModifierTypeScriptPostgreSQLMySQLSQLite
s.json()unknownJSONBJSONTEXT
.schema(schema)InferredJSONBJSONTEXT
.nullable()T | nullJSONB NULLJSON NULLTEXT NULL

Note: JSON fields do not support .array() modifier or shorthand syntax for updates/filters. You must use explicit operations like { set: value } and { equals: value }.

Examples

Untyped JSON

const metadata = s.json().default({});
const tags = s.json().default([]);

Typed with Valibot

import {
  object,
  enum_,
  number,
  boolean,
  string,
  array,
  optional,
  pipe,
  minValue,
  maxValue,
} from "valibot";

// User preferences
const preferencesSchema = object({
  theme: enum_(["light", "dark", "system"]),
  fontSize: pipe(number(), minValue(10), maxValue(24)),
  notifications: object({
    email: boolean(),
    push: boolean(),
  }),
});

const preferences = s
  .json()
  .schema(preferencesSchema)
  .default({
    theme: "system",
    fontSize: 14,
    notifications: { email: true, push: false },
  });

// Address structure
const addressSchema = object({
  street: string(),
  city: string(),
  country: string(),
  postalCode: string(),
});

const address = s.json().schema(addressSchema).nullable();

Complex Nested Types

import {
  object,
  string,
  number,
  array,
  optional,
  pipe,
  minValue,
  maxValue,
  integer,
  positive,
} from "valibot";

const orderItemSchema = object({
  productId: string(),
  quantity: pipe(integer(), positive()),
  price: pipe(number(), positive()),
  discount: optional(pipe(number(), minValue(0), maxValue(1))),
});

const orderSchema = object({
  items: array(orderItemSchema),
  shipping: addressSchema,
  notes: optional(string()),
});

const orderData = s.json().schema(orderSchema);

Querying JSON

JSON fields support path-based filtering. Note: JSON fields do not support shorthand syntax. You must use explicit operations:

// Filter by nested value (must use { equals: value })
await client.user.findMany({
  where: {
    preferences: {
      path: ["theme"],
      equals: "dark",
    },
  },
});

// String operations on JSON strings
await client.user.findMany({
  where: {
    metadata: {
      string_contains: "premium",
    },
  },
});

// Array operations
await client.user.findMany({
  where: {
    tags: {
      array_contains: "featured",
    },
  },
});

// Updates must use { set: value }
await client.user.update({
  where: { id: "user_123" },
  data: {
    preferences: {
      set: { theme: "dark", fontSize: 16 },
    },
  },
});

JSON vs Separate Fields

ApproachProsCons
JSON fieldFlexible, no migrationsHarder to query, no constraints
Separate fieldsType-safe queries, indexesSchema changes need migrations
Related tableFull SQL powerMore complex queries

Use JSON for:

  • User preferences
  • Metadata that varies per record
  • Third-party API responses
  • Audit logs

Use separate fields for:

  • Data you need to filter/sort by
  • Data with strict validation
  • Data referenced in relations

On this page