Model
The model
construct is the core of ZModel. It defines the structure of your data and relations. A model represents a domain entity and is backed by a database table.
Defining models​
A typical model looks like this:
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
name String
}
The simplest models are just a collection of fields. A model must be uniquely identifiable by some of its fields. In most cases, you'll have a field marked with the @id
attribute (more about attributes later).
model User {
id Int @id
}
If your model needs a composite ID, you can use the @@id
model-level attribute to specify it:
model City {
country String
name String
@@id([country, name])
}
If no @id
or @@id
is specified, the ORM will resort to using a field (or fields) marked with the @unique
or @@unique
attribute as the identifier.
model User {
email String @unique
}
model City {
country String
name String
@@unique([country, name])
}
Model fields​
Each model field must at least have a name and a type. A field can be typed in one of the following ways:
-
Built-in types, including:
- String
- Boolean
- Int
- BigInt
- Float
- Decimal
- DateTime
- Json
- Bytes
- Unsupported
The
Unsupported
type is for defining fields of types not supported by the ORM. It lets the migration engine know how to create the field in the database.// from Prisma docs
model Star {
id Int @id
position Unsupported("circle")? @default(dbgenerated("'<(10,4),11>'::circle"))
} -
Enum
We'll talk about enums later.
enum Role {
USER
ADMIN
}
model User {
id Int @id
role Role
} -
Model
It'll then form a relation. We'll cover that topic later.
model Post {
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
} -
Custom type
ZenStack allows you to define custom types in the schema and use them to type JSON fields. This is covered in more detail in the Custom Type section.
type Address {
street String
city String
country String
zip Int
}
model User {
id Int @id
address Address @json
}
A field can be set as optional by adding the ?
suffix to its type, or list by adding the []
suffix. However, a field cannot be both optional and a list at the same time.
model User {
id Int @id
name String?
tags String[]
}
A default value can be specified for a field with the @default
attribute. The value can be a literal, an enum value, or a supported function call, including:
now()
: returns the current timestampcuid()
: returns a CUIDuuid()
: returns a UUIDulid()
: returns a ULIDnanoid()
: returns a Nano IDautoincrement()
: returns an auto-incrementing integer (only for integer fields)dbgenerated("...")
: calls a native db function
model User {
id Int @id @default(autoincrement())
role Role @default(USER)
createdAt DateTime @default(now())
}
Native type mapping​
Besides giving a field a type, you can also specify the native database type to use with the @db.
series of attributes.
model User {
...
name String @db.VarChar(64)
}
These attributes control what data type is used when the migration engine maps the schema to DDL. You can find a complete list of native type attributes in the ZModel Language Reference.
Name mapping​
Quite often, you want to use a different naming scheme for your models and fields than the database. You can achieve that with the @map
and @@map
attribute. The ORM respects the mapping when generating queries, and the migration engine uses it to generate the DDL.
model User {
id Int @id @map('_id')
@@map('users')
}