Semantic types are pre-configured combinations of a base type with validation,
constraints, and metadata baked in. They exist so you don’t have to manually
chain .unique(), .default(), format hints, and regex patterns on every
column.
| Feature | Low-level type | Semantic type |
|---|
| SQL type + size | You specify | Pre-configured |
OpenAPI format | None | Added (e.g. "email") |
| Validation pattern | Manual .pattern() | Built-in |
| Min/max constraints | Manual .min() | Built-in where relevant |
| Unique constraint | Manual .unique() | Automatic (e.g. slug) |
| Default value | Manual .default() | Automatic (e.g. flag) |
| Hidden from API | Never | Conditional (e.g. password_hash) |
Example: col.email() vs col.string(255)
col.string(255) produces a VARCHAR(255) with no other metadata.
col.email() produces a VARCHAR(255) plus an OpenAPI format: "email"
hint, a regex validation pattern, and the semantic type name in x-db for
downstream tooling.
Both map to the same SQL column type. The difference is in the constraints and
metadata that get carried into the OpenAPI schema and generated code.
| Type | Base Type | Details |
|---|
col.id() | UUID | Primary key with auto-generation |
col.email() | string(255) | Email validation pattern |
col.username() | string(50) | Lowercase alphanumeric + underscores |
col.password_hash() | string(255) | Hidden from API exports |
col.phone() | string(50) | E.164 international format |
col.token() | string(64) | For API keys and session tokens |
| Type | Base Type | Details |
|---|
col.name() | string(100) | Person/entity names |
col.title() | string(200) | Article/page titles |
col.slug() | string(255) | URL-safe, unique, lowercase |
col.summary() | string(500) | Short descriptions |
col.code() | string(20) | SKUs, codes (uppercase pattern) |
col.body() | text | Long-form content |
| Type | Base Type | Details |
|---|
col.money() | decimal(19,4) | Currency amounts, min(0) |
col.percentage() | decimal(5,2) | 0-100 range, two decimal places |
col.counter() | integer | Auto-defaults to 0 |
col.quantity() | integer | Non-negative integers |
col.rating() | decimal(2,1) | 0-5 rating scale |
col.duration() | integer | Duration in seconds, min(0) |
| Type | Base Type | Details |
|---|
col.url() | string(2048) | Full URLs with scheme |
col.ip() | string(45) | IPv4 or IPv6 with validation |
col.ipv4() | string(15) | IPv4 only with validation |
col.ipv6() | string(45) | IPv6 only with validation |
col.user_agent() | string(500) | Browser user agent strings |
| Type | Base Type | Details |
|---|
col.color() | string(7) | Hex format (#FF5733) |
col.html() | text | HTML content with format hint |
col.markdown() | text | Markdown formatted content |
| Type | Base Type | Details |
|---|
col.flag() | boolean | Defaults to false |
col.flag(true) | boolean | Defaults to true |
| Type | Base Type | Details |
|---|
col.country() | string(2) | ISO 3166-1 alpha-2 (US) |
col.currency() | string(3) | ISO 4217 (USD) |
col.locale() | string(10) | BCP 47 (en-US) |
col.timezone() | string(50) | IANA timezone names |
Valid Values
- Country:
US, GB, DE, JP, BR
- Currency:
USD, EUR, GBP, JPY, BRL
- Locale:
en-US, en-GB, de-DE, ja-JP, pt-BR
- Timezone:
America/New_York, Europe/London, Asia/Tokyo