Skip to content

Latest commit

Β 

History

History
466 lines (326 loc) Β· 13.6 KB

data-modeling.md

File metadata and controls

466 lines (326 loc) Β· 13.6 KB

Data modeling

Data model definition

The data model definition (short: data model or datamodel) is part of your schema file.

It describes the shape of the data per data source. For example, when connecting to a relational database as a data source, the data model definition is a declarative representation of the database schema (tables, columns, indexes, ...). For a REST API, it describes the shapes of the resources that can be retrieved and manipulated via the API.

Example

Here is an example based on a local SQLite database located in the same directory of the schema file (called data.db):

// schema.prisma

datasource mysql {
  url      = "file:data.db"
  provider = "sqlite"
}

model User {
  id        Int      @id
  createdAt DateTime @default(now())
  email     String   @unique
  name      String?
  role      Role     @default(USER)
  posts     Post[]
  profile   Profile?
  address   Address?
}

embed Address {
  street  String
  zipCode String
}

model Profile {
  id   Int    @id
  user User
  bio  String
}

model Post {
  id         Int        @id
  createdAt  DateTime   @default(now())
  updatedAt  DateTime   @updatedAt
  author     User
  title      String
  published  Boolean    @default(false)
  categories Category[]
}

model Category {
  id    Int    @id
  name  String
  posts Post[]
}

enum Role {
  USER
  ADMIN
}

While this file mostly consists of the data model definition, it is a valid schema file because it also specifies a data source connector (for SQLite, in this case).

Models

Models represent the entities of your application domain. They are defined using model blocks in the data model.

On a technical level, a model maps to the underlying structures of the data source, for example:

  • In PostgreSQL, a model maps to a table
  • In MongoDB, a model maps to a collection
  • In REST, a model maps to a resource

Naming models

Models are typically spelled in PascalCase and use the singular form (e.g. User instead of Users).

Technically, a model can be named anything that adheres to this regular expression:

[A-Za-z_][A-Za-z0-9_]*

Model operations in the Photon API (CRUD)

Every model in the data model definition will result into a number of CRUD operations in the generated Photon API:

  • findMany
  • findOne
  • create
  • update
  • upsert
  • delete
  • updateMany
  • deleteMany

The operations are accessible via a generated property on the Photon instance. By default the name of the property is the plural, lowercase form of the model name, e.g. users for a User model or posts for a Post model.

Here is an example illustrating the use of a users property from the Photon JS API:

const newUser = await photon.users.create({ data: {
  name: "Alice"
}})
const allUsers = await photon.users.findMany()

Note that for Photon JS the name of the users property is auto-generated using the pluralize package.

Fields

The properties of a model are called fields. A field consists of several parts:

You can see examples of fields on the sample models above.

Naming fields

Field names are typically spelled in camelCase starting with a lowercase letter.

Technically, a model can be named anything that adheres to this regular expression:

[A-Za-z_][A-Za-z0-9_]*

Types

The type of a field determines its structure. A type falls in either of three categories:

Type modifiers

The type of a field can be modified by appending either of two modifiers:

  • []: Make a field a list
  • ?: Make a field optional

In the main example above, the field name on the User model is optional and the posts field is a list.

Lists can also be optional and will give the list a third state (which is null):

  • Blog[]: Empty list or non-empty list (default: [])
  • Blog[]?: null, empty list or non-empty list (default: null)

The default value for a required list is an empty list. The default value for an optional list is null.

Field attributes

Learn more about attributes below.

Embeds

Embeds are defined via the embed blocks in the datamodel and define structures that are embedded in a model. For a relational database this is often called an embedded type, for document databases, an embedded document.

Embeds are always included in the default selection set of the generated API Photon.

Named embeds

The example above defines only one embed (called Address) which is used exactly once on the User model:

model User {
  id        Int      @id
  address   Address?
}

embed Address {
  street  String
  zipCode String
}

Named embeds can be reused across multiple models.

Inline embeds

In the above example, the named embed Address is only used once. In this case, it is possible to omit the name and define the embed block directly inline:

model User {
  id        Int      @id
  address   embed {
    street  String
    zipCode String
  }?
}

Inline embeds can also be nested.

Enums

An enum describes a type that has a predefined set of values and is defined via an enum block:

enum Color {
  Red
  Teal
}

You can map the values of an enum to the respective values in the data source:

enum Color {
  Red  = "RED"
  Teal = "TEAL"
}

Prisma currently only supports string enum value types.

Type definitions

Type definitions use the type keyword. They can be used to consolidate various type specifications into a single type:

type Numeric = Float @pg.numeric(precision: 5, scale: 2)
                     @ms.decimal(precision: 5, scale: 2)

model User {
  id     Int     @id
  weight Numeric
}

Attributes

Attributes modify the behavior of a field or block (model, embed, ...). There are two ways to add attributes to your data model:

Depending on their signature, attributes may be called in the following cases:

