# Attributes

Configure field and model behavior with powerful attributes and validators.

## Field Attributes

Field attributes modify individual field behavior, constraints, and mapping.

Attribute | Description
--- | ---
`@id` | Marks field as primary key
`@auto` | Auto-increment for integers
`@unique` | Unique constraint
`@default(value)` | Default value or function
`@updatedAt` | Auto-update timestamp on modification
`@map("name")` | Map to different column name
`@ignore` | Exclude from generated client
`@relation(...)` | Configure relation behavior
`@db.Type` | Database-specific column type

### Basic Usage

### Advanced Usage

## Model Attributes

Model-level attributes (prefixed with `@@`) configure table-wide behavior.

Attribute | Description
--- | ---
`@@map("name")` | Map to different table name
`@@id([fields])` | Composite primary key
`@@unique([fields])` | Composite unique constraint
`@@index([fields])` | Create database index
`@@ignore` | Exclude model from client
`@@schema("name")` | PostgreSQL schema name

## Relation Attributes

Configure how models relate to each other with referential actions.

Parameter | Description
--- | ---
`fields` | Local foreign key field(s)
`references` | Referenced field(s) on related model
`name` | Relation name for disambiguation
`onDelete` | Action when referenced record deleted
`onUpdate` | Action when referenced key updated

### Referential Actions

Action | Behavior
--- | ---
`Cascade` | Delete/update related records
`Restrict` | Prevent delete/update if references exist
`NoAction` | Similar to Restrict (database-dependent)
`SetNull` | Set foreign key to NULL
`SetDefault` | Set foreign key to default value

## ID Generation

Prax supports multiple ID generation strategies for different use cases. Choose the right one based on your requirements for uniqueness, sortability, and distribution.

### uuid() UUID v4

Universally Unique Identifier - the industry standard for distributed systems. Generates 128-bit identifiers with extremely low collision probability.

#### Advantages

- • RFC 4122 standard format
- • Native PostgreSQL `UUID` type support
- • Widely recognized and supported
- • Cryptographically random (v4)
- • No central coordination needed

#### Considerations

- • 36 characters with dashes
- • Not sortable by creation time
- • Random distribution (index fragmentation)
- • Larger storage than auto-increment

Example Output `550e8400-e29b-41d4-a716-446655440000`

### cuid() CUID

Collision-resistant Unique Identifier - designed for horizontal scaling and distributed systems. Includes a timestamp component for rough time-ordering.

#### Advantages

- • Roughly sortable by creation time
- • URL-safe characters only
- • Designed for distributed systems
- • Shorter than UUID (25 chars)
- • Includes machine fingerprint

#### Considerations

- • Not a standard format
- • Timestamp is extractable
- • Consider CUID2 for new projects

Example Output `cjld2cjxh0000qzrmn831i7rn`

### cuid2() CUID2

Next-generation CUID with improved security. More unpredictable and shorter than the original. Recommended for new projects.

#### Advantages

- • More secure than CUID
- • Shorter (24 characters)
- • Better entropy distribution
- • No extractable timestamp
- • URL-safe characters

#### Considerations

- • Not sortable by time
- • Newer, less widespread adoption

Example Output `tz4a98xxat96iws9zmbrgj3a`

### nanoid() NanoID

Compact, URL-friendly unique IDs with customizable length. Perfect for short URLs, invite codes, and user-facing identifiers.

#### Advantages

- • URL-safe (A-Za-z0-9_-)
- • Customizable length
- • Compact (21 chars default)
- • Cryptographically secure
- • Fast generation

#### Considerations

- • Not sortable by time
- • Shorter IDs = higher collision risk
- • No embedded metadata

Example Output
`V1StGXR8_Z5jdHi6B-myT` `nanoid(8): 5fX8pK2m`

### ulid() ULID

Universally Unique Lexicographically Sortable Identifier. Encodes creation timestamp, making IDs naturally sortable by time.

#### Advantages

- • Lexicographically sortable
- • Encodes millisecond timestamp
- • Case-insensitive
- • Compatible with UUID (128-bit)
- • Better index performance

#### Considerations

- • Timestamp is extractable
- • 26 characters (longer than CUID2)
- • Limited entropy in same millisecond

Example Output `01ARZ3NDEKTSV4RRFFQ69G5FAV`
01ARZ3NDEKTSV4RRFFQ69G5FAV
└─ timestamp ─┘└── randomness ──┘

### Comparison

Type | Length | Sortable | URL-Safe | Best For
--- | --- | --- | --- | ---
`uuid()` | 36 | No | No (dashes) | Database PKs, APIs
`cuid()` | 25 | Roughly | Yes | Distributed systems
`cuid2()` | 24 | No | Yes | Security-sensitive apps
`nanoid()` | 21* | No | Yes | Short URLs, invite codes
`ulid()` | 26 | Yes | Yes | Time-series, logs, events

* NanoID length is customizable

## String Validators

Validate string field content with built-in rules.

#### Format Validators

