Field Builders
Comprehensive reference for all field types and modifiers
Field builders provide a type-safe, fluent API for defining model field types and constraints. Each builder corresponds to a PostgreSQL data type and provides relevant modifiers.
String and Text
field.string()
Variable-length string field, maps to VARCHAR in PostgreSQL. Use .max(n) to set maximum length or .fixed(n) for fixed-length CHAR.
import { defineModel, field } from '@justscale/models';
class User extends defineModel({
email: field.string().max(255).unique(),
username: field.string().max(50).index(),
countryCode: field.string().fixed(2), // CHAR(2)
bio: field.string().optional(), // No max length
}) {}field.text()
Unlimited-length text field for long content, maps to TEXT.
import { defineModel, field } from '@justscale/models';
class BlogPost extends defineModel({
title: field.string().max(200),
content: field.text(), // Long content
summary: field.text().optional(),
}) {}Numbers
field.int()
Standard integer field, maps to INTEGER (32-bit signed, range -2,147,483,648 to 2,147,483,647).
import { defineModel, field } from '@justscale/models';
class Product extends defineModel({
quantity: field.int().default(0),
viewCount: field.int().default(0),
priority: field.int().optional(),
}) {}field.smallint()
Small integer field, maps to SMALLINT (16-bit signed, range -32,768 to 32,767). Use for smaller numbers to save space.
import { defineModel, field } from '@justscale/models';
class Rating extends defineModel({
score: field.smallint(), // 1-5 rating
stars: field.smallint(), // 1-10 stars
}) {}field.bigint()
Large integer field, maps to BIGINT (64-bit signed). TypeScript type is bigint.
import { defineModel, field } from '@justscale/models';
class Analytics extends defineModel({
totalViews: field.bigint().default(0n),
uniqueVisitors: field.bigint().default(0n),
}) {}
const stats = new Analytics({
totalViews: 9007199254740991n, // Beyond Number.MAX_SAFE_INTEGER
});field.decimal(precision, scale)
Fixed-precision decimal, maps to NUMERIC(precision, scale). Stored as stringin TypeScript to preserve exact precision. Essential for monetary values.
import { defineModel, field } from '@justscale/models';
class Order extends defineModel({
subtotal: field.decimal(10, 2), // Max: 99,999,999.99
tax: field.decimal(10, 2),
total: field.decimal(10, 2),
discountPercent: field.decimal(5, 2).optional(), // Max: 999.99
}) {}
const order = new Order({
subtotal: '99.99',
tax: '8.50',
total: '108.49',
});field.float() / field.double()
Floating-point numbers. float() maps to REAL (32-bit),double() maps to DOUBLE PRECISION (64-bit). Avoid for money.
import { defineModel, field } from '@justscale/models';
class Measurement extends defineModel({
temperature: field.float(),
latitude: field.double(),
longitude: field.double(),
precision: field.double().optional(),
}) {}Boolean
field.boolean()
Boolean field, maps to BOOLEAN. Often paired with .default().
import { defineModel, field } from '@justscale/models';
class User extends defineModel({
isActive: field.boolean().default(true),
emailVerified: field.boolean().default(false),
isPremium: field.boolean().optional(),
}) {}UUID
field.uuid()
UUID field, maps to UUID. TypeScript type is string. Often used for external IDs or references.
import { defineModel, field } from '@justscale/models';
class ApiKey extends defineModel({
keyId: field.uuid().unique(),
secret: field.string().max(255),
userId: field.uuid().index(),
}) {}Date and Time
field.timestamp()
Timestamp with timezone, maps to TIMESTAMP WITH TIME ZONE. TypeScript type is Date.
import { defineModel, field } from '@justscale/models';
class Event extends defineModel({
name: field.string(),
scheduledAt: field.timestamp(),
cancelledAt: field.timestamp().optional(),
}) {}field.date()
Date only (no time), maps to DATE. TypeScript type is Date.
import { defineModel, field } from '@justscale/models';
class Person extends defineModel({
name: field.string(),
birthDate: field.date(),
hireDate: field.date().optional(),
}) {}field.time()
Time of day (no date), maps to TIME. TypeScript type is string (HH:MM:SS format).
import { defineModel, field } from '@justscale/models';
class Schedule extends defineModel({
name: field.string(),
startTime: field.time(),
endTime: field.time(),
}) {}
const shift = new Schedule({
name: 'Morning Shift',
startTime: '09:00:00',
endTime: '17:00:00',
});JSON
field.json<T>() / field.jsonb<T>()
JSON fields for structured data. json() maps to JSON,jsonb() maps to JSONB (binary, indexable). Provide a TypeScript type parameter for type safety.
import { defineModel, field } from '@justscale/models';
interface ProductMetadata {
dimensions: { width: number; height: number; depth: number };
tags: string[];
features: string[];
}
class Product extends defineModel({
name: field.string(),
// JSONB for indexable structured data
metadata: field.jsonb<ProductMetadata>(),
// JSON for simple unstructured data
settings: field.json<Record<string, unknown>>().optional(),
}) {}
const product = new Product({
name: 'Laptop',
metadata: {
dimensions: { width: 14, height: 10, depth: 1 },
tags: ['electronics', 'computer'],
features: ['Backlit keyboard', 'Touch screen'],
},
});Binary
field.bytes()
Binary data field, maps to BYTEA. TypeScript type is Uint8Array.
import { defineModel, field } from '@justscale/models';
class File extends defineModel({
filename: field.string().max(255),
mimeType: field.string().max(100),
content: field.bytes(),
}) {}Enums
field.enum(name, values)
PostgreSQL enum field. Provide enum name and array of string values. TypeScript infers a union type from the values.
import { defineModel, field } from '@justscale/models';
const USER_ROLES = ['admin', 'editor', 'viewer'] as const;
const ORDER_STATUSES = ['pending', 'processing', 'completed', 'cancelled'] as const;
class User extends defineModel({
email: field.string().max(255),
role: field.enum('user_role', USER_ROLES).default('viewer'),
}) {}
class Order extends defineModel({
orderNumber: field.string().max(50),
status: field.enum('order_status', ORDER_STATUSES).default('pending'),
}) {}
// TypeScript enforces valid enum values
const user = new User({
email: 'admin@example.com',
role: 'admin', // ✓ Type-safe
// role: 'superuser', // ✗ TypeScript error
});Arrays
field.array(elementType)
PostgreSQL array field. Pass another field builder as the element type. TypeScript type is T[].
import { defineModel, field } from '@justscale/models';
class Article extends defineModel({
title: field.string(),
tags: field.array(field.string()),
scores: field.array(field.int()).optional(),
relatedIds: field.array(field.uuid()),
}) {}
const article = new Article({
title: 'Getting Started',
tags: ['tutorial', 'beginner'],
scores: [4, 5, 3],
relatedIds: ['uuid-1', 'uuid-2'],
});Objects
field.object(shape)
Nested object stored as JSONB. Define the shape using field builders. TypeScript infers the complete nested type.
import { defineModel, field } from '@justscale/models';
class Product extends defineModel({
name: field.string(),
dimensions: field.object({
width: field.float(),
height: field.float(),
depth: field.float(),
unit: field.string().max(10),
}),
shippingInfo: field.object({
weight: field.decimal(10, 2),
carrier: field.string().optional(),
}).optional(),
}) {}
const product = new Product({
name: 'Box',
dimensions: {
width: 10.5,
height: 5.0,
depth: 8.0,
unit: 'cm',
},
});References
field.ref(Model)
Reference to another model (many-to-one or one-to-one). Use a function for self-references to avoid hoisting issues. See
Repositories > Referencesimport { defineModel, field } from '@justscale/models';
class User extends defineModel({
email: field.string(),
name: field.string(),
}) {}
class Post extends defineModel({
title: field.string(),
author: field.ref(User), // Reference<User>
parent: field.ref(() => Post), // Self-reference
}) {}field.refs(Model)
Reference to multiple models (many-to-many). Creates a join table in the repository.
import { defineModel, field } from '@justscale/models';
class Tag extends defineModel({
name: field.string().max(50).unique(),
}) {}
class Post extends defineModel({
title: field.string(),
content: field.text(),
tags: field.refs(Tag), // References<Tag> - many-to-many
}) {}System Fields
Special semantic fields for common lifecycle and versioning patterns:
field.createdAt()
Timestamp automatically set when the record is inserted. Maps to TIMESTAMP WITH TIME ZONE.
field.updatedAt()
Timestamp automatically updated whenever the record is saved. Maps to TIMESTAMP WITH TIME ZONE.
field.deletedAt()
Optional timestamp for soft deletes. When set, the record is considered deleted but remains in the database. Automatically marked as optional.
field.version()
Optimistic concurrency control version number. Automatically incremented on updates. Defaults to 1.
import { defineModel, field } from '@justscale/models';
class Document extends defineModel({
title: field.string(),
content: field.text(),
createdAt: field.createdAt(), // Auto-set on insert
updatedAt: field.updatedAt(), // Auto-updated on save
deletedAt: field.deletedAt(), // Soft delete (optional)
version: field.version(), // Optimistic locking (defaults to 1)
}) {}Field Modifiers
All field builders support these common modifiers:
.optional()
Makes the field nullable. TypeScript type becomes T | undefined.
import { defineModel, field } from '@justscale/models';
class User extends defineModel({
email: field.string(), // Required
bio: field.string().optional(), // Optional (can be undefined)
age: field.int().optional(), // number | undefined
}) {}
const user = new User({ email: 'test@example.com' });
// bio and age are undefined.default(value)
Sets a default value when the field is not provided. Can be a static value or a function.
import { defineModel, field } from '@justscale/models';
class User extends defineModel({
email: field.string(),
isActive: field.boolean().default(true),
balance: field.decimal(10, 2).default('0.00'),
joinedAt: field.timestamp().default(() => new Date()),
}) {}.unique()
Adds a unique constraint. PostgreSQL enforces uniqueness across all rows.
import { defineModel, field } from '@justscale/models';
class User extends defineModel({
email: field.string().max(255).unique(),
username: field.string().max(50).unique(),
}) {}.index()
Creates a database index for faster lookups. Use for frequently queried fields.
import { defineModel, field } from '@justscale/models';
class Post extends defineModel({
title: field.string(),
slug: field.string().max(255).unique().index(),
authorId: field.uuid().index(), // Foreign key index
publishedAt: field.timestamp().index(),
}) {}.backfill(value)
Provides a value for existing rows when adding a new non-nullable field via migrations. Required when adding non-nullable fields without defaults to tables with existing data.
import { defineModel, field } from '@justscale/models';
// Migration scenario: adding 'status' to existing User table
class User extends defineModel({
email: field.string(),
// Existing users will get 'active' status when column is added
status: field.string().default('active').backfill('active'),
}) {}Chaining Modifiers
Modifiers can be chained together fluently. Order doesn't matter:
import { defineModel, field } from '@justscale/models';
class Product extends defineModel({
// Chain multiple modifiers
sku: field.string()
.max(50)
.unique()
.index(),
name: field.string()
.max(200)
.index(),
price: field.decimal(10, 2)
.default('0.00'),
description: field.text()
.optional(),
}) {}