Case 1. No arguments

  • Signature: @attribute
  • Description: Parenthesis must be omitted.
  • Examples:
    • @id
    • @unique
    • @updatedAt

Case 2. One positional argument

  • Signature: @attribute(_ p0: T0, p1: T1, ...)
  • Description: There may be up to one positional argument that doesn't need to be named.
  • Examples:
    • @field("my_column")
    • @default(10)
    • @createdAt(now())

For arrays with a single parameter, you may omit the surrounding brackets:

@attribute([email]) // is the same as
@attribute(email)

Case 3. Many named arguments

  • Signature: @attribute(_ p0: T0, p1: T1, ...)
  • Description: There may be any number of named arguments. If there is a positional argument, then it may appear anywhere in the function signature, but if it's present and required, the caller must place it before any named arguments. Named arguments may appear in any order.
  • Examples:
    • @@pg.index([ email, first_name ], name: "my_index", partial: true)
    • @@pg.index([ first_name, last_name ], unique: true, name: "my_index")
    • @@check(a > b, name: "a_b_constraint")
    • @pg.numeric(precision: 5, scale: 2)

You must not have multiple arguments with the same name:

// compiler error
@attribute(key: "a", key: "b")

For arrays with a single parameter, you may omit the surrounding brackets:

@attribute([item], key: [item]) // is the same as
@attribute(item, key: item)

Field attributes

Field attributes are marked by an @ prefix placed at the end of the field definition. A field can have any number of field arguments, potentially spanning multiple lines:

// A field with one attribute
model _ {
  myField String @attribute
}

// A field with two attributes
embed _ {
  myField String @attribute @attribute2
}

// A type definition with three attributes
type MyType String @attribute("input")
         @attribute2("input", key: "value", key2: "value2")
         @attribute3

Block attributes

Block attributes are marked by an @@ prefix placed anywhere inside a block. You can have as many block attributes as you want and they may also span multiple lines:

model \_ { @@attribute0

---

@@attribute1("input") @attribute2("input", key: "value", key2: "value2")

---

@@attribute3 }

embed \_ { @@attribute0

---

@@attribute1 @@attribute2("input") }

Core attributes

Core attributes must be implemented by every data source connector (with a best-effort implementation), this means they will be available in any Prisma setup.

They may be used in model and embed blocks as well as on type definitions.

Here is a list of all available core field attributes:

  • @id: Defines the primary key.
  • @unique: Defines a unique constraint.
  • @map(_ name: String): Defines the raw column name the field is mapped to.
  • @default(_ expr: Expr): Specifies a default value.
  • @relation(_ fields?: Field[], name?: String, onDelete?: CascadeEnum): Disambiguates relationships when needed. More details here.
  • @updatedAt: Updates the time to now() whenever a record is updated.

Here is a list of all available core block attributes:

  • @@map(_ name: String): Defines the raw table name the field is mapped to.

Connector attributes

Connector attributes let you use the native features of your data source. With a PostgreSQL database, you can use it for example to X.

Here is where you can find the documentation of connector attributes per data source connector:

Functions

Prisma core provides a set of functions that must be implemented by every connector with a best-effort implementation. Functions only work inside field and block attributes that accept them:

  • uuid(): Generates a fresh UUID
  • cuid(): Generates a fresh cuid
  • between(min, max): Generates a random int in the specified range
  • now(): Current date and time

Default values using a dynamic generator can be specified as follows:

model User {
  age        Int       @default(between([ 1, 5 ]))
  height     Float     @default(between([ 1, 5 ]))
  createdAt  DateTime  @default(now())
}

Functions will always be provided at the Prisma level by the query engine.

The data types that these functions return will be defined by the data source connectors. For example, now() in a PostgreSQL database will return a timestamp with time zone, while now() with a JSON connector would return an ISOString.

Scalar types

Prisma core provides the following scalar types:

Prisma Type Description
String Variable length text
Boolean True or false value
Int Integer value
Float Floating point number
DateTime Timestamp

The data source connector determines what native database type each of these types map to. Similarly, the generator determines what type in the target programming language each of these types map to.

Expand below to see the mappings per connector and generator.

Scalar mapping to connectors and generators

Connectors

Prisma Type PostgreSQL MySQL SQLite Mongo Raw JSON
String text TEXT TEXT string string
Boolean boolean BOOLEAN N/A bool boolean
Int integer INT INTEGER int32 number
Float real FLOAT REAL double number
DateTime timestamp TIMESTAMP N/A date N/A

N/A: Means that there is no perfect equivalent, but we can probably get pretty close.

Generators

Prisma Type JS / TS Go
String string string
Boolean boolean bool
Int number int
Float number float64
DateTime Date time.Time

Relations

Learn more about relations here.

Reserved model names

When generating Photon JS based on your data model definition, there are a number of reserved names that you can't use for your models. Here is a list of the reserved names:

  • String
  • Int
  • Float
  • Subscription
  • DateTime
  • WhereInput
  • IDFilter
  • StringFilter