- `@validate.email` - Email format
- `@validate.url` - URL format
- `@validate.uuid` - UUID format
- `@validate.cuid` - CUID format
- `@validate.nanoid` - NanoID format
- `@validate.ulid` - ULID format
- `@validate.ip` - IP address (v4 or v6)
- `@validate.ipv4` - IPv4 only
- `@validate.ipv6` - IPv6 only

#### Content Validators

- `@validate.alpha` - Letters only
- `@validate.alphanumeric` - Letters and numbers
- `@validate.lowercase` - Lowercase only
- `@validate.uppercase` - Uppercase only
- `@validate.slug` - URL slug format
- `@validate.hex` - Hexadecimal string
- `@validate.base64` - Base64 encoded
- `@validate.json` - Valid JSON string

## Numeric Validators

Constrain numeric field values with range and sign validators.

Validator | Description
--- | ---
`@validate.min(n)` | Minimum value (inclusive)
`@validate.max(n)` | Maximum value (inclusive)
`@validate.range(min, max)` | Value between min and max
`@validate.positive` | Greater than zero
`@validate.negative` | Less than zero
`@validate.nonNegative` | Zero or greater
`@validate.nonPositive` | Zero or less
`@validate.integer` | Must be whole number
`@validate.multipleOf(n)` | Must be multiple of n
`@validate.finite` | Not Infinity or NaN

## Array Validators

Validate array fields for length and content constraints.

Validator | Description
--- | ---
`@validate.minItems(n)` | Minimum array length
`@validate.maxItems(n)` | Maximum array length
`@validate.items(min, max)` | Array length range
`@validate.unique` | All items must be unique
`@validate.nonEmpty` | At least one item required

## Date Validators

Validate datetime fields with temporal constraints.

Validator | Description
--- | ---
`@validate.past` | Must be in the past
`@validate.future` | Must be in the future
`@validate.pastOrPresent` | Not in the future
`@validate.futureOrPresent` | Not in the past
`@validate.after("date")` | After specific date
`@validate.before("date")` | Before specific date

## General Validators

Universal validators that work across different field types.

Validator | Description
--- | ---
`@validate.required` | Field must have a value (even if optional type)
`@validate.notEmpty` | Non-empty string/array
`@validate.oneOf(...)` | Value must be one of specified options
`@validate.custom("fn")` | Custom validation function

## Default Value Functions

Auto-generate values for fields using built-in functions.

Function | Description
--- | ---
`now()` | Current timestamp
`uuid()` | Random UUID v4
`cuid()` | Collision-resistant unique ID
`cuid2()` | Next-gen CUID (more secure)
`nanoid()` | URL-friendly unique ID
`nanoid(n)` | NanoID with custom length
`ulid()` | Sortable unique ID
`autoincrement()` | Auto-incrementing integer
`dbgenerated("expr")` | Database-generated expression

## Database-Specific Types

Map fields to native database column types with `@db.*` attributes.

#### PostgreSQL Types

- `@db.Text` - Unlimited text
- `@db.JsonB` - Binary JSON (indexable)
- `@db.Uuid` - Native UUID
- `@db.Xml` - XML type
- `@db.Inet` - IP address
- `@db.Cidr` - Network address
- `@db.MacAddr` - MAC address
- `@db.Decimal(p, s)` - Precise decimal

#### MySQL Types

- `@db.TinyText` - 255 bytes
- `@db.MediumText` - 16 MB
- `@db.LongText` - 4 GB
- `@db.TinyInt` - Tiny integer
- `@db.MediumInt` - Medium integer
- `@db.Year` - Year type
- `@db.VarChar(n)` - Variable char
- `@db.Charset("utf8mb4")` - Charset

## Documentation & Metadata

Add metadata and documentation to fields using doc comments.

#### Visibility

- `@hidden` - Exclude from public API
- `@internal` - Admin-only access
- `@sensitive` - Mask in logs
- `@readonly` - Not settable via API
- `@writeonly` - Not in responses

#### Documentation

- `@deprecated` - Mark as deprecated
- `@since version` - Version introduced
- `@example value` - Example value
- `@label text` - Display label
- `@placeholder text` - Input placeholder

## Index Types

Create different types of database indexes for query optimization.

Type | Use Case | PG | MySQL | SQLite
--- | --- | --- | --- | ---
@for (row of indexTypesTable; track row.type) {
{{ row.type }} | {{ row.use }} | {{ row.pg }} | {{ row.mysql }} | {{ row.sqlite }}
}

* Requires pgvector extension

## Vector Indexes

Vector indexes enable fast approximate nearest neighbor (ANN) search for AI/ML embeddings. Requires the `pgvector` PostgreSQL extension.

### Distance Operations

Operation | Description | Best For
--- | --- | ---
@for (row of vectorOpsTable; track row.op) {
{{ row.op }} | {{ row.desc }} | {{ row.best }}
}

#### 💡 Choosing an Index Type

- HNSW: Best recall, faster queries, slower builds - recommended for most cases
- IVFFlat: Faster builds, slightly lower recall - good for large datasets (1M+ vectors)