- Owner: @matthewmueller
- Stakeholders: @schickling @mavilein
- State:
- Spec: In Progress 🚧
- Implementation: In Progress 🚧
The Prisma Schema declaratively describes the structure of your data sources. We use the Prisma Schema to generate Prisma Client libraries for data access, migrate your datasources with Lift and administer your data using Studio.
- Datasource Block
- Generator Block
- Model Block
- Field Names
- Data Types
- Core Data Type to Connector
- Core Data Type to Generator
- List Types
- Optional Types
- Relations
- One-to-One (1:1) Relationships
- One-to-Many (1:N) Relationships
- Implicit Many-to-Many (M:N) Relationships
- Explicit Many-to-Many (M:N) Relationships With or Without Extra Columns
- Self-Referential Relationships
- Multiple-Reference Relationships
- Referencing Primary Composite Keys
- Referencing fields that are not @id
- Attributes
- Comments
- Enum Block
- Env Function
- Function
- Auto Formatting
- FAQ
The datasource block tells the schema where the models are backed.
datasource pg {
provider = "postgresql"
url = env("DATABASE_URL")
}
provider
Can be one of the following built in datasource providers:postgresql
mysql
sqlite
url
Connection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. (more information see Datasource URLs)
Connectors may bring their own attributes to allow users to tailor their schemas according to specific features of their connected datasources.
Generator blocks configure which clients are generated and how they're generated. Language preferences and binary configuration will go in here:
generator js {
provider = "prisma-client-js"
target = "es3"
output = "./client"
}
generator ts {
target = "prisma-client-js"
provider = "./path/to/custom/generator"
}
generator go {
provider = "prisma-client-go"
}
provider
Can be a path or one of the following built in datasource providers:prisma-client-js
prisma-client-go
(⚠ This is not implemented yet.)
output
Path for the generated client
Generators may bring their own attributes.
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-glibc-libssl1.0.2"]
pinnedBinaryTarget = env("BINARY_TARGET") // On local, "native" and in production, "linux-glibc-libssl1.0.2"
}
Field | Description | Behavior |
---|---|---|
binaryTargets |
(optional) An array of binaries that are required by the application, string for known binary targets and path for custom binaries. | Declarative way to download the required binaries. |
pinnedBinaryTarget |
(optional) A string that points to the name of an object in the binaryTargets field, usually an environment variable |
Declarative way to choose the runtime binary |
- Both
binaryTargets
andpinnedBinaryTarget
fields are optional, however when a custom binary is provided thepinnedBinaryTarget
is required.
You can find more information about the binary configuration in the binary spec.
Models are the high-level entities of our business. They are the nouns: the User, the Comment, the Post and the Tweet.
Here's an example of the Model block:
model User {
id Int @id
email String @unique
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id
title String
draft Boolean
categories String[]
slug String
author User
comments Comment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([ title, slug ])
}
model Comment {
id Int @id
email String?
comment String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Field names are the first column of identifier inside the model block.
model _ {
id _
email _
comment _
createdAt _
updatedAt _
}
Field names are:
- Display name for the field
- Not opinionated on casing
- camel, snake, pascal are fine
- Name of the underlying field in the data source
- Unless there's a handwritten
@map
override - If introspected, exactly always the same
- Unless there's a handwritten
- Basis for client generation
- Generators may adjust casing depending on the language though
Prisma has several core primitive types. How these core types are defined may vary across connectors. Every connector must implement these core types. It's part of the connectors interface to Prisma. If a connector doesn't have a core type, it should provide a best-effort implementation.
Type | Description |
---|---|
String | Variable length text |
Boolean | True or false value |
Int | Integer value |
Float | Floating point number |
DateTime | Timestamp |
Here's how some of the databases we're tracking map to the core types:
Type | Postgres | MySQL | SQLite |
---|---|---|---|
String | text | TEXT | TEXT |
Boolean | boolean | BOOLEAN | N/A |
Int | integer | INT | INTEGER |
Float | real | FLOAT | REAL |
Datetime | timestamp | TIMESTAMP | N/A |
N/A: here means no perfect equivalent, but we can probably get pretty close.
Type | JS / TS | Go |
---|---|---|
String | string | string |
Boolean | boolean | bool |
Int | number | int |
Float | number | float64 |
DateTime | Date | time.Time |
Lists are denoted with []
at the end of a type. Whether they are supported by a given datasource depends on the type being used in the list:
- Lists are supported for relations and embeds by every connector.
- Lists are supported for primitive types and enums by a connector if the value can be stored within the record. This means that a retrieval of this field in a query must not incur any additional lookups in the database. This is not the case for every datasource. For example Postgres does support this but SQLite does not.
model User {
names String[]
ages Int[]
heights Float[]
}
The default value for a required list is an empty list.
If a connector does not support lists for primitive types it is possible to work around this limitation through relations. This makes the overhead of a query using this field transparent.
model User {
names UserName[]
}
model UserName {
name String
}
Most field types also support optional fields. By default, fields are required, but if you want to make them optional, you add a ?
at the end. Currently, the
only field type that is not nullable is the List Type.
model User {
names String[]
ages Int?
heights Float?
card Card?
}
enum Card {
Visa = "VISA"
Mastercard = "MASTERCARD"
}
The default output for a nullable field is null
.
Prisma provides a high-level syntax for defining relationships.
There are three kinds of relations: 1-1
, 1-m
and m-n
. In relational databases 1-1
and 1-m
is modeled the same way, and there is no built-in support
for m-n
relations.
Prisma core provides explicit support for all 3 relation types and connectors must ensure that their guarantees are upheld:
1-1
The return value on both sides is a single model, either optional or required. Prisma prevents accidentally storing multiple records in the relation.1-m
The return value on one side is a single model, either optional or required. On the other side is a list that might be empty.m-n
The return value on both sides is a list that might be empty. This is an improvement over the standard implementation in relational databases that require the application developer to deal with implementation details such as an intermediate table / join table. In Prisma, each connector will implement this concept in the way that is most efficient on the given storage engine and expose an API that hides the implementation details.
model User {
id Int @id
customer Customer?
name String
}
model Customer {
id Int @id
user User?
address String
}
For 1:1 relationships, it doesn't matter which side you store the foreign key. Therefore Prisma has a convention that the foreign key is added to the model who
appears first alphanumerically. In the example above, that's the Customer
model.
Under the hood, the models looks like this:
user | |
---|---|
id | integer |
name | text |
customer | |
---|---|
id | integer |
user | integer |
address | text |
You may omit either User.customer
or Customer.user
and the relationship will remain intact. This makes either the back-relation or the forward-relation
optional. If one side of the relation is missing, Prisma implies the field name based on the name of the model it is pointing to.
If you're introspecting an existing database and the foreign key does not follow the alphanumeric convention, then we'll use the
@relation(_ name: String?, references: Identifier[]?, onDelete: OnDeleteEnum?)
attribute to clarify.
model User {
id Int @id
customer Customer? @relation(references: id)
name String
}
model Customer {
id Int @id
user User?
address String
}
A writer can have multiple blogs.
model Writer {
id Int @id
blog Blog[]
}
model Blog {
id Int @id
author Writer
}
Blog.author
: points to the primary key on writer
Connectors for relational databases will implement this as two tables with a foreign-key constraint on the blog
table:
writer | |
---|---|
id | integer |
blog | |
---|---|
id | integer |
author | integer |
You may omit Blog.author
or Writer.blog
and the relationship will remain intact.
model Writer {
id Int @id
}
model Blog {
id Int @id
author Writer
}
For an implied has-many, a required list is added to Writer
. In this case blog Blog[]
. If a blog
field already exists, there is an error and you
must explicitly name the relation.
model Writer {
id Int @id
blog Blog[]
}
model Blog {
id Int @id
}
For an implied has-one, an optional relation is added to Blog
. In this case writer Writer?
. If a writer
field already exists, there is an error and
you must explicitly name the relation.
Blogs can have multiple writers and a writer can write many blogs. Prisma supports implicit join tables as a low-syntax way to get started.
model Blog {
id Int @id
author Writer[]
}
model Writer {
id Int @id
blog Blog[]
}
Connectors for relational databases should implement this as two data tables and a single join table. For data sources that support composite primary keys,
we'll use primary key(blog, writer)
to ensure that there can't be no more than one unique association.
Blog | |
---|---|
id | integer |
Writer | |
---|---|
id | integer |
_BlogtoWriter | |
---|---|
blog | integer |
writer | integer |
For implicit many-to-many relations, you must include both Blog.author
and Writer.blog
. If one of these fields is missing, Prisma will assume it's a
One-to-Many (1:N) relationship.
Many-to-many relationships are simply 2 one-to-many relationships.
model Blog {
id Int @id
blogWriter BlogWriter[]
}
model Writer {
id Int @id
blogWriter BlogWriter[]
}
// many to many
model BlogWriter {
blog Blog
author Writer
is_owner Boolean
@@id([author, blog])
}
Blog | |
---|---|
id | integer |
Writer | |
---|---|
id | integer |
BlogWriter | |
---|---|
blog_id | integer |
author_id | integer |
is_owner | boolean |
Note that the join table, in this case, BlogWriter
may contain extra fields like is_owner
.
Prisma supports self-referential relationships:
model Employee {
id Int @id
reportsTo Employee
}
Employee | |
---|---|
id | integer |
reports_to | integer |
Models may have multiple references to the same model. To prevent ambiguities, we explicitly name the foreign key field using a @relation
attribute:
model User {
id Int @id
asked Question[] @relation("Question_User_Asked")
answered Question[] @relation("Question_User_Answerered")
}
model Question {
id Int @id
asker User @relation("Question_User_Asked")
answerer User @relation("Question_User_Answerered")
}
You can also have relationships to composite primary keys
model Document {
@@id([ projectID, revision ])
projectID String @default('')
revision Int @default(1)
blocks Block[]
}
model Block {
id Int @id
document Document
}
Underneath:
documents | |
---|---|
project_id | text |
revision | int |
blocks | |
---|---|
id | Int |
document_project_id | text |
document_revision | int |
The @id
attribute marks the primary identifier of a model. If a model does not have a primary identifier or you want to reference another field, you can
specify the field using references
in the @relation
attribute
model Document {
projectID String @default('')
revision Int @default(1)
blocks Block[]
@@unique([projectID, revision])
}
model Block {
id Int @id
document Document @relation(references: [projectID, revision])
}
Attributes modify the behavior of a field or block. Field attributes are prefixed with a @
, while block attributes are prefixed with @@
.
Depending on their signature, attributes may be called in the following cases:
@attribute
: parenthesis must be omitted. Examples:
@id
@unique
@updatedAt
@attribute(_ p0: T0, p1: T1, ...)
: There may be up to one positional argument that doesn't need to be named.
@field("my_column")
@default(10)
For arrays with a single parameter, you may omit the surrounding brackets:
@attribute([email]) // is the same as
@attribute(email)
@attribute(p1: T1, p2: T2, _: T3, ...)
: 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:
@@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 are marked by an @
prefix placed at the end of the field definition. You can have as many field attributes as you want and they may also span
multiple lines:
model _ {
_ _ @attribute
}
embed _ {
_ _ @attribute @attribute2
}
type _ _ @attribute("input")
@attribute2("input", key: "value", key2: "value2")
@attribute3
Prisma supports the following core field attributes. Field attributes may be used in model
and embed
blocks as well as type
definitions. These attributes
must be implemented by every connector with a best-effort implementation:
Defines the primary key. There must be exactly one field @id
or block @id
Defines the unique constraint
Defines the raw column name the field is mapped to
Specifies a default value if null is provided
Specifies and disambiguates relationships when needed. Where possible on relational databases, the @relation
annotation will translate to a foreign key constraint, but not an index.
When a model contains a single relation to another model or itself, giving a name to the relation is optional and the @relation
directive can be completely
omitted.
There can be multiple distinct relationships between two models, or between a model and itself ("self relation"). When this is the case, the relationships must be named, so they can be distinguished.
Relation fields that do not clearly belong to a specific relationship constitute an ambiguous relation.
This is an example ambiguous relation on the schema of an imaginary simplified blogging platform:
model Blog {
id Int @id
author User[]
subscriber User[]
}
model User {
id Int @id
authorOf Blog[]
subscribedTo Blog[]
}
There are two relationships between Blog
and User
, so we need to name them to tell them apart. A valid version of this schema could look like this:
model Blog {
id Int @id
author User[] @relation("Authorship")
subscriber User[] @relation("Subscription")
}
model User {
id Int @id
authorOf Blog[] @relation("Authorship")
subscribedTo Blog[] @relation("Subscription")
}
- name: (optional, except when required for disambiguation) defines the name of the relationship. The name of the relation needs to be explicitly given to resolve amibiguities when the model contains two or more fields that refer to the same model (another model or itself).
- references: (optional) list of field names to reference
- Ambiguous relations: when one model contains two fields with an
@relation
directive pointing to another model, and both fields have the same relation name, or no relation name, the relation cannot be resolved and a validation error is emitted. - Ambiguous self relations: when one model contains two fields referencing the model itself without relation name to disambiguate that they should be seen as the same relation, they are considered ambiguous.
- Named relations with more than two fields are rejected, because there is no way to interpret them that makes sense.
Updates the time to now()
whenever the model is updated
Field attributes are marked by an @@
prefix placed anywhere inside the 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") }
Prisma supports the following core block attributes. Block attributes may be used in model
and embed
blocks. These attributes must be implemented by
every connector with a best-effort implementation:
@@map(_ name: String)
: Define the name of the underlying table or collection name@@id(_ fields: Identifier[])
: Defines a composite primary key across fields@@unique(_ fields: Identifier[], name: String?)
: Defines a composite unique constraint across fields@@index(_ fields: Identifier[], name: String?)
: Defines an index for multiple fields
There are 2 types of comments that are supported in the schema:
This comment is for the reader's clarity and is not present in the AST.
⚠ This is not implemented yet.
These comments as either field comments, model comments or as free-floating comments. Instrospection will pull comments on fields or models from the database. Lift will also update these comments in datasources that support them.
Here are some different examples:
/// This comment will get attached to the User model
model User {
/// This comment will get attached to the id field as a comment
id Int
// This comment is just for you
weight Float /// This comment gets attached to the weight field
}
// This comment is just for you. This comment will not
// show up in the AST.
/// This is a free-floating comment that will show up
/// in the AST as a Comment node, but is not attached
/// to any other node. We can use these for documentation
/// in the same way that godoc.org works.
model Customer {}
enum Color {
Red
Teal
}
Enums can include their corresponding value to determine what is stored by the datasource:
⚠ This is not implemented yet. See tracking issue
enum Color {
Red = "RED"
Teal = "TEAL"
}
PSL only supports String
enum value types.
The schema can require certain environment expectations to be met. The purpose of the env
function is to:
- Keeps secrets out of the schema
- Improve portability of the schema
datasource pg {
provider = "postgres"
url = env("DATABASE_URL")
}
Only functionality that actually requires the environment variable to be set will fail if it is missing. E.g. generate
will not require the environment variable:
$ prisma generate
But runtime will:
import { PrismaClient } from '@prisma/client'
const client = new PrismaClient()
// Thrown: required `DATABASE_URL` variable not found
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 UUIDcuid()
- generates a fresh cuidnow()
- current date and time
Default values using a dynamic generator can be specified as follows:
model User {
id String @default(uuid()) @id
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 connectors. For example, now()
in Postgres will return a timestamp with time zone
, while
now()
with a JSON connector would return an ISOString
.
Following the lead of gofmt and prettier, our syntax ships with a formatter for
.prisma
files.
Like gofmt
and unlike prettier
, we offer no options for configurability here. There is one way to format a prisma file.
This strictness serves two benefits:
- No bikeshedding. There's a saying in the Go community that, "Gofmt's style is nobody's favorite, but gofmt is everybody's favorite."
- No pull requests with different spacing schemes.
block _ {
key = "value"
key2 = 1
long_key = true
}
Formatting may be reset up by a newline.
block _ {
key = "value"
key2 = 1
key10 = true
long_key = true
long_key_2 = true
}
Multiline objects follow their own nested formatting rules:
block _ {
key = "value"
key2 = 1
key10 = {
a = "a"
b = "b"
}
key10 = [
1,
2
]
}
block _ {
id String @id
first_name LongNumeric @default
}
Multiline field attributes are properly aligned with the rest of the field attributes:
block _ {
id String @id
@default
first_name LongNumeric @default
}
Formatting may be reset by a newline.
block _ {
id String @id
@default
first_name LongNumeric @default
}
model RecipeIngredient {
recipe Recipe @id
ingredient Ingredient @id
amount Float
quantitativeDisclosures String
comment String
}
With this syntax, the @id
ordering is unclear and the
order of primary keys matters. Additionally, while it looks nice it doesn't
work for the other composite types. For example, @unique
twice:
model RecipeIngredient {
recipe Recipe @unique
ingredient Ingredient @unique
amount Float
quantitativeDisclosures String
comment String
}
Means those are unique across the table, while @unique([recipe, ingredient])
would mean that the combination of fields must be unique in the table:
model RecipeIngredient {
recipe Recipe
ingredient Ingredient
amount Float
quantitativeDisclosures String
comment String
@@unique([recipe, ingredient])
}