Skip to main content

Overview

Zero provides five built-in column types for defining table schemas. Each type maps to both TypeScript types and PostgreSQL storage types.

Column Type Functions

string()

Defines a string column.
function string<T extends string = string>(): ColumnBuilder
Type Parameter:
  • T - (Optional) A custom TypeScript string literal type or union
Returns: A ColumnBuilder for a string column Example:
const user = table('user')
  .columns({
    id: string(),
    name: string(),
    email: string(),
  })
  .primaryKey('id');

number()

Defines a numeric column.
function number<T extends number = number>(): ColumnBuilder
Type Parameter:
  • T - (Optional) A custom TypeScript number type
Returns: A ColumnBuilder for a number column Example:
const issue = table('issue')
  .columns({
    id: string(),
    shortID: number(),
    modified: number(),
    created: number(),
  })
  .primaryKey('id');

boolean()

Defines a boolean column.
function boolean<T extends boolean = boolean>(): ColumnBuilder
Type Parameter:
  • T - (Optional) A custom TypeScript boolean type
Returns: A ColumnBuilder for a boolean column Example:
const issue = table('issue')
  .columns({
    id: string(),
    open: boolean(),
    archived: boolean(),
  })
  .primaryKey('id');

json()

Defines a JSON column for storing structured data.
function json<T extends ReadonlyJSONValue = ReadonlyJSONValue>(): ColumnBuilder
Type Parameter:
  • T - (Optional) A custom TypeScript type for the JSON structure
Returns: A ColumnBuilder for a JSON column Example:
interface UserPreferences {
  theme: 'light' | 'dark';
  notifications: boolean;
}

const user = table('user')
  .columns({
    id: string(),
    preferences: json<UserPreferences>(),
  })
  .primaryKey('id');

enumeration()

Defines a string column with TypeScript enum-like type constraints.
function enumeration<T extends string>(): ColumnBuilder
Type Parameter:
  • T - A TypeScript string literal type or union (required)
Returns: A ColumnBuilder for an enumeration column Example:
type Role = 'admin' | 'member' | 'guest';

const user = table('user')
  .columns({
    id: string(),
    role: enumeration<Role>(),
  })
  .primaryKey('id');

type Visibility = 'internal' | 'public';

const issue = table('issue')
  .columns({
    id: string(),
    visibility: enumeration<Visibility>(),
  })
  .primaryKey('id');

ColumnBuilder Methods

optional()

Marks a column as optional (nullable).
optional(): ColumnBuilder
Returns: The column builder with optional flag set Example:
const user = table('user')
  .columns({
    id: string(),
    name: string().optional(),
    email: string(),
    avatar: string().optional(),
  })
  .primaryKey('id');

const issue = table('issue')
  .columns({
    id: string(),
    shortID: number().optional(),
    assigneeID: string().optional(),
  })
  .primaryKey('id');

from()

Maps the client-side column name to a different server-side column name.
from<ServerName extends string>(serverName: ServerName): ColumnBuilder
Parameters:
  • serverName - The server-side column name in PostgreSQL
Returns: The column builder with server name configured Example:
const user = table('user')
  .columns({
    id: string().from('user_id'),
    email: string().from('email_address'),
  })
  .primaryKey('id');

Column Type Namespace

All column types are also available via the column namespace:
import { column } from '@rocicorp/zero';

const user = table('user')
  .columns({
    id: column.string(),
    age: column.number(),
    active: column.boolean(),
    metadata: column.json(),
    role: column.enumeration<Role>(),
  })
  .primaryKey('id');

Type Mapping

Zero TypeTypeScript TypePostgreSQL Type
string()stringTEXT, VARCHAR, CHAR
number()numberINTEGER, BIGINT, NUMERIC, FLOAT
boolean()booleanBOOLEAN
json()ReadonlyJSONValueJSON, JSONB
enumeration<T>()T extends stringTEXT, VARCHAR

Custom Types

You can provide custom TypeScript types to any column for better type safety:
// String literal unions
type Status = 'pending' | 'approved' | 'rejected';
const status = enumeration<Status>();

// Branded types
type UserID = string & { __brand: 'UserID' };
const userId = string<UserID>();

// Complex JSON structures
interface Metadata {
  tags: string[];
  properties: Record<string, unknown>;
}
const metadata = json<Metadata>();

Complete Example

Here’s a comprehensive example showing all column types:
import {
  table,
  string,
  number,
  boolean,
  json,
  enumeration,
} from '@rocicorp/zero';

type Role = 'admin' | 'member' | 'guest';
type Status = 'active' | 'inactive' | 'suspended';

interface Settings {
  notifications: boolean;
  theme: 'light' | 'dark';
}

const user = table('user')
  .columns({
    // String columns
    id: string(),
    email: string(),
    name: string().optional(),
    
    // Number columns
    age: number().optional(),
    loginCount: number(),
    lastLogin: number(),
    
    // Boolean columns
    verified: boolean(),
    active: boolean(),
    
    // JSON column
    settings: json<Settings>(),
    
    // Enumeration columns
    role: enumeration<Role>(),
    status: enumeration<Status>(),
  })
  .primaryKey('id');

Best Practices

  1. Use enumeration() for constrained strings: Prefer enumeration<T>() over string() when values are limited to a specific set
  2. Mark optional fields explicitly: Use .optional() for nullable columns to match your PostgreSQL schema
  3. Type JSON structures: Always provide a type parameter to json() for better type safety
  4. Use number for timestamps: Store Unix timestamps as number() columns
  5. Consider branded types: Use TypeScript branded types for IDs to prevent mixing different ID types

Build docs developers (and LLMs) love