---
import DocsLayout from '../../layouts/DocsLayout.astro';
import CodeBlock from '../../components/CodeBlock.astro';
const basicTypes = `model Example {
// Integer types
id Int @id @auto // 32-bit integer
bigId BigInt // 64-bit integer
smallNum Int @db.SmallInt // 16-bit (PostgreSQL)
// Floating point
price Float // 64-bit float
rating Decimal @db.Decimal(3, 2) // Exact decimal
// String types
name String // Variable length text
bio String @db.Text // Long text
code String @db.Char(6) // Fixed length
// Boolean
active Boolean @default(true)
// Date/Time
createdAt DateTime @default(now())
birthDate DateTime @db.Date // Date only
loginTime DateTime @db.Time // Time only
// Binary
avatar Bytes // Binary data
// JSON
metadata Json // JSON/JSONB
}`;
const modifiers = `model Example {
// Required (default) - must have a value
required String
// Optional - can be NULL
optional String?
// Array/List - multiple values
tags String[]
// Optional Array - entire array can be NULL
optTags String[]?
}
// Type modifier combinations
model Post {
// Required string
title String
// Optional string (nullable)
subtitle String?
// Required array of strings
tags String[]
// Optional array (array itself can be null)
aliases String[]?
// Array with optional elements (PostgreSQL)
// Each element can be null
maybeItems String?[]
}`;
const attributes = `model User {
// Primary key
id Int @id
// Auto-increment (usually with @id)
sequence Int @auto
// Unique constraint
email String @unique
// Default literal value
role String @default("user")
active Boolean @default(true)
count Int @default(0)
score Float @default(0.0)
// Default function
id String @default(uuid())
publicId String @default(cuid())
shortId String @default(nanoid())
sortableId String @default(ulid())
createdAt DateTime @default(now())
// Auto-update timestamp
updatedAt DateTime @updatedAt
// Database-generated default
trackingId String @default(dbgenerated("gen_random_uuid()"))
// Column name mapping
firstName String @map("first_name")
// Ignore in client (still in DB)
internalNote String? @ignore
// Database-specific type
content String @db.Text
amount Decimal @db.Decimal(10, 2)
}`;
const primaryKeys = `// Auto-increment integer (most common)
model User {
id Int @id @auto
}
// UUID primary key
model Session {
id String @id @default(uuid())
}
// CUID for distributed systems
model Event {
id String @id @default(cuid())
}
// ULID for sortable IDs
model LogEntry {
id String @id @default(ulid())
}
// NanoID for short URLs
model ShortLink {
id String @id @default(nanoid())
}
// Composite primary key
model Membership {
userId Int
orgId Int
@@id([userId, orgId])
}
// Natural key (be careful with changes)
model Country {
code String @id // "US", "GB", etc.
name String
}`;
const defaultValues = `model Record {
// Literal defaults
name String @default("")
count Int @default(0)
active Boolean @default(true)
rating Float @default(0.0)
tags String[] @default([])
config Json @default("{}")
// Built-in functions
id String @default(uuid()) // UUID v4
publicId String @default(cuid()) // CUID
shortId String @default(nanoid()) // NanoID (21 chars)
tinyId String @default(nanoid(8)) // NanoID (custom length)
sortId String @default(ulid()) // ULID
createdAt DateTime @default(now()) // Current timestamp
// Auto-increment
sequence Int @default(autoincrement())
// Enum default
status Status @default(DRAFT)
// Database-generated (PostgreSQL examples)
trackId String @default(dbgenerated("gen_random_uuid()"))
serial Int @default(dbgenerated("nextval('my_seq')"))
}`;
const timestamps = `model Record {
// Creation timestamp - set once on insert
createdAt DateTime @default(now())
// Update timestamp - auto-updates on every change
updatedAt DateTime @updatedAt
// Soft delete timestamp - null means not deleted
deletedAt DateTime?
// Custom timestamp fields
publishedAt DateTime? // When published
expiresAt DateTime? // Expiration date
lastLoginAt DateTime? // Last activity
}
// Database-specific timestamp types
model TimestampExample {
// With timezone (PostgreSQL)
createdAt DateTime @db.Timestamptz
// Without timezone
localTime DateTime @db.Timestamp
// Date only (no time)
birthDate DateTime @db.Date
// Time only (no date)
startTime DateTime @db.Time
}`;
const jsonFields = `model Settings {
id Int @id @auto
// JSON field (JSONB in PostgreSQL for indexing)
config Json @db.JsonB
// JSON with default
options Json @default("{}")
metadata Json @default("{\\"version\\": 1}")
}
// Using JSON in queries
let users = client
.user()
.find_many()
.where(user::settings::path(["theme"])::equals("dark"))
.exec()
.await?;
// JSON structure (not enforced by DB, document in comments)
/// Settings JSON structure:
/// {
/// "theme": "light" | "dark",
/// "notifications": {
/// "email": boolean,
/// "push": boolean
/// },
/// "language": string
/// }
model UserPreferences {
id Int @id @auto
settings Json @default("{}")
}`;
const arrayFields = `model Post {
id Int @id @auto
// String array
tags String[]
// Integer array
scores Int[]
// Enum array
flags Flag[]
// Index for array contains queries (PostgreSQL)
@@index([tags], type: GIN)
}
// Array operations in queries
let posts = client
.post()
.find_many()
.where(post::tags::has("rust")) // Contains element
.where(post::tags::has_some(["rust", "orm"])) // Contains any
.where(post::tags::has_every(["featured"])) // Contains all
.where(post::tags::is_empty(false)) // Not empty
.exec()
.await?;`;
const decimalFields = `// Decimal for exact precision (money, financial data)
model Product {
id Int @id @auto
// Default precision varies by database
price Decimal
// Explicit precision: DECIMAL(10, 2)
// 10 total digits, 2 after decimal
// Range: -99999999.99 to 99999999.99
cost Decimal @db.Decimal(10, 2)
// High precision for currency calculations
amount Decimal @db.Decimal(19, 4)
// Tax rate (percentage)
taxRate Decimal @db.Decimal(5, 4) // 0.0000 to 9.9999
}
// Decimal vs Float
model Comparison {
// Use Decimal for:
// - Money/currency
// - Financial calculations
// - When exact precision matters
price Decimal @db.Decimal(10, 2)
// Use Float for:
// - Scientific calculations
// - When small rounding errors are acceptable
// - Performance-critical calculations
rating Float
}`;
const bytesFields = `model File {
id Int @id @auto
name String
// Binary data (BYTEA in PostgreSQL, BLOB in MySQL)
content Bytes
// File metadata
mimeType String
size Int
}
model Image {
id Int @id @auto
// Store small images directly
thumbnail Bytes?
// Store image hash for deduplication
hash Bytes @unique
}
// Note: For large files, consider storing in object storage
// (S3, GCS) and keeping only the URL/path in the database`;
const databaseTypes = `model DatabaseSpecific {
id Int @id @auto
// PostgreSQL-specific types
uuid String @db.Uuid // Native UUID
jsonb Json @db.JsonB // Binary JSON (indexable)
xml String @db.Xml // XML data
inet String @db.Inet // IP address
cidr String @db.Cidr // Network address
macaddr String @db.MacAddr // MAC address
money Decimal @db.Money // Currency
tsVector String @db.TsVector // Full-text search vector
// MySQL-specific types
tinyText String @db.TinyText // 255 bytes
mediumText String @db.MediumText // 16MB
longText String @db.LongText // 4GB
tinyInt Int @db.TinyInt // -128 to 127
mediumInt Int @db.MediumInt // -8M to 8M
year Int @db.Year // 1901 to 2155
bit Int @db.Bit(8) // Bit field
binary Bytes @db.Binary(16) // Fixed binary
varbinary Bytes @db.VarBinary(255) // Variable binary
// SQLite types (limited type system)
// All map to: INTEGER, REAL, TEXT, BLOB
// Prax handles the conversion automatically
}`;
---
<DocsLayout title="Fields & Types - Prax ORM">
<article class="max-w-4xl mx-auto px-6 py-12">
<header class="mb-12">
<h1 class="text-4xl font-bold mb-4">Fields & Types</h1>
<p class="text-xl text-muted">
Define your model fields with various scalar types, modifiers, and attributes for complete control over your database schema.
</p>
</header>
<div class="space-y-12">
<!-- Scalar Types -->
<section>
<h2 class="text-2xl font-semibold mb-4">Scalar Types</h2>
<p class="text-muted mb-4">
Prax provides scalar types that map to native database types and Rust types:
</p>
<div class="overflow-x-auto mb-6">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-border">
<th class="text-left py-3 px-4 font-semibold">Prax Type</th>
<th class="text-left py-3 px-4 font-semibold">Rust Type</th>
<th class="text-left py-3 px-4 font-semibold">PostgreSQL</th>
<th class="text-left py-3 px-4 font-semibold">MySQL</th>
<th class="text-left py-3 px-4 font-semibold">SQLite</th>
</tr>
</thead>
<tbody class="text-muted">
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Int</code></td>
<td class="py-3 px-4">i32</td>
<td class="py-3 px-4">INTEGER</td>
<td class="py-3 px-4">INT</td>
<td class="py-3 px-4">INTEGER</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">BigInt</code></td>
<td class="py-3 px-4">i64</td>
<td class="py-3 px-4">BIGINT</td>
<td class="py-3 px-4">BIGINT</td>
<td class="py-3 px-4">INTEGER</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Float</code></td>
<td class="py-3 px-4">f64</td>
<td class="py-3 px-4">DOUBLE PRECISION</td>
<td class="py-3 px-4">DOUBLE</td>
<td class="py-3 px-4">REAL</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Decimal</code></td>
<td class="py-3 px-4">rust_decimal::Decimal</td>
<td class="py-3 px-4">DECIMAL</td>
<td class="py-3 px-4">DECIMAL</td>
<td class="py-3 px-4">REAL</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">String</code></td>
<td class="py-3 px-4">String</td>
<td class="py-3 px-4">TEXT</td>
<td class="py-3 px-4">VARCHAR(191)</td>
<td class="py-3 px-4">TEXT</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Boolean</code></td>
<td class="py-3 px-4">bool</td>
<td class="py-3 px-4">BOOLEAN</td>
<td class="py-3 px-4">TINYINT(1)</td>
<td class="py-3 px-4">INTEGER</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">DateTime</code></td>
<td class="py-3 px-4">chrono::DateTime<Utc></td>
<td class="py-3 px-4">TIMESTAMP(3)</td>
<td class="py-3 px-4">DATETIME(3)</td>
<td class="py-3 px-4">TEXT</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Json</code></td>
<td class="py-3 px-4">serde_json::Value</td>
<td class="py-3 px-4">JSONB</td>
<td class="py-3 px-4">JSON</td>
<td class="py-3 px-4">TEXT</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">Bytes</code></td>
<td class="py-3 px-4">Vec<u8></td>
<td class="py-3 px-4">BYTEA</td>
<td class="py-3 px-4">LONGBLOB</td>
<td class="py-3 px-4">BLOB</td>
</tr>
</tbody>
</table>
</div>
<CodeBlock code={basicTypes} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Type Modifiers -->
<section>
<h2 class="text-2xl font-semibold mb-4">Type Modifiers</h2>
<p class="text-muted mb-4">
Modifiers change how field values are handled:
</p>
<CodeBlock code={modifiers} lang="prax" filename="prax/schema.prax" />
<div class="mt-6 overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-border">
<th class="text-left py-3 px-4 font-semibold">Modifier</th>
<th class="text-left py-3 px-4 font-semibold">Syntax</th>
<th class="text-left py-3 px-4 font-semibold">Rust Type</th>
<th class="text-left py-3 px-4 font-semibold">Description</th>
</tr>
</thead>
<tbody class="text-muted">
<tr class="border-b border-border">
<td class="py-3 px-4">Required</td>
<td class="py-3 px-4"><code class="text-primary-400">String</code></td>
<td class="py-3 px-4">String</td>
<td class="py-3 px-4">Must have a value (NOT NULL)</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4">Optional</td>
<td class="py-3 px-4"><code class="text-primary-400">String?</code></td>
<td class="py-3 px-4">Option<String></td>
<td class="py-3 px-4">Can be NULL</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4">Array</td>
<td class="py-3 px-4"><code class="text-primary-400">String[]</code></td>
<td class="py-3 px-4">Vec<String></td>
<td class="py-3 px-4">List of values</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4">Optional Array</td>
<td class="py-3 px-4"><code class="text-primary-400">String[]?</code></td>
<td class="py-3 px-4">Option<Vec<String>></td>
<td class="py-3 px-4">Array that can be NULL</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Field Attributes -->
<section>
<h2 class="text-2xl font-semibold mb-4">Field Attributes</h2>
<p class="text-muted mb-4">
Attributes modify field behavior and add constraints:
</p>
<CodeBlock code={attributes} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Primary Keys -->
<section>
<h2 class="text-2xl font-semibold mb-4">Primary Keys</h2>
<p class="text-muted mb-4">
Every model needs a unique identifier. Choose the right primary key strategy for your use case:
</p>
<CodeBlock code={primaryKeys} lang="prax" filename="prax/schema.prax" />
<div class="mt-6 grid md:grid-cols-2 gap-4">
<div class="p-4 rounded-xl bg-success-500/10 border border-success-500/30">
<h4 class="font-semibold text-success-400 mb-2">Auto-Increment</h4>
<p class="text-muted text-sm">Best for simple applications with a single database. Compact and fast.</p>
</div>
<div class="p-4 rounded-xl bg-info-500/10 border border-info-500/30">
<h4 class="font-semibold text-info-400 mb-2">UUID/CUID/ULID</h4>
<p class="text-muted text-sm">Best for distributed systems, APIs, or when you need to generate IDs client-side.</p>
</div>
</div>
</section>
<!-- Default Values -->
<section>
<h2 class="text-2xl font-semibold mb-4">Default Values</h2>
<p class="text-muted mb-4">
Set default values to simplify record creation and ensure data consistency:
</p>
<CodeBlock code={defaultValues} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Timestamps -->
<section>
<h2 class="text-2xl font-semibold mb-4">Timestamps</h2>
<p class="text-muted mb-4">
Track when records are created, updated, and deleted:
</p>
<CodeBlock code={timestamps} lang="prax" filename="prax/schema.prax" />
</section>
<!-- JSON Fields -->
<section>
<h2 class="text-2xl font-semibold mb-4">JSON Fields</h2>
<p class="text-muted mb-4">
Store flexible, schema-less data in JSON fields:
</p>
<CodeBlock code={jsonFields} lang="prax" filename="prax/schema.prax" />
<div class="mt-4 p-4 rounded-xl bg-warning-500/10 border border-warning-500/30">
<p class="text-warning-400 text-sm">
<strong>Note:</strong> JSON fields are flexible but lack schema enforcement.
Document the expected structure in comments and consider validation in your application layer.
</p>
</div>
</section>
<!-- Array Fields -->
<section>
<h2 class="text-2xl font-semibold mb-4">Array Fields</h2>
<p class="text-muted mb-4">
Store multiple values in a single field (PostgreSQL native support, emulated on other databases):
</p>
<CodeBlock code={arrayFields} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Decimal Fields -->
<section>
<h2 class="text-2xl font-semibold mb-4">Decimal Fields</h2>
<p class="text-muted mb-4">
Use Decimal for exact precision, especially for financial data:
</p>
<CodeBlock code={decimalFields} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Bytes Fields -->
<section>
<h2 class="text-2xl font-semibold mb-4">Binary Data</h2>
<p class="text-muted mb-4">
Store binary data like files, images, or hashes:
</p>
<CodeBlock code={bytesFields} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Database-Specific Types -->
<section>
<h2 class="text-2xl font-semibold mb-4">Database-Specific Types</h2>
<p class="text-muted mb-4">
Use <code class="px-2 py-1 bg-surface-elevated rounded">@db.TypeName</code> to specify
native database types for advanced use cases:
</p>
<CodeBlock code={databaseTypes} lang="prax" filename="prax/schema.prax" />
</section>
<!-- Field Attributes Reference -->
<section>
<h2 class="text-2xl font-semibold mb-4">Field Attributes Reference</h2>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-border">
<th class="text-left py-3 px-4 font-semibold">Attribute</th>
<th class="text-left py-3 px-4 font-semibold">Description</th>
</tr>
</thead>
<tbody class="text-muted">
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@id</code></td>
<td class="py-3 px-4">Marks field as primary key</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@auto</code></td>
<td class="py-3 px-4">Auto-increment (for Int primary keys)</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@unique</code></td>
<td class="py-3 px-4">Unique constraint on field</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@default(value)</code></td>
<td class="py-3 px-4">Default value (literal, function, or dbgenerated)</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@updatedAt</code></td>
<td class="py-3 px-4">Auto-update timestamp on record changes</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@map("name")</code></td>
<td class="py-3 px-4">Custom column name in database</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@db.Type</code></td>
<td class="py-3 px-4">Specific database column type</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@ignore</code></td>
<td class="py-3 px-4">Exclude from generated client</td>
</tr>
<tr class="border-b border-border">
<td class="py-3 px-4"><code class="text-primary-400">@relation(...)</code></td>
<td class="py-3 px-4">Define relationship to another model</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</article>
</DocsLayout>