diff --git a/docs/200-orm/050-overview/100-introduction/100-what-is-prisma.mdx b/docs/200-orm/050-overview/100-introduction/100-what-is-prisma.mdx new file mode 100644 index 0000000000..dd091d68c0 --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/100-what-is-prisma.mdx @@ -0,0 +1,273 @@ +--- +title: 'What is Prisma?' +metaTitle: 'What is Prisma? (Overview)' +metaDescription: "This page gives a high-level overview of what Prisma is and how it works. It's a great starting point for Prisma newcomers!" +--- + +## What is Prisma? + +Prisma is an [open-source](https://github.com/prisma/prisma) next-generation ORM. It consists of the following parts: + +- **Prisma Client**: Auto-generated and type-safe query builder for Node.js & TypeScript +- **Prisma Migrate**: Migration system +- **Prisma Studio**: GUI to view and edit data in your database. + + + + **Prisma Studio** is the only part of Prisma ORM that is not open source. You can only run Prisma Studio locally. + + + +Prisma Client can be used in _any_ Node.js (supported versions) or TypeScript backend application (including serverless applications and microservices). This can be a [REST API](/orm/overview/prisma-in-your-stack/rest), a [GraphQL API](/orm/overview/prisma-in-your-stack/graphql), a gRPC API, or anything else that needs a database. + +
+ +
+ +## How does Prisma work? + +### The Prisma schema + +Every project that uses a tool from the Prisma toolkit starts with a [Prisma schema file](/orm/prisma-schema). The Prisma schema allows developers to define their _application models_ in an intuitive data modeling language. It also contains the connection to a database and defines a _generator_: + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + + + + +```prisma +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + posts Post[] +} +``` + + + + +> **Note**: The Prisma schema has powerful data modeling features. For example, it allows you to define "Prisma-level" [relation fields](/orm/prisma-schema/data-model/relations) which will make it easier to work with [relations in the Prisma Client API](/orm/prisma-client/queries/relation-queries). In the case above, the `posts` field on `User` is defined only on "Prisma-level", meaning it does not manifest as a foreign key in the underlying database. + +In this schema, you configure three things: + +- **Data source**: Specifies your database connection (via an environment variable) +- **Generator**: Indicates that you want to generate Prisma Client +- **Data model**: Defines your application models + +### The Prisma data model + +On this page, the focus is on the data model. You can learn more about [Data sources](/orm/prisma-schema/overview/data-sources) and [Generators](/orm/prisma-schema/overview/generators) on the respective docs pages. + +#### Functions of Prisma models + +The data model is a collection of [models](/orm/prisma-schema/data-model/models#defining-models). A model has two major functions: + +- Represent a table in relational databases or a collection in MongoDB +- Provide the foundation for the queries in the Prisma Client API + +#### Getting a data model + +There are two major workflows for "getting" a data model into your Prisma schema: + +- Manually writing the data model and mapping it to the database with [Prisma Migrate](/orm/prisma-migrate) +- Generating the data model by [introspecting](/orm/prisma-schema/introspection) a database + +Once the data model is defined, you can [generate Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client) which will expose CRUD and more queries for the defined models. If you're using TypeScript, you'll get full type-safety for all queries (even when only retrieving the subsets of a model's fields). + +### Accessing your database with Prisma Client + +#### Generating Prisma Client + +The first step when using Prisma Client is installing the `@prisma/client` npm package: + +```terminal +npm install @prisma/client +``` + +Installing the `@prisma/client` package invokes the `prisma generate` command, which reads your Prisma schema and _generates_ Prisma Client code. The code is [generated into the `node_modules/.prisma/client` folder by default](/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-prismaclient-npm-package). + +After you change your data model, you'll need to manually re-generate Prisma Client to ensure the code inside `node_modules/.prisma/client` gets updated: + +```terminal +prisma generate +``` + +#### Using Prisma Client to send queries to your database + +Once Prisma Client has been generated, you can import it in your code and send queries to your database. This is what the setup code looks like. + +##### Import and instantiate Prisma Client + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() +``` + + + + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() +``` + + + + +Now you can start sending queries via the generated Prisma Client API, here are a few sample queries. Note that all Prisma Client queries return _plain old JavaScript objects_. + +Learn more about the available operations in the [Prisma Client API reference](/orm/prisma-client). + +##### Retrieve all `User` records from the database + +```ts +// Run inside `async` function +const allUsers = await prisma.user.findMany() +``` + +##### Include the `posts` relation on each returned `User` object + +```ts +// Run inside `async` function +const allUsers = await prisma.user.findMany({ + include: { posts: true }, +}) +``` + +##### Filter all `Post` records that contain `"prisma"` + +```ts +// Run inside `async` function +const filteredPosts = await prisma.post.findMany({ + where: { + OR: [ + { title: { contains: 'prisma' } }, + { content: { contains: 'prisma' } }, + ], + }, +}) +``` + +##### Create a new `User` and a new `Post` record in the same query + +```ts +// Run inside `async` function +const user = await prisma.user.create({ + data: { + name: 'Alice', + email: 'alice@prisma.io', + posts: { + create: { title: 'Join us for Prisma Day 2020' }, + }, + }, +}) +``` + +##### Update an existing `Post` record + +```ts +// Run inside `async` function +const post = await prisma.post.update({ + where: { id: 42 }, + data: { published: true }, +}) +``` + +#### Usage with TypeScript + +Note that when using TypeScript, the result of this query will be _statically typed_ so that you can't accidentally access a property that doesn't exist (and any typos are caught at compile-time). Learn more about leveraging Prisma Client's generated types on the [Advanced usage of generated types](/orm/prisma-client/type-safety/operating-against-partial-structures-of-model-types) page in the docs. + +## Typical Prisma workflows + +As mentioned above, there are two ways for "getting" your data model into the Prisma schema. Depending on which approach you choose, your main Prisma workflow might look different. + +### Prisma Migrate + +With **Prisma Migrate**, Prisma's integrated database migration tool, the workflow looks as follows: + +1. Manually adjust your [Prisma data model](/orm/prisma-schema/data-model/models) +1. Migrate your development database using the `prisma migrate dev` CLI command +1. Use Prisma Client in your application code to access your database + +![Typical workflow with Prisma Migrate](/img/prisma-migrate-development-workflow.png) + +To learn more about the Prisma Migrate workflow, see: + +- [Deploying database changes with Prisma Migrate](/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate) + +* [Developing with Prisma Migrate](/orm/prisma-migrate) + +### SQL migrations and introspection + +If for some reason, you can not or do not want to use Prisma Migrate, you can still use introspection to update your Prisma schema from your database schema. +The typical workflow when using **SQL migrations and introspection** is slightly different: + +1. Manually adjust your database schema using SQL or a third-party migration tool +1. (Re-)introspect your database +1. Optionally [(re-)configure your Prisma Client API](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names)) +1. (Re-)generate Prisma Client +1. Use Prisma Client in your application code to access your database + +![Introspect workflow](/img/prisma-evolve-app-workflow.png) + +To learn more about the introspection workflow, please refer the [introspection section](/orm/prisma-schema/introspection). diff --git a/docs/200-orm/050-overview/100-introduction/200-why-prisma.mdx b/docs/200-orm/050-overview/100-introduction/200-why-prisma.mdx new file mode 100644 index 0000000000..2a895d874a --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/200-why-prisma.mdx @@ -0,0 +1,95 @@ +--- +title: 'Why Prisma?' +metaTitle: 'Why Prisma? Comparison with SQL query builders & ORMs' +metaDescription: 'Learn about the motivation for Prisma and how it compares to other Node.js and TypeScript database tools like ORMs and SQL query builders.' +--- + + + +On this page, you'll learn about the motivation for Prisma and how it compares to other database tools like ORMs and SQL query builders. + +Working with relational databases is a major bottleneck in application development. Debugging SQL queries or complex ORM objects often consume hours of development time. + +Prisma makes it easy for developers to reason about their database queries by providing a clean and type-safe API for submitting database queries which returns _plain old JavaScript objects_. + + + +## TLDR + +Prisma's main goal is to make application developers more productive when working with databases. Here are a few examples of how Prisma achieves this: + +- **Thinking in objects** instead of mapping relational data +- **Queries not classes** to avoid complex model objects +- **Single source of truth** for database and application models +- **Healthy constraints** that prevent common pitfalls and anti-patterns +- **An abstraction that makes the right thing easy** ("pit of success") +- **Type-safe database queries** that can be validated at compile time +- **Less boilerplate** so developers can focus on the important parts of their app +- **Auto-completion in code editors** instead of needing to look up documentation + +The remaining parts of this page discuss how Prisma compares to existing database tools. + +## Problems with SQL, ORMs and other database tools + +The main problem with the database tools that currently exist in the Node.js and TypeScript ecosystem is that they require a major tradeoff between _productivity_ and _control_. + +![Productivity vs Control in ORMs, SQL query builders, and SQL](node-js-db-tools-tradeoffs.png) + +### Raw SQL: Full control, low productivity + +With raw SQL (e.g. using the native [`pg`](https://node-postgres.com/) or [`mysql`](https://github.com/mysqljs/mysql#readme) Node.js database drivers) you have full control over your database operations. However, productivity suffers as sending plain SQL strings to the database is cumbersome and comes with a lot of overhead (manual connection handling, repetitive boilerplate, ...). + +Another major issue with this approach is that you don't get any type safety for your query results. Of course, you can type the results manually but this is a huge amount of work and requires major refactorings each time you change your database schema or queries to keep the typings in sync. + +Furthermore, submitting SQL queries as plain strings means you don't get any autocompletion in your editors. + +### SQL query builders: High control, medium productivity + +A common solution that retains a high level of control and provides better productivity is to use a SQL query builder (e.g. [knex.js](https://knexjs.org/)). These sort of tools provide a programmatic abstraction to construct SQL queries. + +The biggest drawback with SQL query builders is that application developers still need to think about their data in terms of SQL. This incurs a cognitive and practical cost of translating relational data into objects. Another issue is that it's too easy to shoot yourself in the foot if you don't know exactly what you're doing in your SQL queries. + +### ORMs: Less control, better productivity + +ORMs abstract away from SQL by letting you _define your application models as classes_, these classes are mapped to tables in the database. + +> "Object relational mappers" (ORMs) exist to bridge the gap between the programmers' friend (the object), and the database's primitive (the relation). The reasons for these differing models are as much cultural as functional: programmers like objects because they encapsulate the state of a single thing in a running program. Databases like relations because they better suit whole-dataset constraints and efficient access patterns for the entire dataset. +> +> [The Troublesome Active Record Pattern, Cal Paterson (2020)](https://calpaterson.com/activerecord.html) + +You can then read and write data by calling methods on the instances of your model classes. + +This is way more convenient and comes closer to the mental model developers have when thinking about their data. So, what's the catch? + +> ORM represents a quagmire which starts well, gets more complicated as time passes, and before long entraps its users in a commitment that has no clear demarcation point, no clear win conditions, and no clear exit strategy. +> +> [The Vietnam of Computer Science, Ted Neward (2006)](http://blogs.tedneward.com/post/the-vietnam-of-computer-science/) + +As an application developer, the mental model you have for your data is that of an _object_. The mental model for data in SQL on the other hand are _tables_. + +The divide between these two different representations of data is often referred to as the [object-relational impedance mismatch](https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch). The object-relational impedance mismatch also is a major reason why many developers don't like working with traditional ORMs. + +As an example, consider how data is organized and relationships are handled with each approach: + +- **Relational databases**: Data is typically normalized (flat) and uses foreign keys to link across entities. The entities then need to be JOINed to manifest the actual relationships. +- **Object-oriented**: Objects can be deeply nested structures where you can traverse relationships simply by using dot notation. + +This alludes to one of the major pitfalls with ORMs: While they make it _seem_ that you can simply traverse relationships using familiar dot notation, under the hood the ORM generates SQL JOINs which are expensive and have the potential to drastically slow down your application (one symptom of this is the [n+1 problem](https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping)). + +To conclude: The appeal of ORMs is the premise of abstracting away the relational model and thinking about your data purely in terms of objects. While the premise is great, it's based on the wrong assumption that relational data can easily be mapped to objects which leads to lots of complications and pitfalls. + +## Application developers should care about data – not SQL + +Despite being developed in the 1970s(!), SQL has stood the test of time in an impressive manner. However, with the advancement and modernization of developers tools, it's worth asking if SQL really is the best abstraction for application developers to work with? + +After all, **developers should only care about the _data_ they need to implement a feature** and not spend time figuring out complicated SQL queries or massaging query results to fit their needs. + +There's another argument to be made against SQL in application development. The power of SQL can be a blessing if you know exactly what you're doing, but its complexity can be a curse. There are a lot of [anti-patterns](https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back) and pitfalls that even experienced SQL users struggle to anticipate, often at the cost of performance and hours of debugging time. + +Developers should be able to ask for the data they need instead of having to worry about "doing the right thing" in their SQL queries. They should be using an abstraction that makes the right decisions for them. This can mean that the abstraction imposes certain "healthy" constraints that prevent developers from making mistakes. + +## Prisma makes developers productive + +Prisma's main goal is to make application developers more productive when working with databases. Considering the tradeoff between productivity and control again, this is how Prisma fits in: + +![Prisma makes developers productive](prisma-makes-devs-productive.png) diff --git a/docs/200-orm/050-overview/100-introduction/250-should-you-use-prisma.mdx b/docs/200-orm/050-overview/100-introduction/250-should-you-use-prisma.mdx new file mode 100644 index 0000000000..4c5bd7ec49 --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/250-should-you-use-prisma.mdx @@ -0,0 +1,112 @@ +--- +title: 'Should you use Prisma?' +metaTitle: 'Should you use Prisma as a Node.js/TypeScript ORM?' +metaDescription: 'Prisma is a new kind of ORM. This page explains when Prisma would be a good fit, and provides alternatives for other scenarios.' +tocDepth: 3 +toc: true +--- + + + +Prisma is a new kind of ORM that - like any other tool - comes with its own tradeoffs. This page explains when Prisma would be a good fit, and provides alternatives for other scenarios. + + + +## Prisma likely _is_ a good fit for you if ... + +### ... you are building a server-side application that talks to a database + +This is the main use case for Prisma. Server-side applications typically are API servers that expose data operations via technologies like REST, GraphQL or gRPC. They are commonly built as microservices or monolithic apps and deployed via long-running servers or serverless functions. Prisma is a great fit for all of these application and deployment models. + +Refer to the full list of databases (relational, NoSQL, and NewSQL) that Prisma [supports](/orm/reference/supported-databases). + +### ... you care about productivity and developer experience + +Productivity and developer experience are core to how we're building our tools. We're looking to build developer-friendly abstractions for tasks that are complex, error-prone and time-consuming when performed manually. + +No matter if you're a SQL newcomer or veteran, Prisma will give you a significant productivity boost for the most common database workflows. + +Here are a couple of the guiding principles and general practices we apply when designing and building our tools: + +- [make the right thing easy](https://www.jason.af/right-thing-easy-thing/) +- [pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/) +- offer intelligent autocompletion where possible +- build powerful editor extensions (e.g. for [VS Code](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma)) +- go the extra mile to achieve full type-safety + +### ... you are working in a team + +Prisma shines especially when used in collaborative environments. + +The declarative [Prisma schema](/orm/prisma-schema) provides an overview of the current state of the database that's easy to understand for everyone. This is a major improvement to traditional workflows where developers have to dig through migration files to understand the current table structure. + +[Prisma Client](/orm/prisma-client)'s minimal API surface enables developers to pick it up quickly without much learning overhead, so onboarding new developers to a team becomes a lot smoother. + +The [Prisma Migrate](/orm/prisma-migrate) workflows are designed in a way to cover database schema changes in collaborative environments. From the initial schema creation up to the point of deploying schema changes to production and resolving conflicts that were introduced by parallel modifications, Prisma Migrate has you covered. + +### ... you want a tool that holistically covers your database workflows + +Prisma is a lot more than "just another ORM". We are building a database toolkit that covers the daily workflows of application developers that interact with databases. A few examples are: + +- querying (with [Prisma Client](/orm/prisma-client)) +- data modeling (in the [Prisma schema](/orm/prisma-schema)) +- migrations (with [Prisma Migrate](/orm/prisma-migrate)) +- prototyping (via [`prisma db push`](/orm/reference/prisma-cli-reference#db-push)) +- seeding (via [`prisma db seed`](/orm/reference/prisma-cli-reference#db-seed)) +- visual viewing and editing (with [Prisma Studio](https://www.prisma.io/studio)) + +### ... you value type-safety + +Prisma is the only _fully_ type-safe ORM in the TypeScript ecosystem. The generated Prisma Client ensures typed query results even for partial queries and relations. You can learn more about this in the [type-safety comparison with TypeORM](/orm/more/comparisons/prisma-and-typeorm#type-safety). + +### ... you want an ORM with a transparent development process, proper maintenance & support + +Development of Prisma's open source tools is happening in the open. Most of it happens directly on GitHub in the main [`prisma/prisma`](https://github.com/prisma/prisma) repo: + +- issues and PRs in our repos are triaged and prioritized (usually within 1-2 days) +- there is a public [roadmap](https://pris.ly/roadmap) that is kept up to date with our plans +- new [releases](https://github.com/prisma/prisma/releases) with new features and improvements are issued every three weeks +- we have a dedicated support team that responds to questions in [GitHub Discussions](https://github.com/prisma/prisma/discussions) +- our product team is always eager to talk to you in the `#product-feedback` channel on Slack to get your feedback about Prisma + +### ... you want to be part of an awesome community + +Prisma has a lively [community](https://www.prisma.io/community), which you can find on [Slack](https://slack.prisma.io) and [Discord](https://discord.gg/KQyTW2H5ca). We also regularly host Meetups, conferences and other developer-focused events. Join us! + +## Prisma likely is _not_ a good fit for you if ... + +### ... you need _full_ control over all database queries + +Prisma is an abstraction. As such, an inherent tradeoff of Prisma is a reduced amount of control in exchange for higher productivity. This means, the [Prisma Client API](/orm/prisma-client) might have less capabilities in some scenarios than you get with plain SQL. + +If your application has requirements for database queries that Prisma does not provide and the workarounds are too costly, you might be better off with a tool that allows you to exercise full control over your database operations using plain SQL. + +> **Note**: If you can work around a certain limitation but still would like to see an improvement in the way how Prisma handles the situation, we encourage you to create a [feature request](https://github.com/prisma/prisma/issues/new?assignees=&labels=&template=feature_request.md&title=) on GitHub so that our Product and Engineering teams can look into it. + +_Alternatives_: SQL drivers (e.g. [`node-postgres`](https://node-postgres.com/), [`mysql`](https://github.com/mysqljs/mysql#readme), [`sqlite3`](https://github.com/mapbox/node-sqlite3#README), ...) + +### ... you do not want to write any code for your backend + +If you don't want to write any code for your backend and just be able to generate your API server and the database out-of-the-box, you might rather choose a Backend-as-a-Service (BaaS) for your project. + +With a BaaS, you can typically configure your data model via a high-level API (e.g. [GraphQL SDL](https://www.prisma.io/blog/graphql-sdl-schema-definition-language-6755bcb9ce51)) or a visual editor. Based on this data model, the BaaS generates a CRUD API and provisions a database for you. With this setup, you typically don't have control over the infrastructure the API server and database are running on. + +With Prisma, you are building the backend yourself using Node.js or TypeScript. This means you'll have to do a lot more coding work compared to using a BaaS. The benefit of this approach is that you have full flexibility for building, deploying, scaling and maintaining your backend and are not dependent on 3rd party software for a crucial part of your stack. + +_Alternatives_: [AWS AppSync](https://aws.amazon.com/appsync/), [8base](https://www.8base.com/), [Nhost](https://nhost.io/), [Supabase](https://supabase.com/), [Firebase](https://firebase.google.com/), [Amplication](https://amplication.com/) + +### ... you want a CRUD GraphQL API without writing any code + +While tools like the [`nexus-plugin-prisma`](https://nexusjs.org/docs/plugins/prisma/overview) and [`typegraphql-prisma`](https://github.com/MichalLytek/typegraphql-prisma#readme) allow you to quickly generate CRUD operations for your Prisma models in a GraphQL API, these approaches still require you to set up your GraphQL server manually and do some work to expose GraphQL queries and mutations for the models defined in your Prisma schema. + +If you want to get a GraphQL endpoint for your database out-of-the box, other tools might be better suited for your use case. + +_Alternatives_: [Hasura](https://hasura.io/), [Postgraphile](https://www.graphile.org/postgraphile/) + +### ... you want to use raw, type-safe SQL for querying your database + +While Prisma does allow you to [send plain SQL queries](/orm/prisma-client/queries/raw-database-access/raw-queries) to your database, it might not be the best fit if you prefer to work with a SQL-based abstraction that you want to be type-safe. Prisma's main benefit is to provide an abstraction layer that makes you more productive compared to writing SQL. + +If you're a solo developer that is very comfortable with SQL, and you just want to be sure that your database layer is type-safe, a lower-level TypeScript database library might be better for you. + +_Alternatives_: [Slonik](https://github.com/gajus/slonik), [pgtyped](https://github.com/adelsz/pgtyped), [Zapatos](https://jawj.github.io/zapatos/), [postgres-schema-builder](https://github.com/yss14/postgres-schema-builder) diff --git a/docs/200-orm/050-overview/100-introduction/300-data-modeling.mdx b/docs/200-orm/050-overview/100-introduction/300-data-modeling.mdx new file mode 100644 index 0000000000..ac5ff0e3a2 --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/300-data-modeling.mdx @@ -0,0 +1,246 @@ +--- +title: 'Data modeling' +metaTitle: 'Data modeling with Prisma' +metaDescription: 'Learn how data modeling with Prisma differs from data modeling with SQL or ORMs. Prisma uses a declarative data modeling language to describe a database schema.' +--- + +## What is data modeling? + +The term _data modeling_ refers to the **process of defining the shape and structure of the objects in an application**, these objects are often called "application models". In relational databases (like PostgreSQL), they are stored in _tables_ . When using document databases (like MongoDB), they are stored in _collections_. + +Depending on the domain of your application, the models will be different. For example, if you're writing a blogging application, you might have models such as _blog_, _author_, _article_. When writing a car-sharing app, you probably have models like _driver_, _car_, _route_. Application models enable you to represent these different entities in your code by creating respective _data structures_. + +When modeling data, you typically ask questions like: + +- What are the main entities/concepts in my application? +- How do they relate to each other? +- What are their main characteristics/properties? +- How can they be represented with my technology stack? + +## Data modeling without Prisma + +Data modeling typically needs to happen on (at least) two levels: + +- On the **database** level +- On the **application** level (i.e., in your programming language) + +The way that the application models are represented on both levels might differ due to a few reasons: + +- Databases and programming languages use different data types +- Relations are represented differently in a database than in a programming language +- Databases typically have more powerful data modeling capabilities, like indexes, cascading deletes, or a variety of additional constraints (e.g. unique, not null, ...) +- Databases and programming languages have different technical constraints + +### Data modeling on the database level + +#### Relational databases + +In relational databases, models are represented by _tables_. For example, you might define a `users` table to store information about the users of your application. Using PostgreSQL, you'd define it as follows: + +```sql +CREATE TABLE users ( + user_id SERIAL PRIMARY KEY NOT NULL, + name VARCHAR(255), + email VARCHAR(255) UNIQUE NOT NULL, + isAdmin BOOLEAN NOT NULL DEFAULT false +); +``` + +A visual representation of the `users` table with some random data might look as follows: + +| `user_id` | `name` | `email` | `isAdmin` | +| :-------- | :------ | :---------------- | :-------- | +| `1` | `Alice` | `alice@prisma.io` | `false` | +| `2` | `Bob` | `bob@prisma.io` | `false` | +| `3` | `Sarah` | `sarah@prisma.io` | `true` | + +It has the following columns: + +- `user_id`: An integer that increments with every new record in the `users` table. It also represents the [primary key](https://en.wikipedia.org/wiki/Primary_key) for each record. +- `name`: A string with at most 255 characters. +- `email`: A string with at most 255 characters. Additionally, the added constraints express that no two records can have duplicate values for the `email` column, and that _every_ record needs to have a value for it. +- `isAdmin`: A boolean that indicates whether the user has admin rights (default value: `false`) + +#### MongoDB + +In MongoDB databases, models are represented by _collections_ and contain _documents_ that can have any structure: + +```js +{ + _id: '607ee94800bbe41f001fd568', + slug: 'prisma-loves-mongodb', + title: 'Prisma <3 MongoDB', + body: "This is my first post. Isn't MongoDB + Prisma awesome?!" +} +``` + +Prisma Client currently expects a consistent model and [normalized model design](https://docs.mongodb.com/manual/core/data-model-design/#normalized-data-models). This means that: + +- If a model or field is not present in the Prisma schema, it is ignored +- If a field is mandatory but not present in the MongoDB dataset, you will get an error + +### Data modeling on the application level + +In addition to creating the tables that represent the entities from your application domain, you also need to create application models in your programming language. In object-oriented languages, this is often done by creating _classes_ to represent your models. Depending on the programming language, this might also be done with _interfaces_ or _structs_. + +There often is a strong correlation between the tables in your database and the models you define in your code. For example, to represent records from the aforementioned `users` table in your application, you might define a JavaScript (ES6) class looking similar to this: + +```js +class User { + constructor(user_id, name, email, isAdmin) { + this.user_id = user_id + this.name = name + this.email = email + this.isAdmin = isAdmin + } +} +``` + +When using TypeScript, you might define an interface instead: + +```js +interface User { + user_id: number + name: string + email: string + isAdmin: boolean +} +``` + +Notice how the `User` model in both cases has the same properties as the `users` table in the previous example. While it's often the case that there's a 1:1 mapping between database tables and application models, it can also happen that models are represented completely differently in the database and your application. + +With this setup, you can retrieve records from the `users` table and store them as instances of your `User` type. The following example code snippet uses [`pg`](https://node-postgres.com/) as the driver for PostgreSQL and creates a `User` instance based on the above defined JavaScript class: + +```js +const resultRows = await client.query('SELECT * FROM users WHERE user_id = 1') +const userData = resultRows[0] +const user = new User( + userData.user_id, + userData.name, + userData.email, + userData.isAdmin +) +// user = { +// user_id: 1, +// name: "Alice", +// email: "alice@prisma.io", +// isAdmin: false +// } +``` + +Notice that in these examples, the application models are "dumb", meaning they don't implement any logic but their sole purpose is to carry data as _plain old JavaScript objects_. + +### Data modeling with ORMs + +ORMs are commonly used in object-oriented languages to make it easier for developers to work with a database. The key characteristic of an ORM is that it lets you model your application data in terms of _classes_ which are mapped to _tables_ in the underlying database. + +The main difference compared to the approaches explained above is these classes not only carry data but also implement a substantial amount of logic. Mostly for storage, retrieval, serialization, and deserialization, but sometimes they also implement business logic that's specific to your application. + +This means, you don't write SQL statements to read and write data in the database, but instead the instances of your model classes provide an API to store and retrieve data. + +[Sequelize](https://sequelize.org/) is a popular ORM in the Node.js ecosystem, this is how you'd define the same `User` model from the sections before using Sequelize's modeling approach: + +```js +class User extends Model {} +User.init( + { + user_id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: Sequelize.STRING(255), + email: { + type: Sequelize.STRING(255), + unique: true, + }, + isAdmin: Sequelize.BOOLEAN, + }, + { sequelize, modelName: 'user' } +) +``` + +To get an example with this `User` class to work, you still need to create the corresponding table in the database. With Sequelize, you have two ways of doing this: + +- Run `User.sync()` (typically not recommended for production) +- Use [Sequelize migrations](https://sequelize.org/v5/manual/migrations.html) to change your database schema + +Note that you'll never instantiate the `User` class manually (using `new User(...)`) as was shown in the previous section, but rather call _static_ methods on the `User` class which then return the `User` model instances: + +```js +const user = await User.findByPk(42) +``` + +The call to `findByPk` creates a SQL statement to retrieve the `User` record that's identified by the ID value `42`. + +The resulting `user` object is an instance of Sequelize's `Model` class (because `User` inherits from `Model`). It's not a POJO, but an object that implements additional behavior from Sequelize. + +## Data modeling with Prisma + +Depending on which parts of Prisma you want to use in your application, the data modeling flow looks slightly different. The following two sections explain the workflows for using [**only Prisma Client**](#using-only-prisma-client) and using [**Prisma Client and Prisma Migrate**](#using-prisma-client-and-prisma-migrate). + +No matter which approach though, with Prisma you never create application models in your programming language by manually defining classes, interfaces, or structs. Instead, the application models are defined in your [Prisma schema](/orm/prisma-schema): + +- **Only Prisma Client**: Application models in the Prisma schema are _generated based on the introspection of your database schema_. Data modeling happens primarily on the database-level. +- **Prisma Client and Prisma Migrate**: Data modeling happens in the Prisma schema by _manually adding application models_ to it. Prisma Migrate maps these application models to tables in the underlying database (currently only supported for relational databases). + +As an example, the `User` model from the previous example would be represented as follows in the Prisma schema: + +```prisma +model User { + user_id Int @id @default(autoincrement()) + name String? + email String @unique + isAdmin Boolean @default(false) +} +``` + +Once the application models are in your Prisma schema (whether they were added through introspection or manually by you), the next step typically is to generate Prisma Client which provides a programmatic and type-safe API to read and write data in the shape of your application models. + +Prisma Client uses TypeScript [type aliases](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases) to represent your application models in your code. For example, the `User` model would be represented as follows in the generated Prisma Client library: + +```ts +export declare type User = { + id: number + name: string | null + email: string + isAdmin: boolean +} +``` + +In addition to the generated types, Prisma Client also provides a data access API that you can use once you've installed the `@prisma/client` package: + +```js +import { PrismaClient } from '@prisma/client' +// or +// const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +// use inside an `async` function to `await` the result +await prisma.user.findUnique(...) +await prisma.user.findMany(...) +await prisma.user.create(...) +await prisma.user.update(...) +await prisma.user.delete(...) +await prisma.user.upsert(...) +``` + +### Using only Prisma Client + +When using only Prisma Client and _not_ using Prisma Migrate in your application, data modeling needs to happen on the database level via SQL. Once your SQL schema is ready, you use Prisma's introspection feature to add the application models to your Prisma schema. Finally, you generate Prisma Client which creates the types as well as the programmatic API for you to read and write data in your database. + +Here is an overview of the main workflow: + +1. Change your database schema using SQL (e.g. `CREATE TABLE`, `ALTER TABLE`, ...) +1. Run `prisma db pull` to introspect the database and add application models to the Prisma schema +1. Run `prisma generate` to update your Prisma Client API + +### Using Prisma Client and Prisma Migrate + +When using [Prisma Migrate](/orm/prisma-migrate), you define your application in the Prisma schema and with relational databases use the `prisma migrate` subcommand to generate plain SQL migration files, which you can edit before applying. With MongoDB, you use `prisma db push` instead which applies the changes to your database directly. + +Here is an overview of the main workflow: + +1. Manually change your application models in the Prisma schema (e.g. add a new model, remove an existing one, ...) +1. Run `prisma migrate dev` to create and apply a migration or run `prisma db push` to apply the changes directly (in both cases Prisma Client is automatically generated) diff --git a/docs/200-orm/050-overview/100-introduction/index.mdx b/docs/200-orm/050-overview/100-introduction/index.mdx new file mode 100644 index 0000000000..90a1c1af59 --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/index.mdx @@ -0,0 +1,19 @@ +--- +title: 'Introduction' +metaTitle: 'Introduction (Overview)' +metaDescription: "This section gives a high-level overview of what Prisma is and how it works. It's a great starting point for Prisma newcomers!" +--- + + + +This page gives a high-level overview of what Prisma is and how it works. + +If you want to get started with a _practical introduction_ and learn about the Prisma Client API, head over to the [**Getting Started**](/getting-started) documentation. + +To learn more about the _motivation_ for Prisma, check out the [**Why Prisma?**](/orm/overview/introduction/why-prisma) page. + + + +## In this section + + diff --git a/docs/200-orm/050-overview/100-introduction/node-js-db-tools-tradeoffs.png b/docs/200-orm/050-overview/100-introduction/node-js-db-tools-tradeoffs.png new file mode 100644 index 0000000000..8037da2e08 Binary files /dev/null and b/docs/200-orm/050-overview/100-introduction/node-js-db-tools-tradeoffs.png differ diff --git a/docs/200-orm/050-overview/100-introduction/prisma-makes-devs-productive.png b/docs/200-orm/050-overview/100-introduction/prisma-makes-devs-productive.png new file mode 100644 index 0000000000..fcd68959c6 Binary files /dev/null and b/docs/200-orm/050-overview/100-introduction/prisma-makes-devs-productive.png differ diff --git a/docs/200-orm/050-overview/100-introduction/prisma-rest-apis.png b/docs/200-orm/050-overview/100-introduction/prisma-rest-apis.png new file mode 100644 index 0000000000..9e374a2fa0 Binary files /dev/null and b/docs/200-orm/050-overview/100-introduction/prisma-rest-apis.png differ diff --git a/docs/200-orm/050-overview/100-introduction/user-post-relation-1-n.png b/docs/200-orm/050-overview/100-introduction/user-post-relation-1-n.png new file mode 100644 index 0000000000..bab7611d32 Binary files /dev/null and b/docs/200-orm/050-overview/100-introduction/user-post-relation-1-n.png differ diff --git a/docs/200-orm/050-overview/100-introduction/user-table.png b/docs/200-orm/050-overview/100-introduction/user-table.png new file mode 100644 index 0000000000..a44df94e25 Binary files /dev/null and b/docs/200-orm/050-overview/100-introduction/user-table.png differ diff --git a/docs/200-orm/050-overview/100-introduction/user-table.svg b/docs/200-orm/050-overview/100-introduction/user-table.svg new file mode 100644 index 0000000000..1bc2e636a6 --- /dev/null +++ b/docs/200-orm/050-overview/100-introduction/user-table.svg @@ -0,0 +1 @@ +UseridPKfirst_name: stringlast_name: stringemail: stringemail_confirmed: boolbirthDate: date \ No newline at end of file diff --git a/docs/200-orm/050-overview/300-prisma-in-your-stack/01-rest.mdx b/docs/200-orm/050-overview/300-prisma-in-your-stack/01-rest.mdx new file mode 100644 index 0000000000..cb65d49966 --- /dev/null +++ b/docs/200-orm/050-overview/300-prisma-in-your-stack/01-rest.mdx @@ -0,0 +1,166 @@ +--- +title: 'REST' +metaTitle: 'Building REST APIs with Prisma' +metaDescription: 'This page gives an overview of the most important things when building REST APIs with Prisma. It shows practical examples and the supported libraries.' +--- + + + +When building REST APIs, Prisma Client can be used inside your _route controllers_ to send databases queries. + +![REST APIs with Prisma Client](../100-introduction/prisma-rest-apis.png) + + + +## Supported libraries + +As Prisma Client is "only" responsible for sending queries to your database, it can be combined with any HTTP server library or web framework of your choice. + +Here's a non-exhaustive list of libraries and frameworks you can use with Prisma: + +- [Express](https://expressjs.com/) +- [koa](https://koajs.com/) +- [hapi](https://hapi.dev/) +- [Fastify](https://www.fastify.io/) +- [Sails](https://sailsjs.com/) +- [AdonisJs](https://adonisjs.com/) +- [NestJS](https://nestjs.com/) +- [Next.js](https://nextjs.org/) +- [Foal TS](https://foalts.org/) +- [Polka](https://github.com/lukeed/polka) +- [Micro](https://github.com/zeit/micro) +- [Feathers](https://feathersjs.com/) +- [Remix](https://remix.run/) + +## REST API server example + +Assume you have a Prisma schema that looks similar to this: + +```prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +You can now implement route controller (e.g. using Express) that use the generated [Prisma Client API](/orm/prisma-client) to perform a database operation when an incoming HTTP request arrives. This page only shows few sample code snippets; if you want to run these code snippets, you can use a [REST API example](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-express). + +#### `GET` + +```ts +app.get('/feed', async (req, res) => { + const posts = await prisma.post.findMany({ + where: { published: true }, + include: { author: true }, + }) + res.json(posts) +}) +``` + +Note that the `feed` endpoint in this case returns a nested JSON response of `Post` objects that _include_ an `author` object. Here's a sample response: + +```json +[ + { + "id": "21", + "title": "Hello World", + "content": "null", + "published": "true", + "authorId": 42, + "author": { + "id": "42", + "name": "Alice", + "email": "alice@prisma.io" + } + } +] +``` + +#### `POST` + +```ts +app.post(`/post`, async (req, res) => { + const { title, content, authorEmail } = req.body + const result = await prisma.post.create({ + data: { + title, + content, + published: false, + author: { connect: { email: authorEmail } }, + }, + }) + res.json(result) +}) +``` + +#### `PUT` + +```ts +app.put('/publish/:id', async (req, res) => { + const { id } = req.params + const post = await prisma.post.update({ + where: { id: Number(id) }, + data: { published: true }, + }) + res.json(post) +}) +``` + +#### `DELETE` + +```ts +app.delete(`/post/:id`, async (req, res) => { + const { id } = req.params + const post = await prisma.post.delete({ + where: { + id: Number(id), + }, + }) + res.json(post) +}) +``` + +## Ready-to-run example projects + +You can find several ready-to-run examples that show how to implement a REST API with Prisma Client, as well as build full applications, in the [`prisma-examples`](https://github.com/prisma/prisma-examples/) repository. + +### TypeScript + +| **Example** | **Stack** | **Description** | +| ----------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------- | +| [`rest-express`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-express) | Backend only | REST API with Express for TypeScript | +| [`rest-fastify`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-fastify) | Backend only | REST API using Fastify and Prisma Client. | +| [`rest-hapi`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-hapi) | Backend only | REST API using hapi and Prisma Client | +| [`rest-nestjs`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nestjs) | Backend only | Nest.js app (Express) with a REST API | +| [`rest-nextjs-express`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-express) | Fullstack | Next.js app (React, Express) and Prisma Client | +| [`rest-nextjs-api-routes`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-api-routes) | Fullstack | Next.js app (React) with a REST API | +| [`rest-nextjs-api-routes-auth`](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-api-routes-auth) | Fullstack | Implement authentication using NextAuth.js | + +### JavaScript + +| **Example** | **Stack** | **Description** | +| ----------------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------- | +| [`rest-express`](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-express) | Backend only | REST API using Express and Prisma Client | +| [`rest-fastify`](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-fastify) | Backend only | REST API using Fastify and Prisma Client | +| [`rest-nextjs`](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-nextjs) | Fullstack | Next.js app (React) with a REST API | +| [`rest-nuxtjs`](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-nuxtjs) | Fullstack | App with NuxtJs using Vue (frontend), Express, and Prisma Client | diff --git a/docs/200-orm/050-overview/300-prisma-in-your-stack/02-graphql.mdx b/docs/200-orm/050-overview/300-prisma-in-your-stack/02-graphql.mdx new file mode 100644 index 0000000000..9482816f77 --- /dev/null +++ b/docs/200-orm/050-overview/300-prisma-in-your-stack/02-graphql.mdx @@ -0,0 +1,85 @@ +--- +title: 'GraphQL' +metaTitle: 'Building GraphQL servers with Prisma' +metaDescription: 'This page gives explains how to build GraphQL servers with Prisma. It shows how Prisma fits into the GraphQL ecosystem and provides practical examples.' +--- + + + +[GraphQL](https://graphql.org/) is a query language for APIs. It is often used as an alternative to RESTful APIs, but can also be used as an additional "gateway" layer on top of existing RESTful services. + +With Prisma, you can build GraphQL servers that connect to a database. Prisma is completely agnostic to the GraphQL tools you use. When building a GraphQL server, you can combine Prisma with tools like Apollo Server, GraphQL Yoga, TypeGraphQL, GraphQL.js, or pretty much any tool or library that you're using in your GraphQL server setup. + + + +## GraphQL servers under the hood + +A GraphQL server consists of two major components: + +- GraphQL schema (type definitions + resolvers) +- HTTP server + +Note that a GraphQL schema can be written code-first or SDL-first. Check out this [article](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3) to learn more about these two approaches. If you like the SDL-first approach but still want to make your code type-safe, check out [GraphQL Code Generator](https://graphql-code-generator.com/) to generate various type definitions based on SDL. + +The GraphQL schema and HTTP server are typically handled by separate libraries. Here is an overview of current GraphQL server tools and their purpose: + +| Library (npm package) | Purpose | Compatible with Prisma | Prisma integration | +| :-------------------- | :-------------------------- | :--------------------- | :----------------------------------------------------------------------------- | +| `graphql` | GraphQL schema (code-first) | Yes | No | +| `graphql-tools` | GraphQL schema (SDL-first) | Yes | No | +| `type-graphql` | GraphQL schema (code-first) | Yes | [`typegraphql-prisma`](https://www.npmjs.com/package/typegraphql-prisma) | +| `nexus` | GraphQL schema (code-first) | Yes | [`nexus-prisma`](https://graphql-nexus.github.io/nexus-prisma) _Early Preview_ | +| `apollo-server` | HTTP server | Yes | n/a | +| `express-graphql` | HTTP server | Yes | n/a | +| `fastify-gql` | HTTP server | Yes | n/a | +| `graphql-yoga` | HTTP server | Yes | n/a | + +In addition to these standalone and single-purpose libraries, there are several projects building integrated _application frameworks_: + +| Framework | Stack | Built by | Prisma | Description | +| :---------------------------------- | :-------- | :------------------------------------------------ | :--------------------- | :------------------------------------- | +| [Redwood.js](https://redwoodjs.com) | Fullstack | [Tom Preston-Werner](https://github.com/mojombo/) | Built on top of Prisma | _Bringing full-stack to the JAMstack._ | + +> **Note**: If you notice any GraphQL libraries/frameworks missing from the list, please let us know. + +## Prisma & GraphQL examples + +In the following section will find several ready-to-run examples that showcase how to use Prisma with different combinations of the tools mentioned in the table above. + +### TypeScript + +| Example | HTTP Server | GraphQL schema | Description | +| :------------------------------------------------------------------------------------------------------------------------------- | :---------------------- | :-------------- | :--------------------------------------------------------------------------------------------- | +| [GraphQL API (Pothos)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql) | `graphql-yoga` | `pothos` | GraphQL server based on [`graphql-yoga`](https://the-guild.dev/graphql/yoga-server) | +| [GraphQL API (SDL-first)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-sdl-first) | `graphql-yoga` | n/a | GraphQL server based on the SDL-first approach | +| [GraphQL API -- NestJs](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-nestjs) | `@nestjs/apollo` | n/a | GraphQL server based on [NestJS](https://nestjs.com/) | +| [GraphQL API -- NestJs (SDL-first)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-nestjs-sdl-first) | `@nestjs/apollo` | n/a | GraphQL server based on [NestJS](https://nestjs.com/) | +| [GraphQL API (Nexus)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-nexus) | `@apollo/server` | `nexus` | GraphQL server based on [`@apollo/server`](https://www.apollographql.com/docs/apollo-server) | +| [GraphQL API (TypeGraphQL)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-typegraphql) | `apollo-server` | `type-graphql` | GraphQL server based on the code-first approach of [TypeGraphQL](https://typegraphql.com/) | +| [GraphQL API (Auth)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-auth) | `apollo-server` | `nexus` | GraphQL server with email-password authentication & permissions | +| [Fullstack app](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-nextjs) | `graphql-yoga` | `pothos` | Fullstack app with Next.js (React), Apollo Client, GraphQL Yoga and Pothos | +| [GraphQL subscriptions](https://github.com/prisma/prisma-examples/tree/latest/typescript/subscriptions-pubsub) | `apollo-server` | `nexus` | GraphQL server implementing realtime GraphQL subscriptions | +| [GraphQL API -- Hapi](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-hapi) | `apollo-server-hapi` | `nexus` | GraphQL server based on [Hapi](https://hapi.dev/) | +| [GraphQL API -- Hapi (SDL-first)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-hapi-sdl-first) | `apollo-server-hapi` | `graphql-tools` | GraphQL server based on [Hapi](https://hapi.dev/) | +| [GraphQL API -- Fastify](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-fastify) | `fastify` & `mercurius` | n/a | GraphQL server based on [Fastify](https://fastify.io/) and [Mercurius](https://mercurius.dev/) | +| [GraphQL API -- Fastify (SDL-first)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-fastify-sdl-first) | `fastify` | `Nexus` | GraphQL server based on [Fastify](https://fastify.io/) and [Mercurius](https://mercurius.dev/) | + +### JavaScript (Node.js) + +| Demo | HTTP Server | GraphQL schema | Description | +| :------------------------------------------------------------------------------------------------------------ | :-------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------ | +| [GraphQL API (Apollo Server)](https://github.com/prisma/prisma-examples/tree/latest/javascript/graphql) | `apollo-server` | `nexus` | GraphQL server based on [`apollo-server`](https://www.apollographql.com/docs/apollo-server/) | +| [GraphQL API (Auth)](https://github.com/prisma/prisma-examples/tree/latest/javascript/graphql-auth) | `apollo-server` | `nexus` | GraphQL server with email-password authentication & permissions | +| [GraphQL API (SDL-first)](https://github.com/prisma/prisma-examples/tree/latest/javascript/graphql-sdl-first) | `apollo-server` | `graphql-tools` | GraphQL server based on the SDL-first approach of [`graphql-tools`](https://www.apollographql.com/docs/graphql-tools/) (Apollo) | + +## FAQ + +### What is Prisma's role in a GraphQL server? + +No matter which of the above GraphQL tools/libraries you use, Prisma is used inside your GraphQL resolvers to connect to your database. It has the same role that any other ORM or SQL query builder would have inside your resolvers. + +In the resolver of a GraphQL query, Prisma typically reads data from the database to return it in the GraphQL response. In the resolver of a GraphQL mutation, Prisma typically also writes data to the database (e.g. creating new or updating existing records). + +## Other GraphQL Resources + +Prisma curates [GraphQL Weekly](https://www.graphqlweekly.com/), a newsletter highlighting resources and updates from the GraphQL community. Subscribe to keep up-to-date with GraphQL articles, videos, tutorials, libraries, and more. diff --git a/docs/200-orm/050-overview/300-prisma-in-your-stack/03-fullstack.mdx b/docs/200-orm/050-overview/300-prisma-in-your-stack/03-fullstack.mdx new file mode 100644 index 0000000000..637f008e44 --- /dev/null +++ b/docs/200-orm/050-overview/300-prisma-in-your-stack/03-fullstack.mdx @@ -0,0 +1,122 @@ +--- +title: 'Fullstack' +metaTitle: 'Building fullstack applications with Prisma' +metaDescription: 'This page gives explains how to build fullstack applications with Prisma. It shows how Prisma fits in with fullstack frameworks and provides practical examples' +--- + + + +Fullstack frameworks, such as Next.js, Remix or SvelteKit, blur the lines between the server and the client. These frameworks also provide different patterns for fetching and mutating data on the server. + +You can query your database using Prisma Client, using your framework of choice, from the server-side part of your application. + + + +## Supported frameworks + +Here's a non-exhaustive list of frameworks and libraries you can use with Prisma: + +- [Next.js](https://nextjs.org/) +- [Remix](https://remix.run) +- [SvelteKit](https://kit.svelte.dev/) +- [Nuxt](https://nuxt.com/) +- [Redwood](https://redwoodjs.com/) +- [t3 stack — using tRPC](https://create.t3.gg/) +- [Wasp](https://wasp-lang.dev/) + +## Fullstack app example (e.g. Next.js) + +Assume you have a Prisma schema that looks similar to this: + +```prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +You can now implement the logic for querying your database using [Prisma Client API](/orm/prisma-client) inside `getServerSideProps`, `getStaticProps`, API routes, or using API libraries such as [tRPC](https://trpc.io/) and [GraphQL](https://graphql.org/). + +### ` getServerSideProps` + +```ts +// (in /pages/index.tsx) + +// Alternatively, you can use `getStaticProps` +// in place of `getServerSideProps`. +export const getServerSideProps = async () => { + const feed = await prisma.post.findMany({ + where: { + published: true, + }, + }) + return { props: { feed } } +} +``` + +Next.js will pass the props to your React component where you can display the data from your database. + +### API Routes + +```ts +// Fetch all posts (in /pages/api/posts.ts) +const prisma = new PrismaClient() + +export default async function handle(req, res) { + const posts = await prisma.post.findMany({ + where: { + published: true, + }, + }) + res.json(posts) +} +``` + +Note that you can use Prisma inside of Next.js API routes to send queries to your database – with REST, GraphQL, and tRPC. + +You can then fetch data and display it in your frontend. + +## Ready-to-run fullstack example projects + +You can find several ready-to-run examples that show how to fullstack apps with Prisma Client in the [`prisma-examples`](https://github.com/prisma/prisma-examples/) repository. + +### TypeScript + +| **Example** | **Description** | +| :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | +| [Next.js (API Routes)](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-api-routes) | Fullstack Next.js app using `getServerSideProps` & API Routes | +| [Next.js (GraphQL)](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-nextjs) | Fullstack Next.js app using GraphQL Yoga, Pothos, & Apollo Client | +| [Next.js (tRPC)](https://github.com/prisma/prisma-examples/tree/latest/typescript/trpc-nextjs) | Fullstack Next.js app using tRPC | +| [Next.js (API Routes with auth)](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-api-routes-auth) | Fullstack Next.js app using `getServerSideProps`, API Routes, & [NextAuth](https://next-auth.js.org/) | +| [Remix](https://github.com/prisma/prisma-examples/tree/latest/typescript/remix) | Fullstack Remix app using actions and loaders | +| [SvelteKit](https://github.com/prisma/prisma-examples/tree/latest/typescript/sveltekit) | Fullstack Sveltekit app using actions and loaders | +| [SvelteKit (REST API)](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-sveltekit) | Fullstack Sveltekit app using API routes | +| [Nuxt (REST API)](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nuxtjs) | Fullstack Nuxt app using API routes | + +### JavaScript + +| **Example** | **Description** | +| :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------ | +| [Next.js (API Routes)](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-nextjs) | Fullstack Next.js app using `getServerSideProps` & API Routes | +| [SvelteKit (REST API)](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-sveltekit) | Fullstack Sveltekit app using API routes | +| [Nuxt (REST API)](https://github.com/prisma/prisma-examples/tree/latest/javascript/rest-nuxtjs) | Fullstack Nuxt app using API routes | diff --git a/docs/200-orm/050-overview/300-prisma-in-your-stack/04-is-prisma-an-orm.mdx b/docs/200-orm/050-overview/300-prisma-in-your-stack/04-is-prisma-an-orm.mdx new file mode 100644 index 0000000000..274decd080 --- /dev/null +++ b/docs/200-orm/050-overview/300-prisma-in-your-stack/04-is-prisma-an-orm.mdx @@ -0,0 +1,445 @@ +--- +title: 'Is Prisma an ORM?' +metaTitle: 'Is Prisma an ORM? | What is an ORM?' +metaDescription: 'Learn about how Prisma implements the Data Mapper ORM pattern and how it achieves the same goal as traditional ORMs without requiring you to map classes to tables as traditional ORMs do.' +--- + + + +To answer the question briefly: _Yes, Prisma is a new kind of ORM that fundamentally differs from traditional ORMs and doesn't suffer from many of the problems commonly associated with these_. + +Traditional ORMs provide an object-oriented way for working with relational databases by mapping tables to _model classes_ in your programming language. This approach leads to many problems that are caused by the [object-relational impedance mismatch](https://en.wikipedia.org/wiki/Object%E2%80%93relational_impedance_mismatch). + +Prisma works fundamentally different compared to that. With Prisma, you define your models in the declarative [Prisma schema](/orm/prisma-schema) which serves as the single source of truth for your database schema and the models in your programming language. In your application code, you can then use Prisma Client to read and write data in your database in a type-safe manner without the overhead of managing complex model instances. This makes the process of querying data a lot more natural as well as more predictable since Prisma Client always returns plain JavaScript objects. + +In this article, you will learn in more detail about ORM patterns and workflows, how Prisma implements the Data Mapper pattern, and the benefits of Prisma's approach. + + + +## What are ORMs? + +If you're already familiar with ORMs, feel free to jump to the [next section](#prisma) on Prisma. + +### ORM Patterns - Active Record and Data Mapper + +ORMs provide a high-level database abstraction. They expose a programmatic interface through objects to create, read, delete, and manipulate data while hiding some of the complexity of the database. + +The idea with ORMs is that you define your models as **classes** that map to tables in a database. The classes and their instances provide you with a programmatic API to read and write data in the database. + +There are two common ORM patterns: [_Active Record_](https://en.wikipedia.org/wiki/Active_record_pattern) and [_Data Mapper_](https://en.wikipedia.org/wiki/Data_mapper_pattern) which differ in how they transfer data between objects and the database. While both patterns require you to define classes as the main building block, the most notable difference between the two is that the Data Mapper pattern decouples in-memory objects in the application code from the database and uses the data mapper layer to transfer data between the two. In practice, this means that with Data Mapper the in-memory objects (representing data in the database) don't even know that there’s a database present. + +#### Active Record + +_Active Record_ ORMs map model classes to database tables where the structure of the two representations is closely related, e.g. each field in the model class will have a matching column in the database table. Instances of the model classes wrap database rows and carry both the data and the access logic to handle persisting changes in the database. Additionally, model classes can carry business logic specific to the data in the model. + +The model class typically has methods that do the following: + +- Construct an instance of the model from an SQL query. +- Construct a new instance for later insertion into the table. +- Wrap commonly used SQL queries and return Active Record objects. +- Update the database and insert into it the data in the Active Record. +- Get and set the fields. +- Implement business logic. + +#### Data Mapper + +_Data Mapper_ ORMs, in contrast to Active Record, decouple the application's in-memory representation of data from the database's representation. The decoupling is achieved by requiring you to separate the mapping responsibility into two types of classes: + +- **Entity classes**: The application's in-memory representation of entities which have no knowledge of the database +- **Mapper classes**: These have two responsibilities: + - Transforming the data between the two representations. + - Generating the SQL necessary to fetch data from the database and persist changes in the database. + +Data Mapper ORMs allow for greater flexibility between the problem domain as implemented in code and the database. This is because the data mapper pattern allows you to hide the ways in which your database is implemented which isn’t an ideal way to think about your domain behind the whole data-mapping layer. + +One of the reasons that traditional data mapper ORMs do this is due to the structure of organizations where the two responsibilities would be handled by separate teams, e.g., [DBAs](https://en.wikipedia.org/wiki/Database_administrator) and backend developers. + +In reality, not all Data Mapper ORMs adhere to this pattern strictly. For example, [TypeORM](https://github.com/typeorm/typeorm/blob/master/docs/active-record-data-mapper.md#what-is-the-data-mapper-pattern), a popular ORM in the TypeScript ecosystem which supports both Active Record and Data Mapper, takes the following approach to Data Mapper: + +- Entity classes use decorators (`@Column`) to map class properties to table columns and are aware of the database. +- Instead of mapper classes, _repository_ classes are used for querying the database and may contain custom queries. Repositories use the decorators to determine the mapping between entity properties and database columns. + +Given the following `User` table in the database: + +![user-table](../100-introduction/user-table.png) + +This is what the corresponding entity class would look like: + +```ts +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ name: 'first_name' }) + firstName: string + + @Column({ name: 'last_name' }) + lastName: string + + @Column({ unique: true }) + email: string +} +``` + +### Schema migration workflows + +A central part of developing applications that make use of a database is changing the database schema to accommodate new features and to better fit the problem you're solving. In this section, we'll discuss what [schema migrations](https://www.prisma.io/dataguide/types/relational/what-are-database-migrations) are and how they affect the workflow. + +Because the ORM sits between the developer and the database, most ORMs provide a **migration tool** to assist with the creation and modification of the database schema. + +A migration is a set of steps to take the database schema from one state to another. The first migration usually creates tables and indices. Subsequent migrations may add or remove columns, introduce new indices, or create new tables. Depending on the migration tool, the migration may be in the form of SQL statements or programmatic code which will get converted to SQL statements (as with [ActiveRecord](https://guides.rubyonrails.org/active_record_migrations.html) and [SQLAlchemy](https://alembic.sqlalchemy.org/en/latest/tutorial.html#create-a-migration-script)). + +Because databases usually contain data, migrations assist you with breaking down schema changes into smaller units which helps avoid inadvertent data loss. + +Assuming you were starting a project from scratch, this is what a full workflow would look like: you create a migration that will create the `User` table in the database schema and define the `User` entity class as in the example above. + +Then, as the project progresses and you decide you want to add a new `salutation` column to the `User` table, you would create another migration which would alter the table and add the `salutation` column. + +Let's take a look at how that would look like with a TypeORM migration: + +```ts +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class UserRefactoring1604448000 implements MigrationInterface { + async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "User" ADD COLUMN "salutation" TEXT`) + } + + async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "User" DROP COLUMN "salutation"`) + } +} +``` + +Once a migration is carried out and the database schema has been altered, the entity and mapper classes must also be updated to account for the new `salutation` column. + +With TypeORM that means adding a `salutation` property to the `User` entity class: + +```ts highlight=17,18;normal +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ name: 'first_name' }) + firstName: string + + @Column({ name: 'last_name' }) + lastName: string + + @Column({ unique: true }) + email: string + + @Column() + salutation: string +} +``` + +Synchronizing such changes can be a challenge with ORMs because the changes are applied manually and are not easily verifiable programmatically. Renaming an existing column can be even more cumbersome and involve searching and replacing references to the column. + +> **Note:** Django's [makemigrations](https://docs.djangoproject.com/en/3.1/ref/django-admin/#django-admin-makemigrations) CLI generates migrations by inspecting changes in models which, similar to Prisma, does away with the synchronization problem. + +In summary, evolving the schema is a key part of building applications. With ORMs, the workflow for updating the schema involves using a migration tool to create a migration followed by updating the corresponding entity and mapper classes (depending on the implementation). As you'll see, Prisma takes a different approach to this. + +Now that you've seen what migrations are and how they fit into the development workflows, you will learn more about the benefits and drawbacks of ORMs. + +### Benefits of ORMs + +There are different reasons why developers choose to use ORMs: + +- ORMs facilitate implementing the domain model. The domain model is an object model that incorporates the behavior and data of your business logic. In other words, it allows you to focus on real business concepts rather than the database structure or SQL semantics. +- ORMs help reduce the amount of code. They save you from writing repetitive SQL statements for common CRUD (Create Read Update Delete) operations and escaping user input to prevent vulnerabilities such as SQL injections. +- ORMs require you to write little to no SQL (depending on your complexity you may still need to write the odd raw query). This is beneficial for developers who are not familiar with SQL but still want to work with a database. +- Many ORMs abstract database-specific details. In theory, this means that an ORM can make changing from one database to another easier. It should be noted that in practice applications rarely change the database they use. + +As with all abstractions that aim to improve productivity, there are also drawbacks to using ORMs. + +### Drawbacks of ORMs + +The drawbacks of ORMs are not always apparent when you start using them. This section covers some of the commonly accepted ones: + +- With ORMs, you form an object graph representation of database tables which may lead to the [object-relational impedance mismatch](https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch). This happens when the problem you are solving forms a complex object graph which doesn't trivially map to a relational database. Synchronizing between two different representations of data, one in the relational database, and the other in-memory (with objects) is quite difficult. This is because objects are more flexible and varied in the way they can relate to each other compared to relational database records. +- While ORMs handle the complexity associated with the problem, the synchronization problem doesn't go away. Any changes to the database schema or the data model require the changes to be mapped back to the other side. This burden is often on the developer. In the context of a team working on a project, database schema changes require coordination. +- ORMs tend to have a large API surface due to the complexity they encapsulate. The flip side of not having to write SQL is that you spend a lot of time learning how to use the ORM. This applies to most abstractions, however without understanding how the database works, improving slow queries can be difficult. +- Some _complex queries_ aren't supported by ORMs due to the flexibility that SQL offers. This problem is alleviated by raw SQL querying functionality in which you pass the ORM a SQL statement string and the query is run for you. + +Now that the costs and benefits of ORMs have been covered, you can better understand what Prisma is and how it fits in. + +## Prisma + +Prisma is a **next-generation ORM** that makes working with databases easy for application developers and features the following tools: + +- [**Prisma Client**](/orm/prisma-client): Auto-generated and type-safe database client for use in your application. +- [**Prisma Migrate**](/orm/prisma-migrate): A declarative data modeling and migration tool. +- [**Prisma Studio**](/orm/tools/prisma-studio): A modern GUI for browsing and managing data in your database. + +> **Note:** Since Prisma Client is the most prominent tool, we often refer to it as simply Prisma. + +The three tools use the [Prisma schema](/orm/prisma-schema) as a single source of truth for the database schema, your application's object schema, and the mapping between the two. It's defined by you and is your main configuration file for Prisma. + +Prisma makes you productive and confident in the software you're building with features such as _type safety_, rich auto-completion, and a natural API for fetching relations. + +In the next section, you will learn about how Prisma implements the Data Mapper ORM pattern. + +### How Prisma implements the Data Mapper pattern + +As mentioned earlier in the article, the Data Mapper pattern aligns well with organizations where the database and application are owned by different teams. + +With the rise of modern cloud environments with managed database services and DevOps practices, more teams embrace a cross-functional approach, whereby teams own both the full development cycle including the database and operational concerns. + +Prisma enables the evolution of the DB schema and object schema in tandem, thereby reducing the need for deviation in the first place, while still allowing you to keep your application and database somewhat decoupled using `@map` attributes. While this may seem like a limitation, it prevents the domain model's evolution (through the object schema) from getting imposed on the database as an afterthought. + +To understand how Prisma's implementation of the Data Mapper pattern differs conceptually to traditional Data Mapper ORMs, here's a brief comparison of their concepts and building blocks: + +| Concept | Description | Building block in traditional ORMs | Building block in Prisma | Source of truth in Prisma | +| --------------- | -------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------ | ------------------------------------ | +| Object schema | The in-memory data structures in your applications | Model classes | Generated TypeScript types | Models in the Prisma schema | +| Data Mapper | The code which transforms between the object schema and the database | Mapper classes | Generated functions in Prisma Client | @map attributes in the Prisma schema | +| Database schema | The structure of data in the database, e.g., tables and columns | SQL written by hand or with a programmatic API | SQL generated by Prisma Migrate | Prisma schema | + +Prisma aligns with the Data Mapper pattern with the following added benefits: + +- Reducing the boilerplate of defining classes and mapping logic by generating a Prisma Client based on the Prisma schema. +- Eliminating the synchronization challenges between application objects and the database schema. +- Database migrations are a first-class citizen as they're derived from the Prisma schema. + +Now that we've talked about the concepts behind Prisma's approach to Data Mapper, we can go through how the Prisma schema works in practice. + +### Prisma schema + +At the heart of Prisma's implementation of the Data Mapper pattern is the _Prisma schema_ – a single source of truth for the following responsibilities: + +- Configuring how Prisma connects to your database. +- Generating Prisma Client – the type-safe ORM for use in your application code. +- Creating and evolving the database schema with Prisma Migrate. +- Defining the mapping between application objects and database columns. + +Models in Prisma mean something slightly different to Active Record ORMs. With Prisma, models are defined in the Prisma schema as abstract entities which describe tables, relations, and the mappings between columns to properties in Prisma Client. + +As an example, here's a Prisma schema for a blog: + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? @map("post_content") + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +Here's a break down of the example above: + +- The `datasource` block defines the connection to the database. +- The `generator` block tells Prisma to generate Prisma Client for TypeScript and Node.js. +- The `Post` and `User` models map to database tables. +- The two models have a _1-n_ relation where each `User` can have many related `Post`s. +- Each field in the models has a type, e.g. the `id` has the type `Int`. +- Fields may contain field attributes to define: + - Primary keys with the `@id` attribute. + - Unique keys with the `@unique` attribute. + - Default values with the `@default` attribute. + - Mapping between table columns and Prisma Client fields with the `@map` attribute, e.g., the `content` field (which will be accessible in Prisma Client) maps to the `post_content` database column. + +The `User` / `Post` relation can be visualized with the following diagram: + +![1-n relation between User and Post](../100-introduction/user-post-relation-1-n.png) + +At a Prisma level, the `User` / `Post` relation is made up of: + +- The scalar `authorId` field, which is referenced by the `@relation` attribute. This field exists in the database table – it is the foreign key that connects Post and User. +- The two relation fields: `author` and `posts` **do not exist** in the database table. Relation fields define connections between models at the Prisma level and exist only in the Prisma schema and generated Prisma Client, where they are used to access the relations. + +The declarative nature of Prisma schema is concise and allows defining the database schema and corresponding representation in Prisma Client. + +In the next section, you will learn about Prisma's supported workflows. + +### Prisma workflow + +The workflow with Prisma is slightly different to traditional ORMs. You can use Prisma when building new applications from scratch or adopt it incrementally: + +- _New application_ (greenfield): Projects that have no database schema yet can use Prisma Migrate to create the database schema. +- _Existing application_ (brownfield): Projects that already have a database schema can be [introspected](/orm/prisma-schema/introspection) by Prisma to generate the Prisma schema and Prisma Client. This use-case works with any existing migration tool and is useful for incremental adoption. It's possible to switch to Prisma Migrate as the migration tool. However, this is optional. + +With both workflows, the Prisma schema is the main configuration file. + +#### Workflow for incremental adoption in projects with an existing database + +Brownfield projects typically already have some database abstraction and schema. Prisma can integrate with such projects by introspecting the existing database to obtain a Prisma schema that reflects the existing database schema and to generate Prisma Client. This workflow is compatible with any migration tool and ORM which you may already be using. If you prefer to incrementally evaluate and adopt, this approach can be used as part of a [parallel adoption strategy](https://en.wikipedia.org/wiki/Parallel_adoption). + +A non-exhaustive list of setups compatible with this workflow: + +- Projects using plain SQL files with `CREATE TABLE` and `ALTER TABLE` to create and alter the database schema. +- Projects using a third party migration library like [db-migrate](https://github.com/db-migrate/node-db-migrate) or [Umzug](https://github.com/sequelize/umzug). +- Projects already using an ORM. In this case, database access through the ORM remains unchanged while the generated Prisma Client can be incrementally adopted. + +In practice, these are the steps necessary to introspect an existing DB and generate Prisma Client: + +1. Create a `schema.prisma` defining the `datasource` (in this case, your existing DB) and `generator`: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://janedoe:janedoe@localhost:5432/hello-prisma" +} + +generator client { + provider = "prisma-client-js" +} +``` + +2. Run `prisma db pull` to populate the Prisma schema with models derived from your database schema. +3. (Optional) Customize [field and model mappings](/orm/prisma-schema/data-model/models#mapping-model-names-to-tables-or-collections) between Prisma Client and the database. +4. Run `prisma generate`. + +Prisma will generate Prisma Client inside the `node_modules` folder, from which it can be imported in your application. For more extensive usage documentation, see the [Prisma Client API](/orm/prisma-client) docs. + +To summarize, Prisma Client can be integrated into projects with an existing database and tooling as part of a parallel adoption strategy. New projects will use a different workflow detailed next. + +#### Workflow for new projects + +Prisma is different from ORMs in terms of the workflows it supports. A closer look at the steps necessary to create and change a new database schema is useful for understanding Prisma Migrate. + +Prisma Migrate is a CLI for declarative data modeling & migrations. Unlike most migration tools that come as part of an ORM, you only need to describe the current schema, instead of the operations to move from one state to another. Prisma Migrate infers the operations, generates the SQL and carries out the migration for you. + +This example demonstrates using Prisma in a new project with a new database schema similar to the blog example above: + +1. Create the Prisma schema: + +```prisma +// schema.prisma +datasource db { + provider = "postgresql" + url = "postgresql://janedoe:janedoe@localhost:5432/hello-prisma" +} + +generator client { + provider = "prisma-client-js" +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? @map("post_content") + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +2. Run `prisma migrate` to generate the SQL for the migration, apply it to the database, and generate Prisma Client. + +For any further changes to the database schema: + +1. Apply changes to the Prisma schema, e.g., add a `registrationDate` field to the `User` model +1. Run `prisma migrate` again. + +The last step demonstrates how declarative migrations work by adding a field to the Prisma schema and using Prisma Migrate to transform the database schema to the desired state. After the migration is run, Prisma Client is automatically regenerated so that it reflects the updated schema. + +If you don't want to use Prisma Migrate but still want to use the type-safe generated Prisma Client in a new project, see the next section. + +##### Alternative for new projects without Prisma Migrate + +It is possible to use Prisma Client in a new project with a third-party migration tool instead of Prisma Migrate. For example, a new project could choose to use the Node.js migration framework [db-migrate](https://github.com/db-migrate/node-db-migrate) to create the database schema and migrations and Prisma Client for querying. In essence, this is covered by the [workflow for existing databases](#workflow-for-incremental-adoption-in-projects-with-an-existing-database). + +## Accessing data with Prisma Client + +So far, the article covered the concepts behind Prisma, its implementation of the Data Mapper pattern, and the workflows it supports. In this last section, you will see how to access data in your application using Prisma Client. + +Accessing the database with Prisma Client happens through the query methods it exposes. All queries return plain old JavaScript objects. Given the blog schema from above, fetching a user looks as follows: + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +const user = await prisma.user.findUnique({ + where: { + email: 'alice@prisma.io', + }, +}) +``` + +In this query, the `findUnique` method is used to fetch a single row from the `User` table. By default, Prisma will return all the scalar fields in the `User` table. + +> **Note:** The example uses TypeScript to make full use of the type safety features offered by Prisma Client. However, Prisma also works with [JavaScript in Node.js](https://dev.to/prisma/productive-development-with-prisma-s-zero-cost-type-safety-4od2). + +Prisma Client maps queries and results to [structural types](https://en.wikipedia.org/wiki/Structural_type_system) by generating code from the Prisma schema. This means that `user` has an associated type in the generated Prisma Client: + +``` +export type User = { + id: number + email: string + name: string | null +} +``` + +This ensures that accessing a non-existent field will raise a type error. More broadly, it means that the result's type for every query is known ahead of running the query, which helps catch errors. For example, the following code snippet will raise a type error: + +```ts +console.log(user.lastName) // Property 'lastName' does not exist on type 'User'. +``` + +### Fetching relations + +Fetch relations with Prisma Client is done with the `include` option. For example, to fetch a user and their posts would be done as follows: + +```ts +const user = await prisma.user.findUnique({ + where: { + email: 'alice@prisma.io', + }, + include: { + posts: true, + }, +}) +``` + +With this query, `user`'s type will also include `Post`s which can be accessed with the `posts` array field: + +```ts +console.log(user.posts[0].title) +``` + +The example only scratches the surface of Prisma Client's API for [CRUD operations](/orm/prisma-client/queries/crud) which you can learn more about in the docs. The main idea is that all queries and results are backed by types and you have full control over how relations are fetched. + +## Conclusion + +In summary, Prisma is a new kind of Data Mapper ORM that differs from traditional ORMs and doesn't suffer from the problems commonly associated with them. + +Unlike traditional ORMs, with Prisma, you define the Prisma schema – a declarative single source of truth for the database schema and application models. All queries in Prisma Client return plain JavaScript objects which makes the process of interacting with the database a lot more natural as well as more predictable. + +Prisma supports two main workflows for starting new projects and adopting in an existing project. For both workflows, the Prisma schema is the main configuration file. + +Like all abstractions, both Prisma and other ORMs hide away some of the underlying details of the database with different assumptions. + +These differences and your use case all affect the workflow and cost of adoption. Hopefully understanding how they differ can help you make an informed decision. diff --git a/docs/200-orm/050-overview/300-prisma-in-your-stack/index.mdx b/docs/200-orm/050-overview/300-prisma-in-your-stack/index.mdx new file mode 100644 index 0000000000..3ba9ab5661 --- /dev/null +++ b/docs/200-orm/050-overview/300-prisma-in-your-stack/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Prisma in your stack' +metaTitle: 'How Prisma fits into your stack' +metaDescription: 'How Prisma fits into your stack' +toc: false +--- + + + +Prisma is an ORM that provides a fully type-safe API and simplified database access. You can use Prisma tools to build a GraphQL or REST API, or as part of a fullstack application - the extent to which you incorporate Prisma is up to you. + + + +## In this section + + diff --git a/docs/200-orm/050-overview/500-databases/200-database-drivers.mdx b/docs/200-orm/050-overview/500-databases/200-database-drivers.mdx new file mode 100644 index 0000000000..d9297fb4cc --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/200-database-drivers.mdx @@ -0,0 +1,132 @@ +--- +title: 'Database drivers' +metaTitle: 'Database drivers' +metaDescription: 'Learn how Prisma connects to your database using the built-in drivers and how you can use Prisma along with other JavaScript database drivers using driver adapters (Preview)' +tocDepth: 4 +--- + +## Default built-in drivers + +One of Prisma Client's components is the [Query Engine](/orm/more/under-the-hood/engines) . The Query Engine is responsible for transforming Prisma Client queries to SQL statements. The Query Engine connects to your database using the included drivers that don't require additional setup. The built-in drivers use TCP connections to connect to the database. + +![Query flow from the user application to the database with Prisma Client](./images/drivers/qe-query-execution-flow.png) + +## Driver adapters + +Prisma Client can connect and run queries against your database using JavaScript database drivers using **driver adapters**. Adapters act as _translators_ between Prisma Client and the JavaScript database driver. + +Prisma will use the Query Engine to transform the Prisma Client query to SQL and run the generated SQL queries via the JavaScript database driver. + +![Query flow from the user application to the database using Prisma Client and driver adapters](./images/drivers/qe-query-engine-adapter.png) + +There are 2 different types of driver adapters: + +- [Database driver adapters](#database-driver-adapters) +- [Serverless driver adapters](#serverless-driver-adapters) + +### Database driver adapters + +You can connect to your database using a Node.js-based driver from Prisma Client using a database driver adapter. Prisma maintains the following database driver adapters: + +- [PostgreSQL](/orm/overview/databases/postgresql#using-the-node-postgres-driver) +- [Turso](/orm/overview/databases/turso#how-to-connect-and-query-a-turso-database) + +### Serverless driver adapters + +Database providers, such as Neon and PlanetScale, allow you to connect to your database using other protocols besides TCP, such as HTTP and WebSockets. These database drivers are optimized for connecting to your database in serverless and edge environments. + +Prisma maintains the following serverless driver adapters: + +- [Neon](/orm/overview/databases/neon#how-to-use-neons-serverless-driver-with-prisma-preview) +- [PlanetScale](/orm/overview/databases/planetscale#how-to-use-the-planetscale-serverless-driver-with-prisma-preview) + +## Community maintained database driver adapters + +You can also build your own driver adapter for the database you're using. The following is a list of community maintained driver adapters: + +- [TiDB](https://github.com/tidbcloud/prisma-adapter) + +### How to use driver adapters + +To use this feature: + +1. Update the `previewFeatures` block in your schema to include the the `driverAdapters` Preview feature: + + ```prisma + generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] + } + ``` + +2. Generate Prisma Client: + + ```sh + npx prisma generate + ``` + +3. Refer to the following pages to learn more how to use the specific driver adapters with the specific database providers: + + - [Neon](/orm/overview/databases/neon#how-to-use-neons-serverless-driver-with-prisma-preview) + - [PlanetScale](/orm/overview/databases/planetscale#how-to-use-the-planetscale-serverless-driver-with-prisma-preview) + - [Turso](/orm/overview/databases/turso#how-to-connect-and-query-a-turso-database) + +### Driver adapters and custom output paths + +Since Prisma 5.9.0, when using the driver adapters Preview feature along with a [custom output path for Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client#using-a-custom-output-path), you cannot reference Prisma Client using a relative path. + +Let's assume you had `output` in your Prisma schema set to `../src/generated/client`: + +```prisma +generator client { + provider = "prisma-client-js" + output = "../src/generated/client" +} +``` + +What you should **not** do is reference that path relatively: + +```ts no-copy +// what not to do! +import { PrismaClient } from './src/generated/client' + +const client = new PrismaClient() +``` + +Instead, you will need to use a linked dependency. + + + + + +```terminal +npm add db@./src/generated/client +``` + + + + + +```terminal +pnpm add db@link:./src/generated/client +``` + + + + + +```terminal +yarn add db@link:./src/generated/client +``` + + + + + +Now you should be able to reference your generated client using `db`! + +```ts +import { PrismaClient } from 'db' + +const client = new PrismaClient() +``` diff --git a/docs/200-orm/050-overview/500-databases/300-postgresql.mdx b/docs/200-orm/050-overview/500-databases/300-postgresql.mdx new file mode 100644 index 0000000000..a1a48a64cd --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/300-postgresql.mdx @@ -0,0 +1,302 @@ +--- +title: 'PostgreSQL' +metaTitle: 'PostgreSQL database connector' +metaDescription: 'This page explains how Prisma can connect to a PostgreSQL database using the PostgreSQL database connector.' +tocDepth: 3 +--- + + + +The PostgreSQL data source connector connects Prisma to a [PostgreSQL](https://www.postgresql.org/) database server. + +By default, the PostgreSQL connector contains a database driver responsible for connecting to your database. You can use a [driver adapter](/orm/overview/databases/database-drivers#driver-adapters) (Preview) to connect to your database using a JavaScript database driver from Prisma Client. + + + +## Example + +To connect to a PostgreSQL database server, you need to configure a [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [Prisma schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `postgresql` data source connector. +- `url`: Specifies the [connection URL](#connection-url) for the PostgreSQL database server. In this case, an [environment variable is used](/orm/prisma-schema/overview#accessing-environment-variables-from-the-schema) to provide the connection URL. + +## Using the `node-postgres` driver + +As of [`v5.4.0`](https://github.com/prisma/prisma/releases/tag/5.4.0), you can use Prisma ORM with database drivers from the JavaScript ecosystem (instead of using Prisma ORM's built-in drivers). You can do this by using a [driver adapter](/orm/overview/databases/database-drivers). + +For PostgreSQL, [`node-postgres`](https://node-postgres.com) (`pg`) is one of the most popular drivers in the JavaScript ecosystem. It can be used with any PostgreSQL database that's accessed via TCP. + +This section explains how you can use it with Prisma ORM and the `@prisma/adapter-pg` driver adapter. + +### 1. Enable the `driverAdapters` Preview feature flag + +Since driver adapters are currently in [Preview](/orm/more/releases#preview), you need to enable its feature flag on the `datasource` block in your Prisma schema: + +```prisma +// schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +Once you have added the feature flag to your schema, re-generate Prisma Client: + +```terminal copy +npx prisma generate +``` + +### 2. Install the dependencies + +Next, install the `pg` package and Prisma ORM's driver adapter: + +```terminal copy +npm install pg +npm install @prisma/adapter-pg +``` + +### 3. Instantiate Prisma Client using the driver adapter + +Finally, when you instantiate Prisma Client, you need to pass an instance of Prisma ORM's driver adapter to the `PrismaClient` constructor: + +```ts copy +import { Pool } from 'pg' +import { PrismaPg } from '@prisma/adapter-pg' +import { PrismaClient } from '@prisma/client' + +const connectionString = `${process.env.DATABASE_URL}` + +const pool = new Pool({ connectionString }) +const adapter = new PrismaPg(pool) +const prisma = new PrismaClient({ adapter }) +``` + +Notice that this code requires the `DATABASE_URL` environment variable to be set to your PostgreSQL connection string. You can learn more about the connection string below. + +## Connection details + +### Connection URL + +Prisma is based on the [official PostgreSQL format](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for connection URLs, but does not support all arguments and includes additional arguments such as `schema`. Here's an overview of the components needed for a PostgreSQL connection URL: + +![Structure of the PostgreSQL connection URL](postgresql-connection-string.png) + +#### Base URL and path + +Here is an example of the structure of the _base URL_ and the _path_ using placeholder values in uppercase letters: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE +``` + +The following components make up the _base URL_ of your database, they are always required: + +| Name | Placeholder | Description | +| :------- | :---------- | :-------------------------------------------------------------------------------------------------------------- | +| Host | `HOST` | IP address/domain of your database server, e.g. `localhost` | +| Port | `PORT` | Port on which your database server is running, e.g. `5432` | +| User | `USER` | Name of your database user, e.g. `janedoe` | +| Password | `PASSWORD` | Password for your database user | +| Database | `DATABASE` | Name of the [database](https://www.postgresql.org/docs/12/manage-ag-overview.html) you want to use, e.g. `mydb` | + + + +You must [percentage-encode special characters](/orm/reference/connection-urls#special-characters). + + + +#### Arguments + +A connection URL can also take arguments. Here is the same example from above with placeholder values in uppercase letters for three _arguments_: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE?KEY1=VALUE&KEY2=VALUE&KEY3=VALUE +``` + +The following arguments can be used: + +| Argument name | Required | Default | Description | +| :--------------------- | :------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `schema` | **Yes** | `public` | Name of the [schema](https://www.postgresql.org/docs/12/ddl-schemas.html) you want to use, e.g. `myschema` | +| `connection_limit` | No | `num_cpus * 2 + 1` | Maximum size of the [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool) | +| `connect_timeout` | No | `5` | Maximum number of seconds to wait for a new connection to be opened, `0` means no timeout | +| `pool_timeout` | No | `10` | Maximum number of seconds to wait for a new connection from the pool, `0` means no timeout | +| `sslmode` | No | `prefer` | Configures whether to use TLS. Possible values: `prefer`, `disable`, `require` | +| `sslcert` | No | | Path of the server certificate. Certificate paths are [resolved relative to the `./prisma folder`](/orm/prisma-schema/overview/data-sources#securing-database-connections) | +| `sslidentity` | No | | Path to the PKCS12 certificate | +| `sslpassword` | No | | Password that was used to secure the PKCS12 file | +| `sslaccept` | No | `accept_invalid_certs` | Configures whether to check for missing values in the certificate. Possible values: `accept_invalid_certs`, `strict` | +| `host` | No | | Points to a directory that contains a socket to be used for the connection | +| `socket_timeout` | No | | Maximum number of seconds to wait until a single query terminates | +| `pgbouncer` | No | `false` | Configure the Engine to [enable PgBouncer compatibility mode](/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer) | +| `statement_cache_size` | No | `500` | Since 2.1.0: Specifies the number of [prepared statements](#prepared-statement-caching) cached per connection | +| `application_name` | No | | Since 3.3.0: Specifies a value for the application_name configuration parameter | +| `channel_binding` | No | `prefer` | Since 4.8.0: Specifies a value for the channel_binding configuration parameter | +| `options` | No | | Since 3.8.0: Specifies command line options to send to the server at connection start | + +As an example, if you want to connect to a schema called `myschema`, set the connection pool size to `5` and configure a timeout for queries of `3` seconds. You can use the following arguments: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=myschema&connection_limit=5&socket_timeout=3 +``` + +### Configuring an SSL connection + +You can add various parameters to the connection URL if your database server uses SSL. Here's an overview of the possible parameters: + +- `sslmode=(disable|prefer|require)`: + - `prefer` (default): Prefer TLS if possible, accept plain text connections. + - `disable`: Do not use TLS. + - `require`: Require TLS or fail if not possible. +- `sslcert=`: Path to the server certificate. This is the root certificate used by the database server to sign the client certificate. You need to provide this if the certificate doesn't exist in the trusted certificate store of your system. For Google Cloud this likely is `server-ca.pem`. Certificate paths are [resolved relative to the `./prisma folder`](/orm/prisma-schema/overview/data-sources#securing-database-connections) +- `sslidentity=`: Path to the PKCS12 certificate database created from client cert and key. This is the SSL identity file in PKCS12 format which you will generate using the client key and client certificate. It combines these two files in a single file and secures them via a password (see next parameter). You can create this file using your client key and client certificate by using the following command (using `openssl`): + ``` + openssl pkcs12 -export -out client-identity.p12 -inkey client-key.pem -in client-cert.pem + ``` +- `sslpassword=`: Password that was used to secure the PKCS12 file. The `openssl` command listed in the previous step will ask for a password while creating the PKCS12 file, you will need to provide that same exact password here. +- `sslaccept=(strict|accept_invalid_certs)`: + - `strict`: Any missing value in the certificate will lead to an error. For Google Cloud, especially if the database doesn't have a domain name, the certificate might miss the domain/IP address, causing an error when connecting. + - `accept_invalid_certs` (default): Bypass this check. Be aware of the security consequences of this setting. + +Your database connection URL will look similar to this: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslidentity=client-identity.p12&sslpassword=mypassword&sslcert=rootca.cert +``` + +### Connecting via sockets + +To connect to your PostgreSQL database via sockets, you must add a `host` field as a _query parameter_ to the connection URL (instead of setting it as the `host` part of the URI). +The value of this parameter then must point to the directory that contains the socket, e.g.: `postgresql://USER:PASSWORD@localhost/database?host=/var/run/postgresql/` + +Note that `localhost` is required, the value itself is ignored and can be anything. + +> **Note**: You can find additional context in this [GitHub issue](https://github.com/prisma/prisma-client-js/issues/437#issuecomment-592436707). + +## Type mapping between PostgreSQL and Prisma schema + +These two tables show the type mapping between PostgreSQL and Prisma schema. First [how Prisma scalar types are translated into PostgreSQL database column types](#mapping-between-prisma-scalar-types-and-postgresql-database-column-types), and then [how PostgreSQL database column types relate to Prisma scalar and native types](#mapping-between-postgresql-database-column-types-to-prisma-scalar-and-native-types). + +> Alternatively, see [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for type mappings organized by Prisma type. + +### Mapping between Prisma scalar types and PostgreSQL database column types + +The PostgreSQL connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the Prisma [data model](/orm/prisma-schema/data-model/models) as follows to database column types: + +| Prisma | PostgreSQL | +| ---------- | ------------------ | +| `String` | `text` | +| `Boolean` | `boolean` | +| `Int` | `integer` | +| `BigInt` | `bigint` | +| `Float` | `double precision` | +| `Decimal` | `decimal(65,30)` | +| `DateTime` | `timestamp(3)` | +| `Json` | `jsonb` | +| `Bytes` | `bytea` | + +### Mapping between PostgreSQL database column types to Prisma scalar and native types + +- When [introspecting](/orm/prisma-schema/introspection) a PostgreSQL database, the database types are mapped to Prisma types according to the following table. +- When [creating a migration](/orm/prisma-migrate) or [prototyping your schema](/orm/prisma-migrate/workflows/prototyping-your-schema) the table is also used - in the other direction. + +| PostgreSQL (Type \| Aliases) | Supported | Prisma | Native database type attribute | Notes | +| ------------------------------------------- | :-------: | ------------- | :--------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `bigint` \| `int8` | ✔️ | `BigInt` | `@db.BigInt`\* | \*Default mapping for `BigInt` - no type attribute added to schema. | +| `boolean` \| `bool` | ✔️ | `Bool` | `@db.Boolean`\* | \*Default mapping for `Bool` - no type attribute added to schema. | +| `timestamp with time zone` \| `timestamptz` | ✔️ | `DateTime` | `@db.Timestamptz(x)` | +| `time without time zone` \| `time` | ✔️ | `DateTime` | `@db.Time(x)` | +| `time with time zone` \| `timetz` | ✔️ | `DateTime` | `@db.Timetz(x)` | +| `numeric(p,s)` \| `decimal(p,s)` | ✔️ | `Decimal` | `@db.Decimal(x, y)` | +| `real` \| `float`, `float4` | ✔️ | `Float` | `@db.Real` | +| `double precision` \| `float8` | ✔️ | `Float` | `@db.DoublePrecision`\* | \*Default mapping for `Float` - no type attribute added to schema. | +| `smallint` \| `int2` | ✔️ | `Int` | `@db.SmallInt` | | +| `integer` \| `int`, `int4` | ✔️ | `Int` | `@db.Int`\* | \*Default mapping for `Int` - no type attribute added to schema. | +| `smallserial` \| `serial2` | ✔️ | `Int` | `@db.SmallInt @default(autoincrement())` | +| `serial` \| `serial4` | ✔️ | `Int` | `@db.Int @default(autoincrement())` | +| `bigserial` \| `serial8` | ✔️ | `Int` | `@db.BigInt @default(autoincrement()` | +| `character(n)` \| `char(n)` | ✔️ | `String` | `@db.Char(x)` | +| `character varying(n)` \| `varchar(n)` | ✔️ | `String` | `@db.VarChar(x)` | +| `money` | ✔️ | `Decimal` | `@db.Money` | +| `text` | ✔️ | `String` | `@db.Text`\* | \*Default mapping for `String` - no type attribute added to schema. | +| `timestamp` | ✔️ | `DateTime` | `@db.TimeStamp`\* | \*Default mapping for `DateTime` - no type attribute added to schema. | +| `date` | ✔️ | `DateTime` | `@db.Date` | +| `enum` | ✔️ | `Enum` | N/A | +| `inet` | ✔️ | `String` | `@db.Inet` | +| `bit(n)` | ✔️ | `String` | `@Bit(x)` | +| `bit varying(n)` | ✔️ | `String` | `@VarBit` | +| `oid` | ✔️ | `Int` | `@db.Oid` | +| `uuid` | ✔️ | `String` | `@db.Uuid` | +| `json` | ✔️ | `Json` | `@db.Json` | +| `jsonb` | ✔️ | `Json` | `@db.JsonB`\* | \*Default mapping for `Json` - no type attribute added to schema. | +| `bytea` | ✔️ | `Bytes` | `@db.ByteA`\* | \*Default mapping for `Bytes` - no type attribute added to schema. | +| `xml` | ✔️ | `String` | `@db.Xml` | +| Array types | ✔️ | `[]` | +| `citext` | ✔️\* | `String` | `@db.Citext` | \* Only available if [Citext extension is enabled](/orm/prisma-schema/data-model/unsupported-database-features#enable-postgresql-extensions-for-native-database-functions). | +| `interval` | Not yet | `Unsupported` | | | +| `cidr` | Not yet | `Unsupported` | | | +| `macaddr` | Not yet | `Unsupported` | | | +| `tsvector` | Not yet | `Unsupported` | | | +| `tsquery` | Not yet | `Unsupported` | | | +| `int4range` | Not yet | `Unsupported` | | | +| `int8range` | Not yet | `Unsupported` | | | +| `numrange` | Not yet | `Unsupported` | | | +| `tsrange` | Not yet | `Unsupported` | | | +| `tstzrange` | Not yet | `Unsupported` | | | +| `daterange` | Not yet | `Unsupported` | | | +| `point` | Not yet | `Unsupported` | | | +| `line` | Not yet | `Unsupported` | | | +| `lseg` | Not yet | `Unsupported` | | | +| `box` | Not yet | `Unsupported` | | | +| `path` | Not yet | `Unsupported` | | | +| `polygon` | Not yet | `Unsupported` | | | +| `circle` | Not yet | `Unsupported` | | | +| Composite types | Not yet | n/a | | | +| Domain types | Not yet | n/a | | | + +[Introspection](/orm/prisma-schema/introspection) adds native database types that are **not yet supported** as [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) fields: + +```prisma file=schema.prisma +model Device { + id Int @id @default(autoincrement()) + name String + data Unsupported("circle") +} +``` + +## Prepared statement caching + +A [prepared statement](https://www.postgresql.org/docs/current/sql-prepare.html) is a feature that can be used to optimize performance. A prepared statement is parsed, compiled, and optimized only once and then can be executed directly multiple times without the overhead of parsing the query again. + +By caching prepared statements, Prisma Client's [query engine](/orm/more/under-the-hood/engines) does not repeatedly compile the same query which reduces database CPU usage and query latency. + +For example, here is the generated SQL for two different queries made by Prisma Client: + +```sql +SELECT * FROM user WHERE name = "John"; +SELECT * FROM user WHERE name = "Brenda"; +``` + +The two queries after parameterization will be the same, and the second query can skip the preparing step, saving database CPU and one extra roundtrip to the database. Query after parameterization: + +```sql +SELECT * FROM user WHERE name = $1 +``` + +Every database connection maintained by Prisma has a separate cache for storing prepared statements. The size of this cache can be tweaked with the `statement_cache_size` parameter in the connection string. By default, Prisma Client caches 500 statements per connection. + +Due to the nature of pgBouncer, if the `pgbouncer` parameter is set to `true`, the prepared statement cache is automatically disabled for that connection. diff --git a/docs/200-orm/050-overview/500-databases/400-mysql.mdx b/docs/200-orm/050-overview/500-databases/400-mysql.mdx new file mode 100644 index 0000000000..cabbaee1e0 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/400-mysql.mdx @@ -0,0 +1,206 @@ +--- +title: 'MySQL' +metaTitle: 'MySQL database connector' +metaDescription: 'This page explains how Prisma can connect to a MySQL database using the MySQL database connector.' +tocDepth: 3 +--- + + + +The MySQL data source connector connects Prisma to a [MySQL](https://www.mysql.com/) database server. + +By default, the MySQL connector contains a database driver responsible for connecting to your database. You can use a [driver adapter](/orm/overview/databases/database-drivers#driver-adapters) (Preview) to connect to your database using a JavaScript database driver from Prisma Client. + + + +## Example + +To connect to a MySQL database server, you need to configure a [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [Prisma schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `mysql` data source connector. +- `url`: Specifies the [connection URL](#connection-url) for the MySQL database server. In this case, an [environment variable is used](/orm/prisma-schema/overview#accessing-environment-variables-from-the-schema) to provide the connection URL. + +## Connection details + +### Connection URL + +Here's an overview of the components needed for a MySQL connection URL: + +![Structure of the MySQL connection URL](mysql-connection-string.png) + +#### Base URL and path + +Here is an example of the structure of the _base URL_ and the _path_ using placeholder values in uppercase letters: + +``` +mysql://USER:PASSWORD@HOST:PORT/DATABASE +``` + +The following components make up the _base URL_ of your database, they are always required: + +| Name | Placeholder | Description | +| :------- | :---------- | :------------------------------------------------------------------------------------------------------------------ | +| Host | `HOST` | IP address/domain of your database server, e.g. `localhost` | +| Port | `PORT` | Port on which your database server is running, e.g. `5432` | +| User | `USER` | Name of your database user, e.g. `janedoe` | +| Password | `PASSWORD` | Password for your database user | +| Database | `DATABASE` | Name of the [database](https://dev.mysql.com/doc/refman/8.0/en/creating-database.html) you want to use, e.g. `mydb` | + + + +You must [percentage-encode special characters](/orm/reference/connection-urls#special-characters). + + + +#### Arguments + +A connection URL can also take arguments. Here is the same example from above with placeholder values in uppercase letters for three _arguments_: + +``` +mysql://USER:PASSWORD@HOST:PORT/DATABASE?KEY1=VALUE&KEY2=VALUE&KEY3=VALUE +``` + +The following arguments can be used: + +| Argument name | Required | Default | Description | +| :----------------- | :------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `connection_limit` | No | `num_cpus * 2 + 1` | Maximum size of the [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool) | +| `connect_timeout` | No | `5` | Maximum number of seconds to wait for a new connection to be opened, `0` means no timeout | +| `pool_timeout` | No | `10` | Maximum number of seconds to wait for a new connection from the pool, `0` means no timeout | +| `sslcert` | No | | Path to the server certificate. Certificate paths are [resolved relative to the `./prisma folder`](/orm/prisma-schema/overview/data-sources#securing-database-connections) | +| `sslidentity` | No | | Path to the PKCS12 certificate | +| `sslpassword` | No | | Password that was used to secure the PKCS12 file | +| `sslaccept` | No | `accept_invalid_certs` | Configures whether to check for missing values in the certificate. Possible values: `accept_invalid_certs`, `strict` | +| `socket` | No | | Points to a directory that contains a socket to be used for the connection | +| `socket_timeout` | No | | Number of seconds to wait until a single query terminates | + +As an example, if you want to set the connection pool size to `5` and configure a timeout for queries of `3` seconds, you can use the following arguments: + +``` +mysql://USER:PASSWORD@HOST:PORT/DATABASE?connection_limit=5&socket_timeout=3 +``` + +### Configuring an SSL connection + +You can add various parameters to the connection URL if your database server uses SSL. Here's an overview of the possible parameters: + +- `sslcert=`: Path to the server certificate. This is the root certificate used by the database server to sign the client certificate. You need to provide this if the certificate doesn't exist in the trusted certificate store of your system. For Google Cloud this likely is `server-ca.pem`. Certificate paths are [resolved relative to the `./prisma folder`](/orm/prisma-schema/overview/data-sources#securing-database-connections) + +- `sslidentity=`: Path to the PKCS12 certificate database created from client cert and key. This is the SSL identity file in PKCS12 format which you will generate using the client key and client certificate. It combines these two files in a single file and secures them via a password (see next parameter). You can create this file using your client key and client certificate by using the following command (using `openssl`): + ``` + openssl pkcs12 -export -out client-identity.p12 -inkey client-key.pem -in client-cert.pem + ``` +- `sslpassword=`: Password that was used to secure the PKCS12 file. The `openssl` command listed in the previous step will ask for a password while creating the PKCS12 file, you will need to provide that same exact password here. +- `sslaccept=(strict|accept_invalid_certs)`: + - `strict`: Any missing value in the certificate will lead to an error. For Google Cloud, especially if the database doesn't have a domain name, the certificate might miss the domain/IP address, causing an error when connecting. + - `accept_invalid_certs` (default): Bypass this check. Be aware of the security consequences of this setting. + +Your database connection URL will look similar to this: + +``` +mysql://USER:PASSWORD@HOST:PORT/DATABASE?sslidentity=client-identity.p12&sslpassword=mypassword&sslcert=rootca.cert +``` + +### Connecting via sockets + +To connect to your MySQL database via sockets, you must add a `socket` field as a _query parameter_ to the connection URL (instead of setting it as the `host` part of the URI). +The value of this parameter then must point to the directory that contains the socket, e.g.: `mysql://USER:POST@localhost/database?socket=/var/run/mysql/` + +Note that `localhost` is required, the value itself is ignored and can be anything. + +> **Note**: You can find additional context in this [GitHub issue](https://github.com/prisma/prisma-client-js/issues/437#issuecomment-592436707). + +## Type mapping between MySQL to Prisma schema + +The MySQL connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the Prisma [data model](/orm/prisma-schema/data-model/models) as follows to native column types: + +> Alternatively, see [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for type mappings organized by Prisma type. + +### Native type mapping from Prisma to MySQL + +| Prisma | MySQL | Notes | +| ---------- | ---------------- | ------------------------------------------------ | +| `String` | `VARCHAR(191)` | | +| `Boolean` | `BOOLEAN` | In MySQL `BOOLEAN` is a synonym for `TINYINT(1)` | +| `Int` | `INT` | | +| `BigInt` | `BIGINT` | +| `Float` | `DOUBLE` | | +| `Decimal` | `DECIMAL(65,30)` | +| `DateTime` | `DATETIME(3)` | | +| `Json` | `JSON` | Supported in MySQL 5.7+ only | +| `Bytes` | `LONGBLOB` | + +### Native type mappings + +When introspecting a MySQL database, the database types are mapped to Prisma according to the following table: + +| MySQL | Prisma | Supported | Native database type attribute | Notes | +| ------------------------- | ------------- | --------- | ---------------------------------------------- | ------------------------------------------------------------------ | +| `serial` | `BigInt` | ✔️ | `@db.UnsignedBigInt @default(autoincrement())` | +| `bigint` | `BigInt` | ✔️ | `@db.BigInt` | +| `bigint unsigned` | `BigInt` | ✔️ | `@db.UnsignedBigInt` | +| `bit` | `Bytes` | ✔️ | `@db.Bit(x)` | `bit(1)` maps to `Boolean` - all other `bit(x)` map to `Bytes` | +| `boolean` \| `tinyint(1)` | `Boolean` | ✔️ | `@db.TinyInt(1)` | +| `varbinary` | `Bytes` | ✔️ | `@db.VarBinary` | +| `longblob` | `Bytes` | ✔️ | `@db.LongBlob` | +| `tinyblob` | `Bytes` | ✔️ | `@db.TinyBlob` | +| `mediumblob` | `Bytes` | ✔️ | `@db.MediumBlob` | +| `blob` | `Bytes` | ✔️ | `@db.Blob` | +| `binary` | `Bytes` | ✔️ | `@db.Binary` | +| `date` | `DateTime` | ✔️ | `@db.Date` | +| `datetime` | `DateTime` | ✔️ | `@db.DateTime` | +| `timestamp` | `DateTime` | ✔️ | `@db.TimeStamp` | +| `time` | `DateTime` | ✔️ | `@db.Time` | +| `decimal(a,b)` | `Decimal` | ✔️ | `@db.Decimal(x,y)` | +| `numeric(a,b)` | `Decimal` | ✔️ | `@db.Decimal(x,y)` | +| `enum` | `Enum` | ✔️ | N/A | +| `float` | `Float` | ✔️ | `@db.Float` | +| `double` | `Float` | ✔️ | `@db.Double` | +| `smallint` | `Int` | ✔️ | `@db.SmallInt` | +| `smallint unsigned` | `Int` | ✔️ | `@db.UnsignedSmallInt` | +| `mediumint` | `Int` | ✔️ | `@db.MediumInt` | +| `mediumint unsigned` | `Int` | ✔️ | `@db.UnsignedMediumInt` | +| `int` | `Int` | ✔️ | `@db.Int` | +| `int unsigned` | `Int` | ✔️ | `@db.UnsignedInt` | +| `tinyint` | `Int` | ✔️ | `@db.TinyInt(x)` | `tinyint(1)` maps to `Boolean` all other `tinyint(x)` map to `Int` | +| `tinyint unsigned` | `Int` | ✔️ | `@db.UnsignedTinyInt(x)` | `tinyint(1) unsigned` **does not** map to `Boolean` | +| `year` | `Int` | ✔️ | `@db.Year` | +| `json` | `Json` | ✔️ | `@db.Json` | Supported in MySQL 5.7+ only | +| `char` | `String` | ✔️ | `@db.Char(x)` | +| `varchar` | `String` | ✔️ | `@db.VarChar(x)` | +| `tinytext` | `String` | ✔️ | `@db.TinyText` | +| `text` | `String` | ✔️ | `@db.Text` | +| `mediumtext` | `String` | ✔️ | `@db.MediumText` | +| `longtext` | `String` | ✔️ | `@db.LongText` | +| `set` | `Unsupported` | Not yet | | +| `geometry` | `Unsupported` | Not yet | | +| `point` | `Unsupported` | Not yet | | +| `linestring` | `Unsupported` | Not yet | | +| `polygon` | `Unsupported` | Not yet | | +| `multipoint` | `Unsupported` | Not yet | | +| `multilinestring` | `Unsupported` | Not yet | | +| `multipolygon` | `Unsupported` | Not yet | | +| `geometrycollection` | `Unsupported` | Not yet | | + +[Introspection](/orm/prisma-schema/introspection) adds native database types that are **not yet supported** as [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) fields: + +```prisma file=schema.prisma +model Device { + id Int @id @default(autoincrement()) + name String + data Unsupported("circle") +} +``` + +## Engine + +If you are using a version of MySQL where MyISAM is the default engine, you must specify `ENGINE = InnoDB;` when you create a table. If you introspect a database that uses a different engine, relations in the Prisma Schema are not created (or lost, if the relation already existed). diff --git a/docs/200-orm/050-overview/500-databases/500-sqlite.mdx b/docs/200-orm/050-overview/500-databases/500-sqlite.mdx new file mode 100644 index 0000000000..fc31ca5d47 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/500-sqlite.mdx @@ -0,0 +1,93 @@ +--- +title: 'SQLite' +metaTitle: 'SQLite database connector' +metaDescription: 'This page explains how Prisma can connect to a SQLite database using the SQLite database connector.' +tocDepth: 3 +--- + + + +The SQLite data source connector connects Prisma to a [SQLite](https://www.sqlite.org/) database file. These files always have the file ending `.db` (e.g.: `dev.db`). + +By default, the SQLite connector contains a database driver responsible for connecting to your database. You can use a [driver adapter](/orm/overview/databases/database-drivers#driver-adapters) (Preview) to connect to your database using a JavaScript database driver from Prisma Client. + + + +## Example + +To connect to a SQLite database file, you need to configure a [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `sqlite` data source connector. +- `url`: Specifies the [connection URL](/orm/reference/connection-urls) for the SQLite database. The connection URL always starts with the prefix `file:` and then contains a file path pointing to the SQLite database file. In this case, the file is located in the same directory and called `dev.db`. + +## Type mapping between SQLite to Prisma schema + +The SQLite connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the [data model](/orm/prisma-schema/data-model/models) to native column types as follows: + +> Alternatively, see [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for type mappings organized by Prisma type. + +### Native type mapping from Prisma to SQLite + +| Prisma | SQLite | +| ---------- | ------------- | +| `String` | `TEXT` | +| `Boolean` | `BOOLEAN` | +| `Int` | `INTEGER` | +| `BigInt` | `INTEGER` | +| `Float` | `REAL` | +| `Decimal` | `DECIMAL` | +| `DateTime` | `NUMERIC` | +| `Json` | Not supported | +| `Bytes` | `BLOB` | + +## Rounding errors on big numbers + +SQLite is a loosely-typed database. If your Schema has a field of type `Int`, then Prisma prevents you from inserting a value larger than an integer. However, nothing prevents the database from directly accepting a bigger number. These manually-inserted big numbers cause rounding errors when queried. + +To avoid this problem, Prisma 4.0.0 and later checks numbers on the way out of the database to verify that they fit within the boundaries of an integer. If a number does not fit, then Prisma throws a P2023 error, such as: + +``` +Inconsistent column data: Conversion failed: +Value 9223372036854775807 does not fit in an INT column, +try migrating the 'int' column type to BIGINT +``` + +## Connection details + +### Connection URL + +The connection URL of a SQLite connector points to a file on your file system. For example, the following two paths are equivalent because the `.db` is in the same directory: + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} +``` + +is the same as: + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = "file:dev.db" +} +``` + +You can also target files from the root or any other place in your file system: + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = "file:/Users/janedoe/dev.db" +} +``` diff --git a/docs/200-orm/050-overview/500-databases/600-mongodb.mdx b/docs/200-orm/050-overview/500-databases/600-mongodb.mdx new file mode 100644 index 0000000000..e350510857 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/600-mongodb.mdx @@ -0,0 +1,573 @@ +--- +title: 'MongoDB' +metaTitle: 'MongoDB database connector' +metaDescription: 'How Prisma can connect to a MongoDB database using the MongoDB database connector.' +hidePage: false +tocDepth: 3 +codeStyle: false +--- + + + +This guide discusses the concepts behind using Prisma and MongoDB, explains the commonalities and differences between MongoDB and other database providers, and leads you through the process for configuring your application to integrate with MongoDB using Prisma. + + + +To connect Prisma with MongoDB, refer to our [Getting Started documentation](/getting-started/setup-prisma/start-from-scratch/mongodb-typescript-mongodb). + + + + + +## What is MongoDB? + +[MongoDB](https://www.mongodb.com/) is a NoSQL database that stores data in [BSON](https://bsonspec.org/) format, a JSON-like document format designed for storing data in key-value pairs. It is commonly used in JavaScript application development because the document model maps easily to objects in application code, and there is built in support for high availability and horizontal scaling. + +MongoDB stores data in collections that do not need a schema to be defined in advance, as you would need to do with tables in a relational database. The structure of each collection can also be changed over time. This flexibility can allow rapid iteration of your data model, but it does mean that there are a number of differences when using Prisma to work with your MongoDB database. + +## Commonalities with other database providers + +Some aspects of using Prisma with MongoDB are the same as when using Prisma with a relational database. You can still: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- connect to your database, using the [`mongodb` database connector](/orm/overview/databases) +- use [Introspection](/orm/prisma-schema/introspection) for existing projects if you already have a MongoDB database +- use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) to push changes in your schema to the database +- use [Prisma Client](/orm/prisma-client) in your application to query your database in a type safe way based on your Prisma Schema + +## Differences to consider + +MongoDB's document-based structure and flexible schemas means that using Prisma with MongoDB differs from using it with a relational database in a number of ways. These are some areas where there are differences that you need to be aware of: + +- **Defining IDs**: MongoDB documents have an `_id` field (that often contains an [ObjectID](https://www.mongodb.com/docs/manual/reference/bson-types/#std-label-objectid)). Prisma does not support fields starting with `_`, so this needs to be mapped to a Prisma field using the `@map` attribute. For more information, see [Defining IDs in MongoDB](/orm/prisma-schema/data-model/models#defining-ids-in-mongodb). + +- **Migrating existing data to match your Prisma schema**: In relational databases, all your data must match your schema. If you change the type of a particular field in your schema when you migrate, all the data must also be updated to match. In contrast, MongoDB does not enforce any particular schema, so you need to take care when migrating. For more information, see [How to migrate old data to new schemas](#how-to-migrate-existing-data-to-match-your-prisma-schema). + +- **Introspection and Prisma relations**: When you introspect an existing MongoDB database, you will get a schema with no relations and will need to add the missing relations in manually. For more information, see [How to add in missing relations after Introspection](#how-to-add-in-missing-relations-after-introspection). + +- **Filtering for `null` and missing fields**: MongoDB makes a distinction between setting a field to `null` and not setting it at all, which is not present in relational databases. Prisma currently does not express this distinction, which means that you need to be careful when filtering for `null` and missing fields. For more information, see [How to filter for `null` and missing fields](#how-to-filter-for-null-and-missing-fields) + +- **Enabling replication**: Prisma uses [MongoDB transactions](https://www.mongodb.com/docs/manual/core/transactions/) internally to avoid partial writes on nested queries. When using transactions, MongoDB requires replication of your data set to be enabled. To do this, you will need to configure a [replica set](https://www.mongodb.com/docs/manual/replication/) — this is a group of MongoDB processes that maintain the same data set. Note that it is still possible to use a single database, by creating a replica set with only one node in it. If you use MongoDB's [Atlas](https://www.mongodb.com/atlas/database) hosting service, the replica set is configured for you, but if you are running MongoDB locally you will need to set up a replica set yourself. For more information, see MongoDB's [guide to deploying a replica set](https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/). + +## How to use Prisma with MongoDB + +This section provides instructions for how to carry out tasks that require steps specific to MongoDB. + +### How to migrate existing data to match your Prisma schema + +Migrating your database over time is an important part of the development cycle. During development, you will need to update your Prisma schema file (for example, to add new fields), then update the data in your development environment’s database, and eventually push both the updated schema and the new data to the production database. + + + +When using MongoDB, be aware that the “coupling” between your schema and the database is purposefully designed to be less rigid than with with SQL databases; MongoDB will not enforce the schema, so you have to verify data integrity. + + + +These iterative tasks of updating the schema and the database can result in inconsistencies between your schema and the actual data in the database. Let’s look at one scenario where this can happen, and then examine several strategies for you and your team to consider for handling these inconsistencies. + +**Scenario**: you need to include a phone number for users, as well as an email. You currently have the following `User` model in your `schema.prisma` file: + +```prisma file=prisma/schema.prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String +} +``` + +There are a number of strategies you could use for migrating this schema: + +- **"On-demand" updates**: with this strategy, you and your team have agreed that updates can be made to the schema as needed. However, in order to avoid migration failures due to inconsistencies between the data and schema, there is agreement in the team that any new fields added are explicitly defined as optional. + + In our scenario above, you can add an optional `phoneNumber` field to the `User` model in your Prisma schema: + + ```prisma file=prisma/schema.prisma highlight=4;add + model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + phoneNumber String? + } + ``` + + Then regenerate your Prisma Client using the `npx prisma generate` command. Next, update your application to reflect the new field, and redeploy your app. + + As the `phoneNumber` field is optional, you can still query the old users where the phone number has not been defined. The records in the database will be updated "on demand" as the application's users begin to enter their phone number in the new field. + + Another option is to add a default value on a required field, for example: + + ```prisma file=prisma/schema.prisma highlight=4;add + model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + phoneNumber String @default("000-000-0000") + } + ``` + + Then when you encounter a missing `phoneNumber`, the value will be coerced into `000-000-0000`. + +- **"No breaking changes" updates**: this strategy builds on the first one, with further consensus amongst your team that you don't rename or delete fields, only add new fields, and always define the new fields as optional. This policy can be reenforced by adding checks in the CI/CD process to verify that there are no backwards-incompatible changes to the schema. + +- **"All-at-once" updates**: this strategy is similar to traditional migrations in relational databases, where all data is updated to reflect the new schema. In the scenario above, you would create a script to add a value for the phone number field to all existing users in your database. You can then make the field a required field in the application because the schema and the data are consistent. + +### How to add in missing relations after Introspection + +After introspecting an existing MongoDB database, you will need to manually add in relations between models. MongoDB does not have the concept of defining relations via foreign keys, as you would in a relational database. However, if you have a collection in MongoDB with a "foreign-key-like" field that matches the ID field of another collection, Prisma will allow you to emulate relations between the collections. + +As an example, take a MongoDB database with two collections, `User` and `Post`. The data in these collections has the following format, with a `userId` field linking users to posts: + +`User` collection: + +- `_id` field with a type of `objectId` +- `email` field with a type of `string` + +`Post` collection: + +- `_id` field with a type of `objectId` +- `title` field with a type of `string` +- `userId` with a type of `objectID` + +On introspection with `db pull`, this is pulled in to the Prisma schema file as follows: + +```prisma file=prisma/schema.prisma +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + userId String @db.ObjectId +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String +} +``` + +This is missing the relation between the `User` and `Post` models. To fix this, manually add a `user` field to the `Post` model with a `@relation` attribute using `userId` as the `fields` value, linking it to the `User` model, and a `posts` field to the `User` model as the back relation: + +```prisma file=prisma/schema.prisma highlight=5;add|11;add +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + userId String @db.ObjectId + user User @relation(fields: [userId], references: [id]) +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + posts Post[] +} +``` + +For more information on how to use relations in Prisma, see [our documentation](/orm/prisma-schema/data-model/relations). + +### How to filter for `null` and missing fields + +To understand how MongoDB distinguishes between `null` and missing fields, consider the example of a `User` model with an optional `name` field: + +```ts +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + name String? +} +``` + +First, try creating a record with the `name` field explicitly set to `null`. Prisma will return `name: null` as expected: + + + + + +```ts +const createNull = await prisma.user.create({ + data: { + email: 'user1@prisma.io', + name: null, + }, +}) +console.log(createNull) +``` + + + + + +```code no-copy +{ + id: '6242c4ae032bc76da250b207', + email: 'user1@prisma.io', + name: null +} +``` + + + + + +If you check your MongoDB database directly, you will also see a new record with `name` set to `null`: + +```json +{ + "_id": "6242c4af032bc76da250b207", + "email": "user1@prisma.io", + "name": null +} +``` + +Next, try creating a record without explicitly setting the `name` field: + + + + + +```ts +const createMissing = await prisma.user.create({ + data: { + email: 'user2@prisma.io', + }, +}) +console.log(createMissing) +``` + + + + + +```code no-copy +{ + id: '6242c4ae032bc76da250b208', + email: 'user2@prisma.io', + name: null +} +``` + + + + + +Prisma still returns `name: null`, but if you look in the database directly you will see that the record has no `name` field defined at all: + +```json +{ + "_id": "6242c4af032bc76da250b208", + "email": "user2@prisma.io" +} +``` + +Prisma returns the same result in both cases, because we currently don't have a way to specify this difference in MongoDB between fields that are `null` in the underlying database, and fields that are not defined at all — see [this Github issue](https://github.com/prisma/prisma/issues/12555) for more information. + +This means that you currently have to be careful when filtering for `null` and missing fields. Filtering for records with `name: null` will only return the first record, with the `name` explicitly set to `null`: + + + + + +```ts +const findNulls = await prisma.user.findMany({ + where: { + name: null, + }, +}) +console.log(findNulls) +``` + + + + + +```terminal no-copy +[ + { + id: '6242c4ae032bc76da250b207', + email: 'user1@prisma.io', + name: null + } +] +``` + + + + + +This is because `name: null` is checking for equality, and a non-existing field isn't equal to `null`. + +To include missing fields as well, use the [`isSet` filter](/orm/reference/prisma-client-reference#isset) to explicitly search for fields which are either `null` or not set. This will return both records: + + + + + +```ts +const findNullOrMissing = await prisma.user.findMany({ + where: { + OR: [ + { + name: null, + }, + { + name: { + isSet: false, + }, + }, + ], + }, +}) +console.log(findNullOrMissing) +``` + + + + + +```terminal no-copy +[ + { + id: '6242c4ae032bc76da250b207', + email: 'user1@prisma.io', + name: null + }, + { + id: '6242c4ae032bc76da250b208', + email: 'user2@prisma.io', + name: null + } +] +``` + + + + + +## More on using MongoDB with Prisma + +The fastest way to start using MongoDB with Prisma is to refer to our Getting Started documentation: + +- [Start from scratch](/getting-started/setup-prisma/start-from-scratch/mongodb-typescript-mongodb) +- [Add to existing project](/getting-started/setup-prisma/add-to-existing-project/mongodb-typescript-mongodb) + +These tutorials will take you through the process of connecting to MongoDB, pushing schema changes, and using Prisma Client. + +Further reference information is available in the [MongoDB connector documentation](/orm/overview/databases/mongodb). + +For more information on how to set up and manage a MongoDB database, see the [Prisma Data Guide](https://www.prisma.io/dataguide#mongodb). + +## Example + +To connect to a MongoDB server, configure the [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [Prisma schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `mongodb` data source connector. +- `url`: Specifies the [connection URL](#connection-url) for the MongoDB server. In this case, an [environment variable is used](/orm/more/development-environment/environment-variables) to provide the connection URL. + + + +The MongoDB database connector uses transactions to support nested writes. Transactions **require** a [replica set](https://docs.mongodb.com/manual/tutorial/deploy-replica-set/) deployment. The easiest way to deploy a replica set is with [Atlas](https://docs.atlas.mongodb.com/getting-started/). It's free to get started. + + + +## Connection details + +### Connection URL + +The MongoDB connection URL can be configured in different ways depending on how you are hosting your database. The standard configuration is made up of the following components: + +![Structure of the MongoDB connection URL](./mongodb.png) + +#### Base URL and path + +The base URL and path sections of the connection URL are made up of your authentication credentials followed by the host (and optionally, a port number) and database. + +``` +mongodb://USERNAME:PASSWORD@HOST/DATABASE +``` + +The following components make up the _base URL_ of your database: + +| Name | Placeholder | Description | +| :------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| User | `USERNAME` | Name of your database user, e.g. `janedoe` | +| Password | `PASSWORD` | Password for your database user | +| Host | `HOST` | The host where a [`mongod`](https://docs.mongodb.com/manual/reference/program/mongod/#mongodb-binary-bin.mongod) instance is running. If you are running a sharded cluster this will a [`mongos`](https://docs.mongodb.com/manual/reference/program/mongos/#mongodb-binary-bin.mongos) instance. This can be a hostname, IP address or UNIX domain socket. | +| Port | `PORT` | Port on which your database server is running, e.g. `1234`. If none is provided the default `27017` is used. | +| Database | `DATABASE` | Name of the database to use. If none is specified but the `authSource` option is set then the `authSource` database name is used. If neither the database in the connection string nor the `authSource` option is specified then it defaults to `admin` | + + + +You must [percentage-encode special characters](/orm/reference/connection-urls#special-characters). + + + +#### Arguments + +A connection URL can also take arguments. The following example sets three arguments: + +- An `ssl` connection +- A `connectTimeoutMS` +- And the `maxPoolSize` + +``` +mongodb://USERNAME:PASSWORD@HOST/DATABASE?ssl=true&connectTimeoutMS=5000&maxPoolSize=50 +``` + +Refer to the [MongoDB connection string documentation](https://docs.mongodb.com/manual/reference/connection-string/#connection-string-options) for a complete list of connection string arguments. There are no Prisma-specific arguments. + +## Using `ObjectId` + +It is common practice for the `_id` field of a MongoDB document to contain an [ObjectId](https://docs.mongodb.com/manual/reference/bson-types/#std-label-objectid): + +```json +{ + "_id": { "$oid": "60d599cb001ef98000f2cad2" }, + "createdAt": { "$date": { "$numberLong": "1624611275577" } }, + "email": "ella@prisma.io", + "name": "Ella", + "role": "ADMIN" +} +``` + +Any field (most commonly IDs and relation scalar fields) that maps to an `ObjectId` in the underlying database: + +- Must be of type `String` or `Bytes` +- Must include the `@db.ObjectId` attribute +- Can optionally use `@default(auto())` to auto-generate a valid `ObjectId` on document creation + +Here is an example that uses `String`: + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + // Other fields +} +``` + +And here is another example that uses `Bytes`: + +```prisma +model User { + id Bytes @id @default(auto()) @map("_id") @db.ObjectId + // Other fields +} +``` + +See also: [Defining ID fields in MongoDB](/orm/prisma-schema/data-model/models#defining-ids-in-mongodb) + +### Generating `ObjectId` + +To generate a valid `ObjectId` (for testing purposes or to manually set an ID field value) in your application, use the [`bson`](https://www.npmjs.com/package/bson) package. + +``` +npm install --save bson +``` + +```ts +import { ObjectId } from 'bson' + +const id = new ObjectId() +``` + +## Differences to connectors for relational databases + +This section covers ways in which the MongoDB connector differs from Prisma connectors for relational databases. + +### No support for Prisma Migrate + +Currently, there are no plans to add support for [Prisma Migrate](/orm/prisma-migrate) as MongoDB projects do not rely on internal schemas where changes need to be managed with an extra tool. Management of `@unique` indexes is realized through `db push`. + +### No support for `@@id` and `autoincrement()` + +The [`@@id`](/orm/reference/prisma-schema-reference#id-1) attribute (an ID for multiple fields) is not supported because primary keys in MongoDB are always on the `_id` field of a model. + +The [`autoincrement()`](/orm/reference/prisma-schema-reference#generate-autoincrementing-integers-as-ids) function (which creates incrementing `@id` values) is not supported because `autoincrement()` does not work with the `ObjectID` type that the `_id` field has in MongoDB. + +### Cyclic references and referential actions + +If you have cyclic references in your models, either from self-relations or a cycle of relations between models, and you use [referential actions](/orm/prisma-schema/data-model/relations/referential-actions), you must set a referential action of `NoAction` to prevent an infinite loop of actions. + +See [Special rules for referential actions](/orm/prisma-schema/data-model/relations/referential-actions/special-rules-for-referential-actions) for more details. + +### Replica set configuration + +MongoDB only allows you to start a transaction on a replica set. Prisma uses transactions internally to avoid partial writes on nested queries. This means we inherit the requirement of needing a replica set configured. + +When you try to use Prisma's MongoDB connector on a deployment that has no replica set configured, Prisma shows the message `Error: Transactions are not supported by this deployment`. The full text of the error message is the following: + +``` +PrismaClientUnknownRequestError2 [PrismaClientUnknownRequestError]: +Invalid `prisma.post.create()` invocation in +/index.ts:9:21 + + 6 await prisma.$connect() + 7 + 8 // Create the first post +→ 9 await prisma.post.create( + Error in connector: Database error. error code: unknown, error message: Transactions are not supported by this deployment + at cb (/node_modules/@prisma/client/runtime/index.js:34804:17) + at processTicksAndRejections (internal/process/task_queues.js:97:5) { + clientVersion: '3.xx.0' +} +``` + +To resolve this, we suggest you change your deployment to one with a replica set configured. + +One simple way for this is to use [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) to launch a free instance that has replica set support out of the box. + +There's also an option to run the replica set locally with this guide: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set + +## Type mapping between MongoDB and the Prisma schema + +The MongoDB connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the Prisma [data model](/orm/prisma-schema/data-model/models) to MongoDB's native column types as follows: + +> Alternatively, see [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for type mappings organized by Prisma type. + +### Native type mapping from Prisma to MongoDB + +| Prisma | MongoDB | +| ---------- | ---------------------------------------------------------------------- | +| `String` | `string` | +| `Boolean` | `bool` | +| `Int` | `int` | +| `BigInt` | `long` | +| `Float` | `double` | +| `Decimal` | [Currently unsupported](https://github.com/prisma/prisma/issues/12637) | +| `DateTime` | `timestamp` | +| `Bytes` | `binData` | +| `Json` | | + +MongoDB types that are currently unsupported: + +- `Decimal128` +- `Undefined` +- `DBPointer` +- `Null` +- `Symbol` +- `MinKey` +- `MaxKey` +- `Object` +- `Javascript` +- `JavascriptWithScope` +- `Regex` + +### Mapping from MongoDB to Prisma types on Introspection + +When introspecting a MongoDB database, Prisma uses the relevant [scalar types](/orm/prisma-schema/data-model/models#scalar-fields). Some special types also get additional native type annotations: + +| MongoDB (Type \| Aliases) | Prisma | Supported | Native database type attribute | Notes | +| ------------------------- | -------- | :-------: | :----------------------------- | :---- | +| `objectId` | `String` | ✔️ | `@db.ObjectId` | | + +[Introspection](/orm/prisma-schema/introspection) adds native database types that are **not yet supported** as [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) fields: + +```prisma file=schema.prisma +model Example { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + regex Unsupported("RegularExpression") +} +``` diff --git a/docs/200-orm/050-overview/500-databases/800-sql-server/020-sql-server-local.mdx b/docs/200-orm/050-overview/500-databases/800-sql-server/020-sql-server-local.mdx new file mode 100644 index 0000000000..f52fd1c524 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/800-sql-server/020-sql-server-local.mdx @@ -0,0 +1,53 @@ +--- +title: 'SQL Server on Windows (local)' +metaTitle: 'SQL Server on Windows' +metaDescription: 'Set up and configure SQL Server on Windows.' +--- + + + +To run a Microsoft SQL Server locally on a Windows machine: + +1. If you do not have access to an instance of Microsoft SQL Server, download and set up [SQL Server 2019 Developer](https://www.microsoft.com/en-us/sql-server/sql-server-downloads). + +1. Download and install [SQL Server Management Studio](https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15). + +1. Use Windows Authentication to log in to Microsoft SQL Server Management Studio (expand the **Server Name** dropdown and click **<Browse for more...>** to find your database engine): + +![The New Query button in SQL Server Management Studio](/img/connect-sql-server.png) + + + +## Enable TCP/IP + +Prisma Client requires TCP/IP to be enabled. To enable TCP/IP: + +1. Open SQL Server Configuration Manager. (Search for "SQL Server Configuration Manager" in the Start Menu, or open the Start Menu and type "SQL Server Configuration Manager".) + +1. In the left-hand panel, click **SQL Server Network Configuration** > **Protocols for MSSQLSERVER** + +1. Right-click **TCP/IP** and choose **Enable**. + +## Enable authentication with SQL logins (Optional) + +If you want to use a username and password in your connection URL rather than integrated security, [enable mixed authentication mode](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/change-server-authentication-mode?view=sql-server-ver15) as follows: + +1. Right-click on your database engine in the Object Explorer and click **Properties**. + +1. In the Server Properties window, click **Security** in the left-hand list and tick the **SQL Server and Windows Authentication Mode** option, then click **OK**. + +1. Right-click on your database engine in the Object Explorer and click **Restart**. + +### Enable the `sa` login + +To enable the default `sa` (administrator) SQL Server login: + +1. In SQL Server Management Studio, in the Object Explorer, expand **Security** > **Logins** and double-click **sa**. + +1. On the **General** page, choose a password for the `sa` account (untick **Enforce password policy** if you do not want to enforce a policy). + +1. On the **Status** page, under **Settings** > **Login**, tick **Enabled**, then click **OK**. + +You can now use the `sa` account in a connection URL and when you log in to SQL Server Management Studio. + +> **Note**: The `sa` user has extensive permissions. You can also [create your own login with fewer permissions](https://docs.microsoft.com/en-us/sql/relational-databases/security/authentication-access/create-a-login?view=sql-server-ver15). diff --git a/docs/200-orm/050-overview/500-databases/800-sql-server/030-sql-server-docker.mdx b/docs/200-orm/050-overview/500-databases/800-sql-server/030-sql-server-docker.mdx new file mode 100644 index 0000000000..a4179f25f2 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/800-sql-server/030-sql-server-docker.mdx @@ -0,0 +1,49 @@ +--- +title: 'SQL Server on Docker' +metaTitle: 'SQL Server on Docker' +metaDescription: 'Download and use the Microsoft SQL Server Docker image.' +--- + + + +To run a Microsoft SQL Server container image with Docker: + +1. Install and set up [Docker](https://docs.docker.com/get-docker/) +1. Run the following command in your terminal to download the Microsoft SQL Server 2019 image: + + ```terminal + docker pull mcr.microsoft.com/mssql/server:2019-latest + ``` + +1. Create an instance of the container image, replacing the value of `SA_PASSWORD` with a password of your choice: + + ```terminal wrap + docker run --name sql_container -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=myPassword' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest + ``` + +1. [Follow Microsoft's instructions to connect to SQL Server and use the `sqlcmd` tool](https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-ver15&pivots=cs1-cmd#connect-to-sql-server), replacing the image name and password with your own. + +1. From the `sqlcmd` command prompt, create a new database: + + ```terminal + CREATE DATABASE quickstart + GO + ``` + +1. Run the following command to check that your database was created successfully: + + ```terminal + sp_databases + GO + ``` + + + +## Connection URL credentials + +Based on this example, your credentials are: + +- **Username**: sa +- **Password**: myPassword +- **Database**: quickstart +- **Port**: 1433 diff --git a/docs/200-orm/050-overview/500-databases/800-sql-server/index.mdx b/docs/200-orm/050-overview/500-databases/800-sql-server/index.mdx new file mode 100644 index 0000000000..79dcac9272 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/800-sql-server/index.mdx @@ -0,0 +1,185 @@ +--- +title: 'Microsoft SQL Server' +metaTitle: 'Microsoft SQL Server' +metaDescription: 'This page explains how Prisma can connect to a Microsoft SQL Server database using the Microsoft SQL Server database connector.' +tocDepth: 4 +--- + + + +The Microsoft SQL Server data source connector connects Prisma to a [Microsoft SQL Server](https://docs.microsoft.com/en-us/sql/sql-server/?view=sql-server-ver15) database server. + + + +## Example + +To connect to a Microsoft SQL Server database, you need to configure a [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [Prisma schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "sqlserver" + url = env("DATABASE_URL") +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `sqlserver` data source connector. +- `url`: Specifies the [connection URL](#connection-details) for the Microsoft SQL Server database. In this case, an [environment variable is used](/orm/prisma-schema/overview#accessing-environment-variables-from-the-schema) to provide the connection URL. + +## Connection details + +The connection URL used to connect to an Microsoft SQL Server database follows the [JDBC standard](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15). + +The following example uses SQL authentication (username and password) with an enabled TLS encrypted connection: + +``` +sqlserver://HOST:PORT;database=DATABASE;user=USER;password=PASSWORD;encrypt=true +``` + + + +Note: If you are using any of the following characters in your connection string, [you will need to escape them](https://learn.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver16#escaping-values-in-the-connection-url). + +```terminal +:\=;/[]{} # these are characters that will need to be escaped +``` + +To escape these characters, use curly braces `{}` around values that contain special characters. As an example: + +```terminal +sqlserver://HOST:PORT;database=DATABASE;user={MyServer/MyUser};password={ThisIsA:SecurePassword;};encrypt=true +``` + + + +### Using [integrated security](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/authentication-in-sql-server) (Windows only) + +The following example uses the currently logged in Windows user to log in to Microsoft SQL Server: + +``` +sqlserver://localhost:1433;database=sample;integratedSecurity=true;trustServerCertificate=true; +``` + +The following example uses a specific Active Directory user to log in to Microsoft SQL Server: + +``` +sqlserver://localhost:1433;database=sample;integratedSecurity=true;username=prisma;password=aBcD1234;trustServerCertificate=true; +``` + +### Using SQL Browser to connect to a named instance + +The following example connects to a named instance of Microsoft SQL Server (`mycomputer\sql2019`) using integrated security: + +``` +sqlserver://mycomputer\sql2019;database=sample;integratedSecurity=true;trustServerCertificate=true; +``` + +### Arguments + +| Argument name | Required | Default | Comments | +| :------------------------------------------------------------------------------------ | :---------------- | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|
  • `database`
  • `initial catalog`
| No | `master` | The database to connect to. | +|
  • `username`
  • `user`
  • `uid`
  • `userid`
| No - see Comments | | SQL Server login (such as `sa`) _or_ a valid Windows (Active Directory) username if `integratedSecurity` is set to `true` (Windows only). | +|
  • `password`
  • `pwd`
| No - see Comments | | Password for SQL Server login _or_ Windows (Active Directory) username if `integratedSecurity` is set to `true` (Windows only). | +| `encrypt` | No | `true` | Configures whether to use TLS all the time, or only for the login procedure, possible values: `true` (use always), `false` (only for login credentials). | +| `integratedSecurity` | No | | Enables [Windows authentication (integrated security)](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/authentication-in-sql-server), possible values: `true`, `false`, `yes`, `no`. If set to `true` or `yes` and `username` and `password` are present, login is performed through Windows Active Directory. If login details are not given via separate arguments, the current logged in Windows user is used to login to the server. | +| `connectionLimit` | No | `num_cpus * 2 + 1` | Maximum size of the [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool) | +| `connectTimeout` | No | `5` | Maximum number of seconds to wait for a new connection | +| `schema` | No | `dbo` | Added as a prefix to all the queries if schema name is not the default. | +|
  • `loginTimeout`
  • `connectTimeout`
  • `connectionTimeout`
| No | | Number of seconds to wait for login to succeed. | +| `socketTimeout` | No | | Number of seconds to wait for each query to succeed. | +| `isolationLevel` | No | | Sets [transaction isolation level](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-ver15). | +| `poolTimeout` | No | `10` | Maximum number of seconds to wait for a new connection from the pool. If all connections are in use, the database will return a `PoolTimeout` error after waiting for the given time. | +|
  • `ApplicationName`
  • `Application Name`
(case insensitive) | No | | Sets the application name for the connection. Since version 2.28.0. | +| `trustServerCertificate` | No | `false` | Configures whether to trust the server certificate. | +| `trustServerCertificateCA` | No | | A path to a certificate authority file to be used instead of the system certificates to authorize the server certificate. Must be either in `pem`, `crt` or `der` format. Cannot be used together with `trustServerCertificate` parameter. | + +## Type mapping between Microsoft SQL Server to Prisma schema + +For type mappings organized by Prisma type, refer to the [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) documentation. + +## Supported versions + +See [Supported databases](/orm/reference/supported-databases). + +## Limitations and known issues + +### Prisma Migrate caveats + +Prisma Migrate is supported in [2.13.0](https://github.com/prisma/prisma/releases/tag/2.13.0) and later with the following caveats: + +#### Database schema names + +SQL Server does not have an equivalent to the PostgreSQL `SET search_path` command familiar from PostgreSQL. This means that when you create migrations, you must define the same schema name in the connection URL that is used by the production database. For most of the users this is `dbo` (the default value). However, if the production database uses another schema name, all the migration SQL must be either edited by hand to reflect the production _or_ the connection URL must be changed before creating migrations (for example: `schema=name`). + +#### Cyclic references + +Circular references can occur between models when each model references another, creating a closed loop. When using a Microsoft SQL Server database, Prisma will show a validation error if the [referential action](/orm/prisma-schema/data-model/relations/referential-actions) on a relation is set to something other than [`NoAction`](/orm/prisma-schema/data-model/relations/referential-actions#noaction). + +See [Special rules for referential actions in SQL Server](/orm/prisma-schema/data-model/relations/referential-actions/special-rules-for-referential-actions) for more information. + +#### Destructive changes + +Certain migrations will cause more changes than you might expect. For example: + +- Adding or removing `autoincrement()`. This cannot be achieved by modifying the column, but requires recreating the table (including all constraints, indices, and foreign keys) and moving all data between the tables. +- Additionally, it is not possible to delete all the columns from a table (possible with PostgreSQL or MySQL). If a migration needs to recreate all table columns, it will also re-create the table. + +#### Shared default values are not supported + +In some cases, user might want to define default values as shared objects: + +```sql file=default_objects.sql +CREATE DEFAULT catcat AS 'musti'; + +CREATE TABLE cats ( + id INT IDENTITY PRIMARY KEY, + name NVARCHAR(1000) +); + +sp_bindefault 'catcat', 'dbo.cats.name'; +``` + +Using the stored procedure `sp_bindefault`, the default value `catcat` can be used in more than one table. The way Prisma manages default values is per table: + +```sql file=default_per_table.sql +CREATE TABLE cats ( + id INT IDENTITY PRIMARY KEY, + name NVARCHAR(1000) CONSTRAINT DF_cat_name DEFAULT 'musti' +); +``` + +The last example, when introspected, leads to the following model: + +```prisma file=schema.prisma +model cats { + id Int @id @default(autoincrement()) + name String? @default("musti") +} +``` + +And the first doesn't get the default value introspected: + +```prisma file=schema.prisma +model cats { + id Int @id @default(autoincrement()) + name String? +} +``` + +If using Prisma Migrate together with shared default objects, changes to them must be done manually to the SQL. + +### Data model limitations + +#### Cannot use column with `UNIQUE` constraint and filtered index as foreign key + +Microsoft SQL Server [only allows one `NULL` value in a column that has a `UNIQUE` constraint](https://docs.microsoft.com/en-us/sql/relational-databases/tables/unique-constraints-and-check-constraints?view=sql-server-ver15#Unique). For example: + +- A table of users has a column named `license_number` +- The `license_number` field has a `UNIQUE` constraint +- The `license_number` field only allows **one** `NULL` value + +The standard way to get around this issue is to create a filtered unique index that excludes `NULL` values. This allows you to insert multiple `NULL` values. If you do not create an index in the database, you will get an error if you try to insert more than one `null` value into a column with Prisma Client. + +_However_, creating an index makes it impossible to use `license_number` as a foreign key in the database (or a relation scalar field in corresponding Prisma Schema) diff --git a/docs/200-orm/050-overview/500-databases/850-planetscale.mdx b/docs/200-orm/050-overview/500-databases/850-planetscale.mdx new file mode 100644 index 0000000000..a018c3d3df --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/850-planetscale.mdx @@ -0,0 +1,316 @@ +--- +title: 'PlanetScale' +metaTitle: 'PlanetScale' +metaDescription: 'Guide to PlanetScale' +tocDepth: 3 +toc: true +--- + + + +Prisma and [PlanetScale](https://planetscale.com/) together provide a development arena that optimizes rapid, type-safe development of data access applications, using Prisma's ORM and PlanetScale's highly scalable MySQL-based platform. + +This document discusses the concepts behind using Prisma and PlanetScale, explains the commonalities and differences between PlanetScale and other database providers, and leads you through the process for configuring your application to integrate with PlanetScale. + + + +## What is PlanetScale? + +PlanetScale uses the [Vitess](https://vitess.io/) database clustering system to provide a MySQL-compatible database platform. Features include: + +- **Enterprise scalability.** PlanetScale provides a highly available production database cluster that supports scaling across multiple database servers. This is particularly useful in a serverless context, as it avoids the problem of having to [manage connection limits](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas). +- **Database branches.** PlanetScale allows you to create [branches of your database schema](https://planetscale.com/docs/concepts/branching), so that you can test changes on a development branch before applying them to your production database. +- **Support for [non-blocking schema changes](https://planetscale.com/docs/concepts/nonblocking-schema-changes).** PlanetScale provides a workflow that allows users to update database schemas without locking the database or causing downtime. + +## Commonalities with other database providers + +Many aspects of using Prisma with PlanetScale are just like using Prisma with any other relational database. You can still: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- use Prisma's existing [`mysql` database connector](/orm/overview/databases/mysql) in your schema, along with the [connection string PlanetScale provides you](https://planetscale.com/docs/concepts/connection-strings) +- use [Introspection](/orm/prisma-schema/introspection) for existing projects if you already have a database schema in PlanetScale +- use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) to push changes in your schema to the database +- use [Prisma Client](/orm/prisma-client) in your application to talk to the database server at PlanetScale + +## Differences to consider + +PlanetScale's branching model and design for scalability means that there are also a number of differences to consider. You should be aware of the following points when deciding to use PlanetScale with Prisma: + +- **Branching and deploy requests.** PlanetScale provides two types of database branches: _development branches_, which allow you to test out schema changes, and _production branches_, which are protected from direct schema changes. Instead, changes must be first created on a development branch and then deployed to production using a deploy request. Production branches are highly available and include automated daily backups. To learn more, see [How to use branches and deploy requests](#how-to-use-branches-and-deploy-requests). + +- **Referential actions and integrity.** To support scaling across multiple database servers, PlanetScale [does not allow the use of foreign key constraints](https://planetscale.com/docs/learn/operating-without-foreign-key-constraints), which are normally used in relational databases to enforce relationships between data in different tables, and asks users to handle this manually in their applications. + With Prisma you can maintain these relationships in your data and allow the use of [referential actions](/orm/prisma-schema/data-model/relations/referential-actions) by using Prisma's ability to [emulate relations in Prisma Client](/orm/prisma-schema/data-model/relations/relation-mode#emulate-relations-in-prisma-with-the-prisma-relation-mode) with the `prisma` relation mode. For more information, see [How to emulate relations in Prisma Client](#how-to-emulate-relations-in-prisma-client). + +- **Creating indexes on foreign keys.** When emulating relations in Prisma, you will need to create indexes on foreign keys. In a standard MySQL database, if a table has a column with a foreign key constraint, an index is automatically created on that column. Because PlanetScale does not support foreign keys, these indexes are [currently](https://github.com/prisma/prisma/issues/10611) not created when Prisma Client emulates relations, which can lead to issues with queries not being well optimised. To avoid this, you can create indexes in Prisma. For more information, see [How to create indexes on foreign keys](#how-to-create-indexes-on-foreign-keys). + +- **Making schema changes with `db push`.** When you merge a development branch into your production branch, PlanetScale will automatically compare the two schemas and generate its own schema diff. This means that Prisma's [`prisma migrate`](/orm/prisma-migrate) workflow, which generates its own history of migration files, is not a natural fit when working with PlanetScale. These migration files may not reflect the actual schema changes run by PlanetScale when the branch is merged. + + + + Prisma recommends not using `prisma migrate` when making schema changes with PlanetScale. Instead, we recommend that you use the `prisma db push` command. + + + + For an example of how this works, see [How to make schema changes with `db push`](#how-to-make-schema-changes-with-db-push) + +- **Introspection**. When you introspect on an existing database, you will get a schema with no relations, as they are usually defined based on foreign keys that connect tables. Because PlanetScale does not support foreign keys, and you use Prisma to emulate relations, you will need to add the missing relations in manually. For more information, see [How to add in missing relations after Introspection](#how-to-add-in-missing-relations-after-introspection). + +## How to use branches and deploy requests + +When connecting to PlanetScale with Prisma, you will need to use the correct connection string for your branch. The connection URL for a given database branch can be found from your PlanetScale account by going to the overview page for the branch and selecting the 'Connect' dropdown. In the 'Passwords' section, generate a new password and select 'Prisma' from the dropdown to get the Prisma format for the connection URL. See Prisma's [Getting Started guide](/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-planetscale) for more details of how to connect to a PlanetScale database. + +Every PlanetScale database is created with a branch called `main`, which is initially a development branch that you can use to test schema changes on. Once you are happy with the changes you make there, you can [promote it](https://planetscale.com/docs/concepts/branching#promote-a-branch-to-production) to become a production branch. Note that you can only push new changes to a development branch, so further changes will need to be created on a separate development branch and then later deployed to production using a [deploy request](https://planetscale.com/docs/concepts/branching#2.-create-a-deploy-request). + +If you try to push to a production branch, you will get the [error message](/orm/reference/error-reference#p3022) `Direct execution of DDL (Data Definition Language) SQL statements is disabled on this database.` + +## How to emulate relations in Prisma Client + +PlanetScale does not allow foreign keys in its database schema. By default, Prisma uses foreign keys in the underlying database to enforce relations between fields in your Prisma schema. In Prisma versions 3.1.1 and later, you can [emulate relations in Prisma Client with the `prisma` relation mode](/orm/prisma-schema/data-model/relations/relation-mode#emulate-relations-in-prisma-with-the-prisma-relation-mode), which avoids the need for foreign keys in the database. + +To enable emulation of relations in Prisma Client, set the `relationMode` field to `"prisma"` in the `datasource` block: + +```prisma file=schema.prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" +} +``` + + + +The ability to set the relation mode was introduced as part of the `referentialIntegrity` preview feature in Prisma version 3.1.1, and is generally available in Prisma versions 4.8.0 and later.

The `relationMode` field was renamed in Prisma version 4.5.0, and was previously named `referentialIntegrity`. + +
+ +If you use relations in your Prisma schema with the default `"foreignKeys"` option for the `referentialIntegrity` field, PlanetScale will error when Prisma tries to create foreign keys. In versions 2.27.0 and later, Prisma will output the [P3021 error message](/orm/reference/error-reference#p3021). + +## How to create indexes on foreign keys + +When [you emulate relations in Prisma Client](#how-to-emulate-relations-in-prisma-client), you need to create your own indexes. As an example of a situation where you would want to add an index, take this schema for a blog with posts and comments: + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String + likes Int @default(0) + comments Comment[] +} + +model Comment { + id Int @id @default(autoincrement()) + comment String + postId Int + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) +} +``` + +The `postId` field in the `Comment` model refers to the corresponding `id` field in the `Post` model. However this is not implemented as a foreign key in PlanetScale, so the column doesn't have an automatic index. This means that some queries may not be well optimised. For example, if you query for all comments with a certain post `id`, PlanetScale may have to do a full table lookup. This could be slow, and also expensive because PlanetScale's billing model charges for the number of rows read. + +To avoid this, you can define an index on the `postId` field using [Prisma's `@@index` argument](/orm/reference/prisma-schema-reference#index): + +```prisma file=schema.prisma highlight=15;add +model Post { + id Int @id @default(autoincrement()) + title String + content String + likes Int @default(0) + comments Comment[] +} + +model Comment { + id Int @id @default(autoincrement()) + comment String + postId Int + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + + @@index([postId]) +} +``` + +You can then add this change to your schema [using `db push`](#how-to-make-schema-changes-with-db-push). + +In Prisma versions 4.7.0 and later, Prisma warns you if you have a relation with no index on the relation scalar field. For more information, see [Index validation](/orm/prisma-schema/data-model/relations/relation-mode#index-validation). + + + +One issue to be aware of is that [implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) cannot have an index added in this way. If query speed or cost is an issue, you may instead want to use an [explicit many-to-many relation](/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations) in this case. + + + +## How to make schema changes with `db push` + +To use `db push` with PlanetScale, you will first need to [enable emulation of relations in Prisma Client](#how-to-emulate-relations-in-prisma-client). Pushing to your branch without referential emulation enabled will give the [error message](/orm/reference/error-reference#p3021) `Foreign keys cannot be created on this database.` + +As an example, let's say you decide to decide to add a new `excerpt` field to the blog post schema above. You will first need to [create a new development branch and connect to it](#how-to-use-branches-and-deploy-requests). + +Next, add the following to your `schema.prisma` file: + +```prisma file=schema.prisma highlight=5;edit +model Post { + id Int @id @default(autoincrement()) + title String + content String + excerpt String? + likes Int @default(0) + comments Comment[] +} + +model Comment { + id Int @id @default(autoincrement()) + comment String + postId Int + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + + @@index([postId]) +} +``` + +To push these changes, navigate to your project directory in your terminal and run + +```terminal +npx prisma db push +``` + +Once you are happy with your changes on your development branch, you can open a deploy request to deploy these to your production branch. + +For more examples, see PlanetScale's tutorial on [automatic migrations with Prisma](https://planetscale.com/docs/prisma/automatic-prisma-migrations) using `db push`. + +## How to add in missing relations after Introspection + +After introspecting with `npx prisma db pull`, the schema you get may be missing some relations. For example, the following schema is missing a relation between the `User` and `Post` models: + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + title String @db.VarChar(255) + content String? + authorId Int + + @@index([authorId]) +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? +} +``` + +In this case you need to add the relation in manually: + +```prisma file=schema.prisma highlight=6,16;add +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + title String @db.VarChar(255) + content String? + author User @relation(fields: [authorId], references: [id]) + authorId Int + + @@index([authorId]) +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +For a more detailed example, see the [Getting Started guide for PlanetScale](/getting-started/setup-prisma/add-to-existing-project/relational-databases/introspection-typescript-planetscale). + +## How to use the PlanetScale serverless driver with Prisma (Preview) + +The [PlanetScale serverless driver](https://planetscale.com/docs/tutorials/planetscale-serverless-driver) provides a way of communicating with your database and executing queries over HTTP. + +You can use Prisma along with the PlanetScale serverless driver using the [`@prisma/adapter-planetscale`](https://www.npmjs.com/package/@prisma/adapter-planetscale) driver adapter. The driver adapter allows you to communicate with your database over HTTP. + + + +This feature is available in Preview from Prisma versions 5.4.2 and later. + + + +To get started, enable the `driverAdapters` Preview feature flag: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} +``` + +Generate Prisma Client: + +```sh +npx prisma generate +``` + + + +Ensure you update the host value in your connection string to `aws.connect.psdb.cloud`. You can learn more about this [here](https://planetscale.com/docs/tutorials/planetscale-serverless-driver#add-and-use-the-planetscale-serverless-driver-for-javascript-to-your-project). + +```bash +DATABASE_URL='mysql://johndoe:strongpassword@aws.connect.psdb.cloud/clear_nightsky?sslaccept=strict' +``` + + + +Install the Prisma adapter for PlanetScale, PlanetScale serverless driver and `undici` packages: + +```sh +npm install @prisma/adapter-planetscale @planetscale/database undici +``` + + + +When using a Node.js version below 18, you must provide a custom fetch function implementation. We recommend the `undici` package on which Node's built-in fetch is based. Node.js versions 18 and later include a built-in global `fetch` function, so you don't have to install an extra package. + + + +Update your Prisma Client instance to use the PlanetScale serverless driver: + +```ts +import { Client } from '@planetscale/database' +import { PrismaPlanetScale } from '@prisma/adapter-planetscale' +import { PrismaClient } from '@prisma/client' +import dotenv from 'dotenv' +import { fetch as undiciFetch } from 'undici' + +dotenv.config() +const connectionString = `${process.env.DATABASE_URL}` + +const client = new Client({ url: connectionString, fetch: undiciFetch }) +const adapter = new PrismaPlanetScale(client) +const prisma = new PrismaClient({ adapter }) +``` + +You can then use Prisma Client as you normally would with full type-safety. Prisma Migrate, introspection, and Prisma Studio will continue working as before using the connection string defined in the Prisma schema. + +## More on using PlanetScale with Prisma + +The fastest way to start using PlanetScale with Prisma is to refer to our Getting Started documentation: + +- [Start from scratch](/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-planetscale) +- [Add to existing project](/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-planetscale) + +These tutorials will take you through the process of connecting to PlanetScale, pushing schema changes, and using Prisma Client. + +For further tips on best practices when using Prisma and PlanetScale together, watch our video: + +
+ + + +
diff --git a/docs/200-orm/050-overview/500-databases/860-cockroachdb.mdx b/docs/200-orm/050-overview/500-databases/860-cockroachdb.mdx new file mode 100644 index 0000000000..eda18fc474 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/860-cockroachdb.mdx @@ -0,0 +1,224 @@ +--- +title: 'CockroachDB' +metaTitle: 'CockroachDB' +metaDescription: 'Guide to CockroachDB' +tocDepth: 3 +toc: true +--- + + + +This guide discusses the concepts behind using Prisma and CockroachDB, explains the commonalities and differences between CockroachDB and other database providers, and leads you through the process for configuring your application to integrate with CockroachDB. + + + + + +The CockroachDB connector is generally available in versions `3.14.0` and later. It was first added as a [Preview feature](/orm/reference/preview-features) in version [`3.9.0`](https://github.com/prisma/prisma/releases/tag/3.9.0) with support for Introspection, and Prisma Migrate support was added in [`3.11.0`](https://github.com/prisma/prisma/releases/tag/3.11.0). + + + +## What is CockroachDB? + +CockroachDB is a distributed database that is designed for scalability and high availability. Features include: + +- **Built-in scaling:** CockroachDB comes with automated replication, failover and repair capabilities to allow easy horizontal scaling of your application +- **Consistent transactions:** CockroachDB is a relational database that supports consistent transactions that maintain data integrity +- **Compatibility with PostgreSQL:** CockroachDB is compatible with PostgreSQL, allowing interoperability with a large ecosystem of existing products + +## Commonalities with other database providers + +CockroachDB is largely compatible with PostgreSQL, and can mostly be used with Prisma in the same way. You can still: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- connect to your database, using Prisma's [`cockroachdb` database connector](/orm/overview/databases/cockroachdb) +- use [Introspection](/orm/prisma-schema/introspection) for existing projects if you already have a CockroachDB database +- use [Prisma Migrate](/orm/prisma-migrate) to migrate your database schema to a new version +- use [Prisma Client](/orm/prisma-client) in your application to query your database in a type safe way based on your Prisma Schema + +## Differences to consider + +There are some CockroachDB-specific differences to be aware of when working with Prisma's `cockroachdb` connector: + +- **Cockroach-specific native types:** Prisma's `cockroachdb` database connector provides support for CockroachDB's native data types. To learn more, see [How to use CockroachDB's native types](#how-to-use-cockroachdbs-native-types). + +- **Creating database keys:** Prisma allows you to generate a unique identifier for each record using the [`autoincrement()`](/orm/reference/prisma-schema-reference#autoincrement) function. For more information, see [How to use database keys with CockroachDB](#how-to-use-database-keys-with-cockroachdb). + +## How to use Prisma with CockroachDB + +This section provides more details on how to use CockroachDB-specific features. + +### How to use CockroachDB's native types + +CockroachDB has its own set of native [data types](https://www.cockroachlabs.com/docs/stable/data-types.html) which are supported in Prisma. For example, CockroachDB uses the `STRING` data type instead of PostgreSQL's `VARCHAR`. + +As a demonstration of this, say you create a `User` table in your CockroachDB database using the following SQL command: + +```sql +CREATE TABLE public."Post" ( + "id" INT8 NOT NULL, + "title" VARCHAR(200) NOT NULL, + CONSTRAINT "Post_pkey" PRIMARY KEY ("id" ASC), + FAMILY "primary" ("id", "title") +); +``` + +After introspecting your database with `npx prisma db pull`, you will have a new `Post` model in your `schema.prisma` file: + +```prisma file=schema.prisma +model Post { + id BigInt @id + title String @db.String(200) +} +``` + +Notice that the `title` field has been annotated with `@db.String(200)` — this differs from PostgreSQL where the annotation would be `@db.VarChar(200)`. + +For a full list of type mappings, see our [connector documentation](/orm/overview/databases/cockroachdb#type-mapping-between-cockroachdb-and-the-prisma-schema). + +### How to use database keys with CockroachDB + +When generating unique identifiers for records in a distributed database like CockroachDB, it is best to avoid using sequential IDs – for more information on this, see CockroachDB's [blog post on choosing index keys](https://cockroachlabs.com/blog/how-to-choose-db-index-keys). + +Instead, Prisma provides the [`autoincrement()`](/orm/reference/prisma-schema-reference#autoincrement) attribute function, which uses CockroachDB's [`unique_rowid()` function](https://www.cockroachlabs.com/docs/stable/serial.html) for generating unique identifiers. For example, the following `User` model has an `id` primary key, generated using the `autoincrement()` function: + +```prisma file=schema.prisma +model User { + id BigInt @id @default(autoincrement()) + name String +} +``` + +For compatibility with existing databases, you may sometimes still need to generate a fixed sequence of integer key values. In these cases, you can use Prisma's inbuilt [`sequence()`](/orm/reference/prisma-schema-reference#sequence) function for CockroachDB. For a list of available options for the `sequence()` function, see our [reference documentation](/orm/reference/prisma-schema-reference#sequence). + +For more information on generating database keys, see CockroachDB's [Primary key best practices](https://www.cockroachlabs.com/docs/v21.2/schema-design-table#primary-key-best-practices) guide. + +## Example + +To connect to a CockroachDB database server, you need to configure a [`datasource`](/orm/prisma-schema/overview/data-sources) block in your [Prisma schema file](/orm/prisma-schema): + +```prisma file=schema.prisma +datasource db { + provider = "cockroachdb" + url = env("DATABASE_URL") +} +``` + +The fields passed to the `datasource` block are: + +- `provider`: Specifies the `cockroachdb` data source connector. +- `url`: Specifies the [connection URL](#connection-details) for the CockroachDB database server. In this case, an [environment variable is used](/orm/prisma-schema/overview#accessing-environment-variables-from-the-schema) to provide the connection URL. + + + +While `cockroachdb` and `postgresql` connectors are similar, it is mandatory to use the `cockroachdb` connector instead of `postgresql` when connecting to a CockroachDB database from version 5.0.0. + + + +## Connection details + +CockroachDB uses the PostgreSQL format for its connection URL. See the [PostgreSQL connector documentation](/orm/overview/databases/postgresql#connection-details) for details of this format, and the optional arguments it takes. + +## Differences between CockroachDB and PostgreSQL + +The following table lists differences between CockroachDB and PostgreSQL: + +| Issue | Area | Notes | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| By default, the `INT` type is an alias for `INT8` in CockroachDB, whereas in PostgreSQL it is an alias for `INT4`. This means that Prisma will introspect an `INT` column in CockroachDB as `BigInt`, whereas in PostgreSQL Prisma will introspect it as `Int`. | Schema | For more information on the `INT` type, see the [CockroachDB documentation](https://www.cockroachlabs.com/docs/stable/int.html#considerations-for-64-bit-signed-integers) | +| When using `@default(autoincrement())` on a field, CockroachDB will automatically generate 64-bit integers for the row IDs. These integers will be increasing but not consecutive. This is in contrast to PostgreSQL, where generated row IDs are consecutive and start from 1. | Schema | For more information on generated values, see the [CockroachDB documentation](https://www.cockroachlabs.com/docs/stable/serial.html#generated-values-for-modes-rowid-and-virtual_sequence) | +| The `@default(autoincrement())` attribute can only be used together with the `BigInt` field type. | Schema | For more information on generated values, see the [CockroachDB documentation](https://www.cockroachlabs.com/docs/stable/serial.html#generated-values-for-modes-rowid-and-virtual_sequence) | + +## Type mapping limitations in CockroachDB + +The CockroachDB connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the Prisma [data model](/orm/prisma-schema/data-model/models) to native column types. These native types are mostly the same as for PostgreSQL — see the [Native type mapping from Prisma to CockroachDB](#native-type-mapping-from-prisma-to-cockroachdb) for details. However, there are some limitations: + +| CockroachDB (Type \| Aliases) | Prisma | Supported | Native database type attribute | Notes | +| ----------------------------- | --------- | :-------: | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| `money` | `Decimal` | Not yet | `@db.Money` | Supported in PostgreSQL but [not currently in CockroachDB](https://github.com/cockroachdb/cockroach/issues/41578) | +| `xml` | `String` | Not yet | `@db.Xml` | Supported in PostgreSQL but [not currently in CockroachDB](https://github.com/cockroachdb/cockroach/issues/43355) | +| `jsonb` arrays | `Json[]` | Not yet | N/A | `Json[]` supported in PostgreSQL but [not currently in CockroachDB](https://github.com/cockroachdb/cockroach/issues/23468) | + +## Other limitations + +The following table lists any other current known limitations of CockroachDB compared to PostgreSQL: + +| Issue | Area | Notes | +| ------------------------------------------------------------------------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Primary keys are named `primary` instead of `TABLE_pkey`, the Prisma default. | Introspection | This means that they are introspected as `@id(map: "primary")`. This will be [fixed in CockroachDB 22.1](https://github.com/cockroachdb/cockroach/pull/70604). | +| Foreign keys are named `fk_COLUMN_ref_TABLE` instead of `TABLE_COLUMN_fkey`, the Prisma default. | Introspection | This means that they are introspected as `@relation([...], map: "fk_COLUMN_ref_TABLE")`. This will be [fixed in CockroachDB 22.1](https://github.com/cockroachdb/cockroach/pull/70658) | +| Index types `Hash`, `Gist`, `SpGist` or `Brin` are not supported. | Schema | In PostgreSQL, Prisma allows [configuration of indexes](/orm/prisma-schema/data-model/indexes#configuring-the-access-type-of-indexes-with-type-postgresql) to use the different index access method. CockroachDB only currently supports `BTree` and `Gin`. | +| Pushing to `Enum` types not supported | Client | Pushing to `Enum` types (e.g. `data: { enum { push: "A" }, }`) is currently [not supported in CockroachDB](https://github.com/cockroachdb/cockroach/issues/71388) | +| Searching on `String` fields without a full text index not supported | Client | Searching on `String` fields without a full text index (e.g. `where: { text: { search: "cat & dog", }, },`) is currently [not supported in CockroachDB](https://github.com/cockroachdb/cockroach/issues/7821) | +| Integer division not supported | Client | Integer division (e.g. `data: { int: { divide: 10, }, }`) is currently [not supported in CockroachDB](https://github.com/cockroachdb/cockroach/issues/41448) | +| Limited filtering on `Json` fields | Client | Currently CockroachDB [only supports](https://github.com/cockroachdb/cockroach/issues/49144) `equals` and `not` filtering on `Json` fields | + +## Type mapping between CockroachDB and the Prisma schema + +The CockroachDB connector maps the [scalar types](/orm/prisma-schema/data-model/models#scalar-fields) from the Prisma [data model](/orm/prisma-schema/data-model/models) as follows to native column types: + +> Alternatively, see the [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for type mappings organized by Prisma type. + +### Native type mapping from Prisma to CockroachDB + +| Prisma | CockroachDB | +| ---------- | ---------------- | +| `String` | `STRING` | +| `Boolean` | `BOOL` | +| `Int` | `INT4` | +| `BigInt` | `INT8` | +| `Float` | `FLOAT8` | +| `Decimal` | `DECIMAL(65,30)` | +| `DateTime` | `TIMESTAMP(3)` | +| `Json` | `JSONB` | +| `Bytes` | `BYTES` | + +### Mapping from CockroachDB to Prisma types on Introspection + +When introspecting a CockroachDB database, the database types are mapped to Prisma according to the following table: + +| CockroachDB (Type \| Aliases) | Prisma | Supported | Native database type attribute | Notes | +| -------------------------------------------- | ---------- | :-------: | :----------------------------- | :--------------------------------------------------------------------- | +| `INT` \| `BIGINT`, `INTEGER` | `BigInt` | ✔️ | `@db.Int8` | | +| `BOOL` \| `BOOLEAN` | `Bool` | ✔️ | `@db.Bool`\* | | +| `TIMESTAMP` \| `TIMESTAMP WITHOUT TIME ZONE` | `DateTime` | ✔️ | `@db.Timestamp(x)` | | +| `TIMESTAMPTZ` \| `TIMESTAMP WITH TIME ZONE` | `DateTime` | ✔️ | `@db.Timestamptz(x)` | | +| `TIME` \| `TIME WITHOUT TIME ZONE` | `DateTime` | ✔️ | `@db.Time(x)` | | +| `TIMETZ` \| `TIME WITH TIME ZONE` | `DateTime` | ✔️ | `@db.Timetz(x)` | | +| `DECIMAL(p,s)` \| `NUMERIC(p,s)`, `DEC(p,s)` | `Decimal` | ✔️ | `@db.Decimal(x, y)` | | +| `REAL` \| `FLOAT4`, `FLOAT` | `Float` | ✔️ | `@db.Float4` | | +| `DOUBLE PRECISION` \| `FLOAT8` | `Float` | ✔️ | `@db.Float8` | | +| `INT2` \| `SMALLINT` | `Int` | ✔️ | `@db.Int2` | | +| `INT4` | `Int` | ✔️ | `@db.Int4` | | +| `CHAR(n)` \| `CHARACTER(n)` | `String` | ✔️ | `@db.Char(x)` | | +| `"char"` | `String` | ✔️ | `@db.CatalogSingleChar` | Internal type for CockroachDB catalog tables, not meant for end users. | +| `STRING` \| `TEXT`, `VARCHAR` | `String` | ✔️ | `@db.String` | | +| `DATE` | `DateTime` | ✔️ | `@db.Date` | | +| `ENUM` | `enum` | ✔️ | N/A | | +| `INET` | `String` | ✔️ | `@db.Inet` | | +| `BIT(n)` | `String` | ✔️ | `@Bit(x)` | | +| `VARBIT(n)` \| `BIT VARYING(n)` | `String` | ✔️ | `@VarBit` | | +| `OID` | `Int` | ✔️ | `@db.Oid` | | +| `UUID` | `String` | ✔️ | `@db.Uuid` | | +| `JSONB` \| `JSON` | `Json` | ✔️ | `@db.JsonB` | | +| Array types | `[]` | ✔️ | | | + +[Introspection](/orm/prisma-schema/introspection) adds native database types that are **not yet supported** as [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) fields: + +```prisma file=schema.prisma +model Device { + id BigInt @id @default(autoincrement()) + interval Unsupported("INTERVAL") +} +``` + +## More on using CockroachDB with Prisma + +The fastest way to start using CockroachDB with Prisma is to refer to our Getting Started documentation: + +- [Start from scratch](/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-cockroachdb) +- [Add to existing project](/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-cockroachdb) + +These tutorials will take you through the process of connecting to CockroachDB, migrating your schema, and using Prisma Client. + +Further reference information is available in the [CockroachDB connector documentation](/orm/overview/databases/cockroachdb). diff --git a/docs/200-orm/050-overview/500-databases/880-supabase.mdx b/docs/200-orm/050-overview/500-databases/880-supabase.mdx new file mode 100644 index 0000000000..c00f56e344 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/880-supabase.mdx @@ -0,0 +1,72 @@ +--- +title: 'Supabase' +metaTitle: 'Supabase' +metaDescription: 'Guide to Supabase' +tocDepth: 2 +toc: true +--- + + + +This guide discusses the concepts behind using Prisma and Supabase, explains the commonalities and differences between Supabase and other database providers, and leads you through the process for configuring your application to integrate with Supabase. + + + +## What is Supabase? + +[Supabase](https://supabase.com/) is a PostgreSQL hosting service and open source Firebase alternative providing all the backend features you need to build a product. Unlike Firebase, Supabase is backed by PostgreSQL which can be accessed directly using Prisma. + +To learn more about Supabase, you can check out their architecture [here](https://supabase.com/docs/guides/getting-started/architecture) and features [here](https://supabase.com/docs/guides/getting-started/features) + +## Commonalities with other database providers + +Many aspects of using Prisma with Supabase are just like using Prisma with any other relational database. You can still: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- use Prisma's existing [`postgresql` database connector](/orm/overview/databases/postgresql) in your schema, along with the [connection string Supabase provides you](https://supabase.com/docs/guides/database/connecting-to-postgres#finding-your-connection-string) +- use [Introspection](/orm/prisma-schema/introspection) for existing projects if you already have a database schema in Supabase +- use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) to push changes in your schema to Supabase +- use [Prisma Client](/orm/prisma-client) in your application to talk to the database server at Supabase + +## Specific considerations + +If you'd like to use the [connection pooling feature](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler) available with Supabase, you will need to use the connection pooling connection string available via your [Supabase database settings](https://supabase.com/dashboard/project/_/settings/database) with `?pgbouncer=true` appended to the end of your `DATABASE_URL` environment variable: + +```env file=.env +# Connect to Supabase via connection pooling with Supavisor. +DATABASE_URL="postgres://postgres.[your-supabase-project]:[password]@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?pgbouncer=true" +``` + +If you would like to use the Prisma CLI in order to perform other actions on your database (e.g. migrations) you will need to add a `DIRECT_URL` environment variable to use in the `datasource.directUrl` property so that the CLI can bypass Supavisor: + +```env file=.env highlight=4-5;add +# Connect to Supabase via connection pooling with Supavisor. +DATABASE_URL="postgres://postgres.[your-supabase-project]:[password]@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?pgbouncer=true" + +# Direct connection to the database. Used for migrations. +DIRECT_URL="postgres://postgres:[password]@db.[your-supabase-project].supabase.co:5432/postgres" +``` + +You can then update your `schema.prisma` to use the new direct URL: + +```prisma file=schema.prisma highlight=4;add +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_URL") +} +``` + +More information about the `directUrl` field can be found [here](/orm/reference/prisma-schema-reference#fields). + + + +We strongly recommend using connection pooling with Supavisor in addition to `DIRECT_URL`. You will gain the great developer experience of the Prisma CLI while also allowing for connections to be pooled regardless of your deployment strategy. While this is not strictly necessary for every app, serverless solutions will inevitably require connection pooling. + + + +## Getting started with Supabase + +If you're interested in learning more, Supabase has a great guide for connecting a database provided by Supabase to your Prisma project available [here](https://supabase.com/docs/guides/integrations/prisma). + +If you're running into issues integrating with Supabase, check out these [specific troubleshooting tips](https://supabase.com/docs/guides/integrations/prisma#troubleshooting) or [Prisma's GitHub Discussions](https://github.com/prisma/prisma/discussions) for more help. diff --git a/docs/200-orm/050-overview/500-databases/890-neon.mdx b/docs/200-orm/050-overview/500-databases/890-neon.mdx new file mode 100644 index 0000000000..64e29e934e --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/890-neon.mdx @@ -0,0 +1,183 @@ +--- +title: 'Neon' +metaTitle: 'Neon' +metaDescription: 'Guide to Neon' +tocDepth: 2 +toc: true +--- + + + +This guide explains how to: + +- [Connect Prisma using Neon's connection pooling feature](#how-to-use-neons-connection-pooling) +- [Resolve connection timeout issues](#resolving-connection-timeouts) +- [Use Neon's serverless driver with Prisma](#how-to-use-neons-serverless-driver-with-prisma-preview) + + + +## What is Neon? + +Neon's logo + +[Neon](https://neon.tech/) is a fully managed serverless PostgreSQL with a generous free tier. Neon separates storage and compute, and offers modern developer features such as serverless, branching, bottomless storage, and more. Neon is open source and written in Rust. + +Learn more about Neon [here](https://neon.tech/docs). + +## Commonalities with other database providers + +Many aspects of using Prisma with Neon are just like using Prisma with any other PostgreSQL database. You can: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- use Prisma's [`postgresql` database connector](/orm/overview/databases/postgresql) in your schema, along with the [connection string Neon provides you](https://neon.tech/docs/connect/connect-from-any-app) +- use [Introspection](/orm/prisma-schema/introspection) for existing projects if you already have a database schema on Neon +- use [`prisma migrate dev`](/orm/prisma-migrate/workflows/development-and-production) to track schema migrations in your Neon database +- use [`prisma db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) to push changes in your schema to Neon +- use [Prisma Client](/orm/prisma-client) in your application to communicate with the database hosted by Neon + +## Differences to consider + +There are a few differences between Neon and PostgreSQL you should be aware of the following when deciding to use Neon with Prisma: + +- **Neon's serverless model** — By default, Neon scales a [compute](https://neon.tech/docs/introduction/compute-lifecycle) to zero after 5 minutes of inactivity. During this state, a compute instance is in _idle_ state. A characteristic of this feature is the concept of a "cold start". Activating a compute from an idle state takes from 500ms to a few seconds. Depending on how long it takes to connect to your database, your application may timeout. To learn more, see: [Connection latency and timeouts](https://neon.tech/docs/guides/prisma#connection-timeouts). +- **Neon's connection pooler** — Neon offers connection pooling using PgBouncer, enabling up to 10,000 concurrent connections. To learn more, see: [Connection pooling](https://neon.tech/docs/connect/connection-pooling). + +## How to use Neon's connection pooling + +If you'd like to use the [connection pooling](https://neon.tech/blog/prisma-dx-improvements#providing-pooled-and-direct-connections-to-the-database) available in Neon, you will +need to add `pgbouncer=true` to the end of the `DATABASE_URL` environment variable used in the `url` property of the `datasource` block of your Prisma schema: + +```env file=.env +# Connect to Neon with PgBouncer. +DATABASE_URL=postgres://daniel:@ep-mute-rain-952417-pooler.us-east-2.aws.neon.tech:5432/neondb?pgbouncer=true +``` + +If you would like to use Prisma CLI in order to perform other actions on your database (e.g. for migrations) you will need to add a `DIRECT_URL` environment variable to use in the `directUrl` property of the `datasource` block of your Prisma schema so that the CLI will use a direct connection string (without PgBouncer): + +```env file=.env highlight=4-5;add +# Connect to Neon with PgBouncer. +DATABASE_URL=postgres://daniel:@ep-mute-rain-952417-pooler.us-east-2.aws.neon.tech/neondb?pgbouncer=true + +# Direct connection to the database used by Prisma CLI for e.g. migrations. +DIRECT_URL="postgres://daniel:@ep-mute-rain-952417.us-east-2.aws.neon.tech/neondb" +``` + +You can then update your `schema.prisma` to use the new direct URL: + +```prisma file=schema.prisma highlight=4;add +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_URL") +} +``` + +More information about the `directUrl` field can be found [here](/orm/reference/prisma-schema-reference#fields). + + + +We strongly recommend using the pooled connection string in your `DATABASE_URL` environment variable. You will gain the great developer experience of the Prisma CLI while also allowing for connections to be pooled regardless of deployment strategy. While this is not strictly necessary for every app, serverless solutions will inevitably require connection pooling. + + + +## Resolving connection timeouts + +A connection timeout that occurs when connecting from Prisma to Neon causes an error similar to the following: + +```text no-copy +Error: P1001: Can't reach database server at `ep-white-thunder-826300.us-east-2.aws.neon.tech`:`5432` +Please make sure your database server is running at `ep-white-thunder-826300.us-east-2.aws.neon.tech`:`5432`. +``` + +This error most likely means that the connection created by Prisma Client timed out before the Neon compute was activated. + +A Neon compute has two main states: _Active_ and _Idle_. Active means that the compute is currently running. If there is no query activity for 5 minutes, Neon places a compute into an idle state by default. Refer to Neon's docs to [learn more](https://neon.tech/docs/introduction/compute-lifecycle). + +When you connect to an idle compute from Prisma, Neon automatically activates it. Activation typically happens within a few seconds but added latency can result in a connection timeout. To address this issue, your can adjust your Neon connection string by adding a `connect_timeout` parameter. This parameter defines the maximum number of seconds to wait for a new connection to be opened. The default value is 5 seconds. A higher setting should provide the time required to avoid connection timeout issues. For example: + +```text wrap +DATABASE_URL=postgres://daniel:@ep-mute-rain-952417.us-east-2.aws.neon.tech/neondb?connect_timeout=10 +``` + + + +A `connect_timeout` setting of 0 means no timeout. + + + +Another possible cause of connection timeouts is Prisma's [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool), which has a default timeout of 10 seconds. This is typically enough time for Neon, but if you are still experiencing connection timeouts, you can try increasing this limit (in addition to the `connect_timeout` setting described above) by setting the `pool_timeout` parameter to a higher value. For example: + +```text wrap +DATABASE_URL=postgres://daniel:@ep-mute-rain-952417.us-east-2.aws.neon.tech/neondb?connect_timeout=15&pool_timeout=15 +``` + +## How to use Neon's serverless driver with Prisma (Preview) + +The [Neon serverless driver](https://github.com/neondatabase/serverless) is a low-latency Postgres driver for JavaScript and TypeScript that allows you to query data from serverless and edge environments over HTTP or WebSockets in place of TCP. + +You can use Prisma along with the Neon serverless driver using a [driver adapter](/orm/overview/databases/database-drivers#driver-adapters) . A driver adapter allows you to use a different database driver from the default Prisma provides to communicate with your database. + + + +This feature is available in Preview from Prisma versions 5.4.2 and later. + + + +To get started, enable the `driverAdapters` Preview feature flag: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +Generate Prisma Client: + +```sh +npx prisma generate +``` + +Install the Prisma adapter for Neon, Neon serverless driver and `ws` packages: + +```sh +npm install @prisma/adapter-neon @neondatabase/serverless ws +npm install --save-dev @types/ws +``` + +Update your Prisma Client instance: + +```ts +import { Pool, neonConfig } from '@neondatabase/serverless' +import { PrismaNeon } from '@prisma/adapter-neon' +import { PrismaClient } from '@prisma/client' +import dotenv from 'dotenv' +import ws from 'ws' + +dotenv.config() +neonConfig.webSocketConstructor = ws +const connectionString = `${process.env.DATABASE_URL}` + +const pool = new Pool({ connectionString }) +const adapter = new PrismaNeon(pool) +const prisma = new PrismaClient({ adapter }) +``` + +You can then use Prisma Client as you normally would with full type-safety. Prisma Migrate, introspection, and Prisma Studio will continue working as before, using the connection string defined in the Prisma schema. diff --git a/docs/200-orm/050-overview/500-databases/900-turso.mdx b/docs/200-orm/050-overview/500-databases/900-turso.mdx new file mode 100644 index 0000000000..51c0aafe23 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/900-turso.mdx @@ -0,0 +1,221 @@ +--- +title: 'Turso' +metaTitle: 'Turso (Early Access)' +metaDescription: 'Guide to Turso' +tocDepth: 3 +--- + + + +This guide discusses the concepts behind using Prisma and Turso, explains the commonalities and differences between Turso and other database providers, and leads you through the process for configuring your application to integrate with Turso. + +Prisma support for Turso is currently in [Early Access](/orm/more/releases#early-access). We would appreciate your feedback in this [GitHub discussion](https://github.com/prisma/prisma/discussions/21345). + + + +## What is Turso? + +Turso's logo + +[Turso](https://turso.tech/) is an edge-hosted, distributed database that's based on [libSQL](https://turso.tech/libsql), an open-source and open-contribution fork of [SQLite](https://sqlite.org/), enabling you to bring data closer to your application and minimize query latency. Turso can also be hosted on a remote server. + + + +Support for Turso is available in [Early Access](/orm/more/releases#early-access) from Prisma versions 5.4.2 and later. + + + +## Commonalities with other database providers + +libSQL is 100% compatible with SQLite. libSQL extends SQLite and adds the following features and capabilities: + +- Support for replication +- Support for automated backups +- Ability to embed Turso as part of other programs such as the Linux kernel +- Supports user-defined functions +- Support for asynchronous I/O + +> To learn more about the differences between libSQL and how it is different from SQLite, see [libSQL Manifesto](https://turso.tech/libsql-manifesto). + +Many aspects of using Prisma with Turso are just like using Prisma with any other relational database. You can still: + +- model your database with the [Prisma Schema Language](/orm/prisma-schema) +- use Prisma's existing [`sqlite` database connector](/orm/overview/databases/sqlite) in your schema +- use [Prisma Client](/orm/prisma-client) in your application to talk to the database server at Turso + +## Differences to consider + +There are a number of differences between Turso and SQLite to consider. You should be aware of the following when deciding to use Turso and Prisma: + +- **Remote and embedded SQLite databases**. libSQL uses HTTP to connect to the remote SQLite database. libSQL also supports remote database replicas and embedded replicas. Embedded replicas enable you to replicate your primary database inside your application. +- **Making schema changes**. Since libSQL uses HTTP to connect to the remote database, this makes it incompatible with Prisma Migrate. However, you can use [`prisma migrate diff`](/orm/reference/prisma-cli-reference#migrate-diff) to create a schema migration and then apply the changes to your database using [Turso's CLI](https://docs.turso.tech/reference/turso-cli). + +## How to connect and query a Turso database + +The subsequent section covers how you can create a Turso database, retrieve your database credentials and connect to your database. + +### How to provision a database and retrieve database credentials + + + +Ensure that you have the [Turso CLI](https://docs.turso.tech/reference/turso-cli) installed to manage your databases. + + + +If you don't have an existing database, you can provision a database by running the following command: + +```terminal +turso db create turso-prisma-db +``` + +The above command will create a database in the closest region to your location. + +Run the following command to retrieve your database's connection string: + +```terminal +turso db show turso-prisma-db +``` + +Next, create an authentication token that will allow you to connect to the database: + +```terminal +turso db tokens create turso-prisma-db +``` + +Update your `.env` file with the authentication token and connection string: + +```text file=.env +TURSO_AUTH_TOKEN="eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..." +TURSO_DATABASE_URL="libsql://turso-prisma-db-user.turso.io" +``` + +### How to connect to a Turso database + +To get started, enable the `driverAdapters` Preview feature flag: + +```prisma highlight=3;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} + +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} +``` + +Generate Prisma Client: + +```terminal +npx prisma generate +``` + +Install the libSQL database client and Prisma driver adapter for libSQL packages: + +```terminal +npm install @libsql/client @prisma/adapter-libsql +``` + +Update your Prisma Client instance: + +```ts +import { PrismaClient } from '@prisma/client' +import { PrismaLibSQL } from '@prisma/adapter-libsql' +import { createClient } from '@libsql/client' + +const libsql = createClient({ + url: `${process.env.TURSO_DATABASE_URL}`, + authToken: `${process.env.TURSO_AUTH_TOKEN}`, +}) + +const adapter = new PrismaLibSQL(libsql) +const prisma = new PrismaClient({ adapter }) +``` + +You can use Prisma Client as you normally would with full type-safety in your project. + +## How to manage schema changes + +Prisma Migrate and Introspection workflows are currently not supported when working with Turso. This is because Turso uses HTTP to connect to your database, which Prisma Migrate doesn't support. + +To update your database schema: + +1. Generate a migration file using `prisma migrate dev` against a local SQLite database: + + ```terminal + npx prisma migrate dev --name init + ``` + +2. Apply the migration using Turso's CLI: + + ```terminal + turso db shell turso-prisma-db < ./prisma/migrations/20230922132717_init/migration.sql + ``` + + + + Replace `20230922132717_init` with the name of your migration. + + + +For subsequent migrations, repeat the above steps to apply changes to your database. This workflow does not support track the history of applied migrations to your remote database. + +## Embedded Turso database replicas + +Turso supports [embedded replicas](https://blog.turso.tech/introducing-embedded-replicas-deploy-turso-anywhere-2085aa0dc242). Turso's embedded replicas enable you to have a copy of your primary, remote database _inside_ your application. Embedded replicas behave similarly to a local SQLite database. Database queries are faster because your database is inside your application. + +### How embedded database replicas work + +When your app initially establishes a connection to your database, the primary database will fulfill the query: + +![Embedded Replica: First remote read](./images/embedded-replica-remote-read.png) + +Turso will (1) create an embedded replica inside your application and (2) copy data from your primary database to the replica so it is locally available: + +![Embedded Replica: Remote DB Copy](./images/embedded-replica-create-replica.png) + +The embedded replica will fulfill subsequent read queries. The libSQL client provides a [`sync()`]() method which you can invoke to ensure the embedded replica's data remains fresh. + +![Embedded Replica: Local DB reads](./images/embedded-replica-read.png) + +With embedded replicas, this setup guarantees a responsive application, because the data will be readily available locally and faster to access. + +Like a read replica setup you may be familiar with, write operations are forwarded to the primary remote database and executed before being propagated to all embedded replicas. + +![Embedded Replica: Write operation propagation](./images/embedded-replica-write-propagation.png) + +1. Write operations propagation are forwarded to the database. +1. Database responds to the server with the updates from 1. +1. Write operations are propagated to the database replica. + +Your application's data needs will determine how often you should synchronize data between your remote database and embedded database replica. For example, you can use either middleware functions (e.g. Express and Fastify) or a cron job to synchronize the data. + +### How to synchronize data between your remote database and embedded replica + +To get started using embedded replicas with Prisma, add the `sync()` method from libSQL in your application. The example below shows how you can synchronize data using Express middleware. + +```ts highlight=5-8;add; +import express from 'express' +const app = express() + +// ... the rest of your application code +app.use(async (req, res, next) => { + await libsql.sync() + next() +}) + +app.listen(3000, () => console.log(`Server ready at http://localhost:3000`)) +``` diff --git a/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-engine-adapter.png b/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-engine-adapter.png new file mode 100644 index 0000000000..068f9c76c5 Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-engine-adapter.png differ diff --git a/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-execution-flow.png b/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-execution-flow.png new file mode 100644 index 0000000000..aca500b4fe Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/drivers/qe-query-execution-flow.png differ diff --git a/docs/200-orm/050-overview/500-databases/images/embedded-replica-create-replica.png b/docs/200-orm/050-overview/500-databases/images/embedded-replica-create-replica.png new file mode 100644 index 0000000000..830501d9c2 Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/embedded-replica-create-replica.png differ diff --git a/docs/200-orm/050-overview/500-databases/images/embedded-replica-read.png b/docs/200-orm/050-overview/500-databases/images/embedded-replica-read.png new file mode 100644 index 0000000000..0edaebcf2e Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/embedded-replica-read.png differ diff --git a/docs/200-orm/050-overview/500-databases/images/embedded-replica-remote-read.png b/docs/200-orm/050-overview/500-databases/images/embedded-replica-remote-read.png new file mode 100644 index 0000000000..7762e8bfaa Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/embedded-replica-remote-read.png differ diff --git a/docs/200-orm/050-overview/500-databases/images/embedded-replica-write-propagation.png b/docs/200-orm/050-overview/500-databases/images/embedded-replica-write-propagation.png new file mode 100644 index 0000000000..fa36c87335 Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/images/embedded-replica-write-propagation.png differ diff --git a/docs/200-orm/050-overview/500-databases/index.mdx b/docs/200-orm/050-overview/500-databases/index.mdx new file mode 100644 index 0000000000..81680e00f6 --- /dev/null +++ b/docs/200-orm/050-overview/500-databases/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Databases' +metaTitle: 'Databases' +metaDescription: 'Databases' +toc: false +--- + + + +Learn about the different databases Prisma supports. + + + +## In this section + + diff --git a/docs/200-orm/050-overview/500-databases/mongodb.png b/docs/200-orm/050-overview/500-databases/mongodb.png new file mode 100644 index 0000000000..192f87b08d Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/mongodb.png differ diff --git a/docs/200-orm/050-overview/500-databases/mysql-connection-string.png b/docs/200-orm/050-overview/500-databases/mysql-connection-string.png new file mode 100644 index 0000000000..5ded2e0d6f Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/mysql-connection-string.png differ diff --git a/docs/200-orm/050-overview/500-databases/postgresql-connection-string.png b/docs/200-orm/050-overview/500-databases/postgresql-connection-string.png new file mode 100644 index 0000000000..56e7347da5 Binary files /dev/null and b/docs/200-orm/050-overview/500-databases/postgresql-connection-string.png differ diff --git a/docs/200-orm/050-overview/index.mdx b/docs/200-orm/050-overview/index.mdx new file mode 100644 index 0000000000..89077e1d99 --- /dev/null +++ b/docs/200-orm/050-overview/index.mdx @@ -0,0 +1,12 @@ +--- +title: 'Overview' +metaTitle: 'Overview' +metaDescription: 'Overview' +staticLink: true +toc: false +--- + + +## In this section + + diff --git a/docs/200-orm/100-prisma-schema/10-overview/02-data-sources.mdx b/docs/200-orm/100-prisma-schema/10-overview/02-data-sources.mdx new file mode 100644 index 0000000000..7ff9a664d8 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/10-overview/02-data-sources.mdx @@ -0,0 +1,42 @@ +--- +title: 'Data sources' +metaTitle: 'Data sources (Reference)' +metaDescription: 'Data sources enable Prisma to connect to your database. This page explains how to configure data sources in your Prisma schema.' +--- + + + +A data source determines how Prisma connects your database, and is represented by the [`datasource`](/orm/reference/prisma-schema-reference#datasource) block in the Prisma schema. The following data source uses the `postgresql` provider and includes a connection URL: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public" +} +``` + +A Prisma schema can only have _one_ data source. However, you can: + +- [Programmatically override a data source `url` when creating your `PrismaClient`](/orm/reference/prisma-client-reference#programmatically-override-a-datasource-url) +- [Specify a different URL for Prisma Migrate's shadow database if you are working with cloud-hosted development databases](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database#cloud-hosted-shadow-databases-must-be-created-manually) + +> **Note**: Multiple provider support was removed in 2.22.0. Please see [Deprecation of provider array notation](https://github.com/prisma/prisma/issues/3834) for more information. + + + +## Securing database connections + +Some data source `provider`s allow you to configure your connection with SSL/TLS, and provide parameters for the `url` to specify the location of certificates. + +- [Configuring an SSL connection with PostgreSQL](/orm/overview/databases/postgresql#configuring-an-ssl-connection) +- [Configuring an SSL connection with MySQL](/orm/overview/databases/mysql#configuring-an-ssl-connection) +- [Configure a TLS connection with Microsoft SQL Server](/orm/overview/databases/sql-server#connection-details) + +Prisma resolves SSL certificates relative to the `./prisma` directory. If your certificate files are located outside that directory, e.g. your project root directory, use relative paths for certificates: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public&sslmode=require&sslcert=../server-ca.pem&sslidentity=../client-identity.p12&sslpassword=" +} +``` diff --git a/docs/200-orm/100-prisma-schema/10-overview/03-generators.mdx b/docs/200-orm/100-prisma-schema/10-overview/03-generators.mdx new file mode 100644 index 0000000000..c7190aef04 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/10-overview/03-generators.mdx @@ -0,0 +1,98 @@ +--- +title: 'Generators' +metaTitle: 'Generators (Reference)' +metaDescription: 'Generators in your Prisma schema specify what assets are generated when the `prisma generate` command is invoked. This page explains how to configure generators.' +--- + + + +A Prisma schema can have one or more generators, represented by the [`generator`](/orm/reference/prisma-schema-reference#generator) block: + +```prisma +generator client { + provider = "prisma-client-js" + output = "./generated/prisma-client-js" +} +``` + +A generator determines which assets are created when you run the `prisma generate` command. The main property `provider` defines which **Prisma Client (language specific)** is created - currently, only `prisma-client-js` is available. Alternatively you can define any npm package that follows our generator specification. Additionally and optionally you can define a custom output folder for the generated assets with `output`. + + + +## Prisma Client: `prisma-client-js` + +The generator for Prisma's JavaScript Client accepts multiple additional properties: + +- `previewFeatures`: [Preview features](/orm/reference/preview-features) to include +- `binaryTargets`: Engine binary targets for `prisma-client-js` (for example, `debian-openssl-1.1.x` if you are deploying to Ubuntu 18+, or `native` if you are working locally) + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["sample-preview-feature"] + binaryTargets = ["linux-musl"] +} +``` + +### Binary targets + +Prisma Client JS (`prisma-client-js`) uses several [engines](https://github.com/prisma/prisma-engines). Engines are implemented in Rust and are used by Prisma in the form of executable, platform dependent engine files. Depending on which platform you are executing your code on, you need the correct file. "Binary targets" are used to define which files should be present for the target platform(s). + +The correct file is particularly important when [deploying](/orm/prisma-client/deployment/deploy-prisma) your application to production, which often differs from your local development environment. + +#### The `native` binary target + +The `native` binary target is special. It doesn't map to a concrete operating system. Instead, when `native` is specified in `binaryTargets`, Prisma detects the _current_ operating system and automatically specifies the correct binary target for it. + +As an example, assume you're running **macOS** and you specify the following generator: + +```prisma +generator client { + provider = "prisma-client-js" + binaryTargets = ["native"] +} +``` + +In that case, Prisma detects your operating system and finds the right binary file for it based on the [list of supported operating systems](/orm/reference/prisma-schema-reference#binarytargets-options) . +If you use macOS Intel x86 (`darwin`), then the binary file that was compiled for `darwin` will be selected. +If you use macOS ARM64 (`darwin-arm64`), then the binary file that was compiled for `darwin-arm64` will be selected. + +> **Note**: The `native` binary target is the default. You can set it explicitly if you wish to include additional [binary targets](/orm/reference/prisma-schema-reference#binarytargets-options) for deployment to different environments. + +## Community generators + +The following is a list of community created generators. If you want to create your own generator, you can use the [`create-prisma-generator`](https://github.com/YassinEldeeb/create-prisma-generator) CLI built by our community member [Yassin Eldeep](https://github.com/YassinEldeeb). + +> **Note**: Community projects are not maintained or officially supported by Prisma and some features may be out of sync. Use at your own discretion. If you create a community generator, please use this naming convention: `prisma-generator-`. + +- [`prisma-dbml-generator`](https://notiz.dev/blog/prisma-dbml-generator): Transforms the Prisma schema into [Database Markup Language](https://www.dbml.org/home/) (DBML) which allows for an easy visual representation +- [`prisma-docs-generator`](https://github.com/pantharshit00/prisma-docs-generator): Generates an individual API reference for Prisma Client +- [`prisma-json-schema-generator`](https://github.com/valentinpalkovic/prisma-json-schema-generator): Transforms the Prisma schema in [JSON schema](https://json-schema.org/) +- [`prisma-json-types-generator`](https://github.com/arthurfiorette/prisma-json-types-generator): Adds support for [Strongly Typed `Json`](https://github.com/arthurfiorette/prisma-json-types-generator#readme) fields for all databases. It goes on `prisma-client-js` output and changes the json fields to match the type you provide. Helping with code generators, intellisense and much more. All of that without affecting any runtime code. +- [`typegraphql-prisma`](https://github.com/MichalLytek/typegraphql-prisma#readme): Generates [TypeGraphQL](https://typegraphql.com/) CRUD resolvers for Prisma models +- [`typegraphql-prisma-nestjs`](https://github.com/EndyKaufman/typegraphql-prisma-nestjs#readme): Fork of [`typegraphql-prisma`](https://github.com/MichalLytek/typegraphql-prisma), which also generates CRUD resolvers for Prisma models but for NestJS +- [`prisma-typegraphql-types-gen`](https://github.com/YassinEldeeb/prisma-tgql-types-gen): Generates [TypeGraphQL](https://typegraphql.com/) class types and enums from your prisma type definitions, the generated output can be edited without being overwritten by the next gen and has the ability to correct you when you mess up the types with your edits. +- [`nexus-prisma`](https://github.com/prisma/nexus-prisma/): Allows to project Prisma models to GraphQL via [GraphQL Nexus](https://nexusjs.org/docs/) +- [`prisma-nestjs-graphql`](https://github.com/unlight/prisma-nestjs-graphql): Generates object types, inputs, args, etc. from the Prisma schema file for usage with `@nestjs/graphql` module +- [`prisma-appsync`](https://github.com/maoosi/prisma-appsync): Generates a full-blown GraphQL API for [AWS AppSync](https://aws.amazon.com/appsync/) +- [`prisma-kysely`](https://github.com/valtyr/prisma-kysely): Generates type definitions for Kysely, a TypeScript SQL query builder. This can be useful to perform queries against your database from an edge runtime, or to write more complex SQL queries not possible in Prisma without dropping type safety. +- [`prisma-generator-nestjs-dto`](https://github.com/vegardit/prisma-generator-nestjs-dto): Generates DTO and Entity classes with relation `connect` and `create` options for use with [NestJS Resources](https://docs.nestjs.com/recipes/crud-generator) and [@nestjs/swagger](https://www.npmjs.com/package/@nestjs/swagger) +- [`prisma-erd-generator`](https://github.com/keonik/prisma-erd-generator): Generates an entity relationship diagram +- [`prisma-class-generator`](https://github.com/kimjbstar/prisma-class-generator): Generates classes from your Prisma Schema that can be used as DTO, Swagger Response, TypeGraphQL and so on. +- [`zod-prisma`](https://github.com/CarterGrimmeisen/zod-prisma): Creates Zod schemas from your Prisma models. +- [`prisma-pothos-types`](https://github.com/hayes/pothos/tree/main/packages/plugin-prisma): Makes it easier to define Prisma-based object types, and helps solve n+1 queries for relations. It also has integrations for the Relay plugin to make defining nodes and connections easy and efficient. +- [`prisma-generator-pothos-codegen`](https://github.com/Cauen/prisma-generator-pothos-codegen): Auto generate input types (for use as args) and auto generate decoupled type-safe base files makes it easy to create customizable objects, queries and mutations for [Pothos](https://pothos-graphql.dev/) from Prisma schema. Optionally generate all crud at once from the base files. +- [`prisma-joi-generator`](https://github.com/omar-dulaimi/prisma-joi-generator): Generate full Joi schemas from your Prisma schema. +- [`prisma-yup-generator`](https://github.com/omar-dulaimi/prisma-yup-generator): Generate full Yup schemas from your Prisma schema. +- [`prisma-class-validator-generator`](https://github.com/omar-dulaimi/prisma-class-validator-generator): Emit TypeScript models from your Prisma schema with class validator validations ready. +- [`prisma-zod-generator`](https://github.com/omar-dulaimi/prisma-zod-generator): Emit Zod schemas from your Prisma schema. +- [`prisma-trpc-generator`](https://github.com/omar-dulaimi/prisma-trpc-generator): Emit fully implemented tRPC routers. +- [`prisma-json-server-generator`](https://github.com/omar-dulaimi/prisma-json-server-generator): Emit a JSON file that can be run with json-server. +- [`prisma-trpc-shield-generator`](https://github.com/omar-dulaimi/prisma-trpc-shield-generator): Emit a tRPC shield from your Prisma schema. +- [`prisma-custom-models-generator`](https://github.com/omar-dulaimi/prisma-custom-models-generator): Emit custom models from your Prisma schema, based on Prisma recommendations. +- [`nestjs-prisma-graphql-crud-gen`](https://github.com/mk668a/nestjs-prisma-graphql-crud-gen): Generate CRUD resolvers from GraphQL schema with NestJS and Prisma. +- [`prisma-generator-dart`](https://github.com/FredrikBorgstrom/abcx3/tree/master/libs/prisma-generator-dart): Generates Dart/Flutter class files with to- and fromJson methods. +- [`prisma-generator-graphql-typedef`](https://github.com/mavvy22/prisma-generator-graphql-typedef): Generates graphql schema. +- [`prisma-markdown`](https://github.com/samchon/prisma-markdown): Generates markdown document composed with ERD diagrams and their descriptions. Supports pagination of ERD diagrams through `@namespace` comment tag. +- [`prisma-models-graph`](https://github.com/dangchinh25/prisma-models-graph): Generates a bi-directional models graph for schema without strict relationship defined in the schema, works via a custom schema annotation. +- [`prisma-generator-fake-data`](https://github.com/luisrudge/prisma-generator-fake-data): Generates realistic-looking fake data for your Prisma models that can be used in unit/integration tests, demos, and more. diff --git a/docs/200-orm/100-prisma-schema/10-overview/index.mdx b/docs/200-orm/100-prisma-schema/10-overview/index.mdx new file mode 100644 index 0000000000..e70164eeb6 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/10-overview/index.mdx @@ -0,0 +1,304 @@ +--- +title: 'Overview' +metaTitle: 'Prisma Schema Overview' +metaDescription: 'The Prisma schema is the main configuration file when using Prisma. It is typically called schema.prisma and contains your database connection and data model.' +--- + + + +The Prisma schema file (short: _schema file_, _Prisma schema_ or _schema_) is the main configuration file for your Prisma setup. It is typically called `schema.prisma` and consists of the following parts: + +- [**Data sources**](data-sources): Specify the details of the data sources Prisma should connect to (e.g. a PostgreSQL database) +- [**Generators**](generators): Specifies what clients should be generated based on the data model (e.g. Prisma Client) +- [**Data model definition**](/orm/prisma-schema/data-model): Specifies your application [models](/orm/prisma-schema/data-model/models#defining-models) (the shape of the data per data source) and their [relations](/orm/prisma-schema/data-model/relations) + +See the [Prisma schema API reference](/orm/reference/prisma-schema-reference) for detailed information about each section of the schema. + +Whenever a `prisma` command is invoked, the CLI typically reads some information from the schema file, e.g.: + +- `prisma generate`: Reads _all_ above mentioned information from the Prisma schema to generate the correct data source client code (e.g. Prisma Client). +- `prisma migrate dev`: Reads the data sources and data model definition to create a new migration. + +You can also [use environment variables](#accessing-environment-variables-from-the-schema) inside the schema file to provide configuration options when a CLI command is invoked. + + + +## Example + +The following is an example of a Prisma schema file that specifies: + +- A data source (PostgreSQL or MongoDB) +- A generator (Prisma Client) +- A data model definition with two models (with one relation) and one `enum` +- Several [native data type attributes](/orm/prisma-schema/data-model/models#native-types-mapping) (`@db.VarChar(255)`, `@db.ObjectId`) + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? + role Role @default(USER) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + published Boolean @default(false) + title String @db.VarChar(255) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +enum Role { + USER + ADMIN +} +``` + + + + +```prisma +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + email String @unique + name String? + role Role @default(USER) + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + published Boolean @default(false) + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId +} + +enum Role { + USER + ADMIN +} +``` + + + + +## Naming + +The default name for the schema file is `schema.prisma`. When your schema file is named like this, the Prisma CLI will detect it automatically in the directory where you invoke the CLI command (or any of its subdirectories). + +If the file is named differently, you can provide the `--schema` argument to the Prisma CLI with the path to the schema file, e.g.: + +``` +prisma generate --schema ./database/myschema.prisma +``` + +## Syntax + +The schema file is written in Prisma Schema Language (PSL). + +### VS Code + +Syntax highlighting for PSL is available via a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) (which also lets you auto-format the contents of your Prisma schema and indicates syntax errors with red squiggly lines). Learn more about [setting up Prisma in your editor](/orm/more/development-environment/editor-setup). + +### GitHub + +PSL code snippets on GitHub can be rendered with syntax highlighting as well by using the `.prisma` file extension or annotating fenced code blocks in Markdown with `prisma`: + +```` +```prisma +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? +} +``` +```` + +## Prisma schema file location + +The Prisma CLI looks for the Prisma schema file in the following locations, in the following order: + +1. The location specified by the [`--schema` flag](/orm/reference/prisma-cli-reference), which is available when you `introspect`, `generate`, `migrate`, and `studio`: + + ```terminal + prisma generate --schema=./alternative/schema.prisma + ``` + +2. The location specified in the `package.json` file (version 2.7.0 and later): + + ```json + "prisma": { + "schema": "db/schema.prisma" + } + ``` + +3. Default locations: + + - `./prisma/schema.prisma` + - `./schema.prisma` + +The Prisma CLI outputs the path of the schema file that will be used. The following example shows the terminal output for `prisma db pull`: + +```no-lines +Environment variables loaded from .env +|Prisma Schema loaded from prisma/schema.prisma + +Introspecting based on datasource defined in prisma/schema.prisma … + +✔ Introspected 4 models and wrote them into prisma/schema.prisma in 239ms + +Run prisma generate to generate Prisma Client. +``` + +## Accessing environment variables from the schema + +You can use environment variables to provide configuration options when a CLI command is invoked, or a Prisma Client query is run. + +Hardcoding URLs directly in your schema is possible but is discouraged because it poses a security risk. Using environment variables in the schema allows you to **keep secrets out of the schema file** which in turn **improves the portability of the schema** by allowing you to use it in different environments. + +Environment variables can be accessed using the `env()` function: + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +You can use the `env()` function in the following places: + +- A datasource url +- Generator binary targets + +See [Environment variables](/orm/more/development-environment/environment-variables) for more information about how to use an `.env` file during development. + +## Comments + +There are two types of comments that are supported in the schema file: + +- `// comment`: This comment is for the reader's clarity and is not present in the abstract syntax tree (AST) of the schema file. +- `/// comment`: These comments will show up in the abstract syntax tree (AST) of the schema file as descriptions to AST nodes. Tools can then use these comments to provide additional information. All comments are attached to the next available node - [free-floating comments](https://github.com/prisma/prisma/issues/3544) are not supported and are not included in the AST. + +Here are some different examples: + +```prisma +/// This comment will get attached to the `User` node in the AST +model User { + /// This comment will get attached to the `id` node in the AST + id Int @default(autoincrement()) + // This comment is just for you + weight Float /// This comment gets attached to the `weight` node +} + +// This comment is just for you. It will not +// show up in the AST. + +/// This comment will get attached to the +/// Customer node. +model Customer {} +``` + +## Auto formatting + +Prisma supports formatting `.prisma` files automatically. There are two ways to format `.prisma` files: + +- Run the [`prisma format`](/orm/reference/prisma-cli-reference#format) command. +- Install the [Prisma VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) and invoke the [VS Code format action](https://code.visualstudio.com/docs/editor/codebasics#_formatting) - manually or on save. + +There are no configuration options - [formatting rules](#formatting-rules) are fixed (similar to Golang's `gofmt` but unlike Javascript's `prettier`): + +### Formatting rules + +#### Configuration blocks are aligned by their `=` sign. + +``` +block _ { + key = "value" + key2 = 1 + long_key = true +} +``` + +A newline resets block alignment: + +``` +block _ { + key = "value" + key2 = 1 + key10 = true + + long_key = true + long_key_2 = true +} +``` + +#### Field definitions are aligned into columns separated by 2 or more spaces + +``` +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 +} +``` + +A newline resets formatting rules: + +``` +block _ { + id String @id + @default + + first_name LongNumeric @default +} + +``` + +#### Block attributes are sorted to the end of the block + +``` +block _ { + key = "value" + + @@attribute +} +``` diff --git a/docs/200-orm/100-prisma-schema/20-data-model/10-models.mdx b/docs/200-orm/100-prisma-schema/20-data-model/10-models.mdx new file mode 100644 index 0000000000..22b8aa92e9 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/10-models.mdx @@ -0,0 +1,1186 @@ +--- +title: 'Models' +metaTitle: 'Models' +metaDescription: 'Learn about the concepts for building your data model with Prisma: Models, scalar types, enums, attributes, functions, IDs, default values and more.' +tocDepth: 3 +--- + + + +The data model definition part of the [Prisma schema](/orm/prisma-schema) defines your application models (also called **Prisma models**). Models: + +- Represent the **entities** of your application domain +- Map to the **tables** (relational databases like PostgreSQL) or **collections** (MongoDB) in your database +- Form the foundation of the **queries** available in the generated [Prisma Client API](/orm/prisma-client) +- When used with TypeScript, Prisma Client provides generated **type definitions** for your models and any [variations](/orm/prisma-client/type-safety/operating-against-partial-structures-of-model-types) of them to make database access entirely type safe. + +The following schema describes a blogging platform - the data model definition is highlighted: + + + + +```prisma highlight=10-46;normal +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + user User @relation(fields: [userId], references: [id]) + userId Int @unique +} + +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] +} + +enum Role { + USER + ADMIN +} +``` + + + + +```prisma highlight=10-45;normal +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + bio String + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + categoryIDs String[] @db.ObjectId + categories Category[] @relation(fields: [categoryIDs], references: [id]) +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + postIDs String[] @db.ObjectId + posts Post[] @relation(fields: [postIDs], references: [id]) +} + +enum Role { + USER + ADMIN +} +``` + + + + +The data model definition is made up of: + +- [Models](#defining-models) ([`model`](/orm/reference/prisma-schema-reference#model) primitives) that define a number of fields, including [relations between models](#relation-fields) +- [Enums](#defining-enums) ([`enum`](/orm/reference/prisma-schema-reference#enum) primitives) (if your connector supports Enums) +- [Attributes](#defining-attributes) and [functions](#using-functions) that change the behavior of fields and models + +The corresponding database looks like this: + +![](../prisma-schema/sample-database.png) + +
+ +A model maps to the underlying structures of the data source. + +- In relational databases like PostgreSQL and MySQL, a `model` maps to a **table** +- In MongoDB, a `model` maps to a **collection** + +> **Note**: In the future there might be connectors for non-relational databases and other data sources. For example, for a REST API it would map to a _resource_. + +
+ +The following query uses Prisma Client that's generated from this data model to create: + +- A `User` record +- Two nested `Post` records +- Three nested `Category` records + + + + + +```ts +const user = await prisma.user.create({ + data: { + email: 'ariadne@prisma.io', + name: 'Ariadne', + posts: { + create: [ + { + title: 'My first day at Prisma', + categories: { + create: { + name: 'Office', + }, + }, + }, + { + title: 'How to connect to a SQLite database', + categories: { + create: [{ name: 'Databases' }, { name: 'Tutorials' }], + }, + }, + ], + }, + }, +}) +``` + + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({}) + +// A `main` function so that you can use async/await +async function main() { + // Create user, posts, and categories + const user = await prisma.user.create({ + data: { + email: 'ariadne@prisma.io', + name: 'Ariadne', + posts: { + create: [ + { + title: 'My first day at Prisma', + categories: { + create: { + name: 'Office', + }, + }, + }, + { + title: 'How to connect to a SQLite database', + categories: { + create: [{ name: 'Databases' }, { name: 'Tutorials' }], + }, + }, + ], + }, + }, + }) + + // Return user, and posts, and categories + const returnUser = await prisma.user.findUnique({ + where: { + id: user.id, + }, + include: { + posts: { + include: { + categories: true, + }, + }, + }, + }) + + console.log(returnUser) +} + +main() +``` + + + + + +Your data model reflects _your_ application domain. For example: + +- In an **ecommerce** application you probably have models like `Customer`, `Order`, `Item` and `Invoice`. +- In a **social media** application you probably have models like `User`, `Post`, `Photo` and `Message`. + +
+ +## Introspection and migration + +There are two ways to define a data model: + +- **Write the data model manually and use Prisma Migrate**: You can write your data model manually and map it to your database using [Prisma Migrate](/orm/prisma-migrate). In this case, the data model is the single source of truth for the models of your application. +- **Generate the data model via introspection**: When you have an existing database or prefer migrating your database schema with SQL, you generate the data model by [introspecting](/orm/prisma-schema/introspection) your database. In this case, the database schema is the single source of truth for the models of your application. + +## Defining models + +Models represent the entities of your application domain. Models are represented by [`model`](/orm/reference/prisma-schema-reference#model) blocks and define a number of [fields](/orm/reference/prisma-schema-reference#model-fields). In the example data model above, `User`, `Profile`, `Post` and `Category` are models. + +A blogging platform can be extended with the following models: + +```prisma +model Comment { + // Fields +} + +model Tag { + // Fields +} +``` + +### Mapping model names to tables or collections + +Prisma model [naming conventions (singular form, PascalCase)](/orm/reference/prisma-schema-reference#naming-conventions) do not always match table names in the database. A common approach for naming tables/collections in databases is to use plural form and [snake_case](https://en.wikipedia.org/wiki/Snake_case) notation - for example: `comments`. When you introspect a database with a table named `comments`, the result Prisma model will look like this: + +```prisma +model comments { + // Fields +} +``` + +However, you can still adhere to the naming convention without renaming the underlying `comments` table in the database by using the [`@@map`](/orm/reference/prisma-schema-reference#map-1) attribute: + +```prisma +model Comment { + // Fields + + @@map("comments") +} +``` + +With this model definition, Prisma automatically maps the `Comment` model to the `comments` table in the underlying database. + +> **Note**: You can also [`@map`](/orm/reference/prisma-schema-reference#map) a column name or enum value, and `@@map` an enum name. + +`@map` and `@@map` allow you to [tune the shape of your Prisma Client API](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names#using-map-and-map-to-rename-fields-and-models-in-the-prisma-client-api) by decoupling model and field names from table and column names in the underlying database. + +
+ + + +
+ +## Defining fields + +The properties of a model are called _fields_, which consist of: + +- A **[field name](/orm/reference/prisma-schema-reference#model-fields)** +- A **[field type](/orm/reference/prisma-schema-reference#model-fields)** +- Optional **[type modifiers](#type-modifiers)** +- Optional **[attributes](#defining-attributes)**, including [native database type attributes](#native-types-mapping) + +A field's type determines its _structure_, and fits into one of two categories: + +- [Scalar types](#scalar-fields) (includes [enums](#defining-enums)) that map to columns (relational databases) or document fields (MongoDB) in the database - for example, [`String`](/orm/reference/prisma-schema-reference#string) or [`Int`](/orm/reference/prisma-schema-reference#int) +- Model types (the field is then called [relation field](relations#relation-fields)) - for example `Post` or `Comment[]`. + +The following table describes `User` model's fields from the sample schema: + +
+ +Expand to see table + +| Name | Type | Scalar vs Relation | Type modifier | Attributes | +| :-------- | :-------- | :---------------------------- | :------------ | :------------------------------------ | +| `id` | `Int` | Scalar | - | `@id` and `@default(autoincrement())` | +| `email` | `String` | Scalar | - | `@unique` | +| `name` | `String` | Scalar | `?` | - | +| `role` | `Role` | Scalar (`enum`) | - | `@default(USER)` | +| `posts` | `Post` | Relation (Prisma-level field) | `[]` | - | +| `profile` | `Profile` | Relation (Prisma-level field) | `?` | - | + +
+ +### Scalar fields + +The following example extends the `Comment` and `Tag` models with several scalar types. Some fields include [attributes](#defining-attributes): + + + + +```prisma highlight=2-4,8;normal +model Comment { + id Int @id @default(autoincrement()) + title String + content String +} + +model Tag { + name String @id +} +``` + + + + +```prisma highlight=2-4,8;normal +model Comment { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String +} + +model Tag { + name String @id @map("_id") +} +``` + + + + +See [complete list of scalar field types](/orm/reference/prisma-schema-reference#model-field-scalar-types) . + +### Relation fields + +A relation field's type is another model - for example, a post (`Post`) can have multiple comments (`Comment[]`): + + + + +```prisma highlight=4,10;normal +model Post { + id Int @id @default(autoincrement()) + // Other fields + comments Comment[] // A post can have many comments +} + +model Comment { + id Int + // Other fields + Post Post? @relation(fields: [postId], references: [id]) // A comment can have one post + postId Int? +} +``` + + + + +```prisma highlight=4,10;normal +model Post { + id String @id @default(auto()) @map("_id") @db.Objectid + // Other fields + comments Comment[] // A post can have many comments +} + +model Comment { + id String @id @default(auto()) @map("_id") @db.Objectid + // Other fields + Post Post? @relation(fields: [postId], references: [id]) // A comment can have one post + postId String? @db.ObjectId +} +``` + + + + +Refer to the [relations documentation](relations) for more examples and information about relationships between models. + +### Native types mapping + +Version [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later support **native database type attributes** (type attributes) that describe the underlying database type: + +```prisma highlight=3;normal +model Post { + id Int @id + title String @db.VarChar(200) + content String +} +``` + +Type attributes are: + +- Specific to the underlying provider - for example, PostgreSQL uses `@db.Boolean` for `Boolean` whereas MySQL uses `@db.TinyInt(1)` +- Written in PascalCase (for example, `VarChar` or `Text`) +- Prefixed by `@db`, where `db` is the name of the `datasource` block in your schema + +Furthermore, during [Introspection](/orm/prisma-schema/introspection) type attributes are _only_ added to the schema if the underlying native type is **not the default type**. For example, if you are using the PostgreSQL provider, `String` fields where the underlying native type is `text` will not have a type attribute. + +See [complete list of native database type attributes per scalar type and provider](/orm/reference/prisma-schema-reference#model-field-scalar-types) . + +#### Benefits and workflows + +- Control **the exact native type** that [Prisma Migrate](/orm/prisma-migrate) creates in the database - for example, a `String` can be `@db.VarChar(200)` or `@db.Char(50)` +- See an **enriched schema** when you introspect + +### Type modifiers + +The type of a field can be modified by appending either of two modifiers: + +- [`[]`](/orm/reference/prisma-schema-reference#-modifier) Make a field a list +- [`?`](/orm/reference/prisma-schema-reference#-modifier-1) Make a field optional + +> **Note**: You **cannot** combine type modifiers - optional lists are not supported. + +#### Lists + +The following example includes a scalar list and a list of related models: + + + + +```prisma highlight=4,5;normal +model Post { + id Int @id @default(autoincrement()) + // Other fields + comments Comment[] // A list of comments + keywords String[] // A scalar list +} +``` + + + + +```prisma highlight=4,5;normal +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + // Other fields + comments Comment[] // A list of comments + keywords String[] // A scalar list +} +``` + + + + +> **Note**: Scalar lists are **only** supported if the database connector supports scalar lists, either natively or at a Prisma level. + +#### Optional and mandatory fields + + + + +```prisma highlight=4;normal +model Comment { + id Int @id @default(autoincrement()) + title String + content String? +} + +model Tag { + name String @id +} +``` + + + + +```prisma highlight=4;normal +model Comment { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String? +} + +model Tag { + name String @id @map("_id") +} +``` + + + + +When **not** annotating a field with the `?` type modifier, the field will be _required_ on every record of the model. This has effects on two levels: + +- **Databases** + - **Relational databases**: Required fields are represented via `NOT NULL` constraints in the underlying database. + - **MongoDB**: Required fields are not a concept on a MongoDB database level. +- **Prisma Client**: Prisma Client's generated [TypeScript types](#type-definitions) that represent the models in your application code will also define these fields as required to ensure they always carry values at runtime. + +> **Note**: The default value of an optional field is `null`. + +### Unsupported types + +When you introspect a relational database, unsupported data types are added as [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) : + +```prisma +location Unsupported("POLYGON")? +``` + +The `Unsupported` type allows you to define fields in the Prisma schema for database types that are not yet supported by Prisma. For example, MySQL's `POLYGON` type is not currently supported by Prisma, but can now be added to the Prisma schema using the `Unsupported("POLYGON")` type. + +Fields of type `Unsupported` are not available in the generated Prisma Client API, but you can still use Prisma's [raw database access](/orm/prisma-client/queries/raw-database-access/raw-queries) feature to query these fields. + +> **Note**: If a model has **mandatory `Unsupported` fields**, the generated client will not include `create` or `update` methods for that model. + +> **Note**: The MongoDB connector does not support nor require the `Unsupported` type because it supports all scalar types. + +## Defining attributes + +Attributes modify the behavior of fields or model blocks. The following example includes three field attributes ([`@id`](/orm/reference/prisma-schema-reference#id) , [`@default`](/orm/reference/prisma-schema-reference#default) , and [`@unique`](/orm/reference/prisma-schema-reference#unique) ) and one block attribute ([`@@unique`](/orm/reference/prisma-schema-reference#unique-1) ): + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@unique([firstName, lastName]) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@unique([firstName, lastName]) +} +``` + + + + +Some attributes accept [arguments](/orm/reference/prisma-schema-reference#attribute-argument-types) - for example, `@default` accepts `true` or `false`: + +```prisma +isAdmin Boolean @default(false) // short form of @default(value: false) +``` + +See [complete list of field and block attributes](/orm/reference/prisma-schema-reference#attributes) + +### Defining an ID field + +An ID uniquely identifies individual records of a model. A model can only have _one_ ID: + +- In **relational databases**, the ID can be a single field or based on multiple fields. If a model does not have an `@id` or an `@@id`, you must define a mandatory `@unique` field or `@@unique` block instead. +- In **MongoDB**, an ID must be a single field that defines an `@id` attribute and a `@map("_id")` attribute. + +#### Defining IDs in relational databases + +In relational databases, an ID can be defined by a single field using the [`@id`](/orm/reference/prisma-schema-reference#id) attribute, or multiple fields using the [`@@id`](/orm/reference/prisma-schema-reference#id-1) attribute. + +##### Single field IDs + +In the following example, the `User` ID is represented by the `id` integer field: + +```prisma highlight=2;normal +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + +##### Composite IDs + +In the following example, the `User` ID is represented by a combination of the `firstName` and `lastName` fields: + +```prisma highlight=7;normal +model User { + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@id([firstName, lastName]) +} +``` + +By default, the name of this field in Prisma Client queries will be `firstName_lastName`. + +You can also provide your own name for the composite ID using the [`@@id`](/orm/reference/prisma-schema-reference#id-1) attribute's `name` field: + +```prisma highlight=7;normal +model User { + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@id(name: "fullName", fields: [firstName, lastName]) +} +``` + +The `firstName_lastName` field will now be named `fullName` instead. + + + +Refer to the documentation on [working with composite IDs](/orm/prisma-client/special-fields-and-types/working-with-composite-ids-and-constraints) to learn how to interact with a composite ID in Prisma Client. + + + +##### `@unique` fields as unique identifiers + +In the following example, users are uniquely identified by a `@unique` field. Because the `email` field functions as a unique identifier for the model (which is required by Prisma), it must be mandatory: + +```prisma highlight=2;normal +model User { + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + + + +**Constraint names in relational databases**
+You can optionally define a [custom primary key constraint name](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) in the underlying database. + +
+ +#### Defining IDs in MongoDB + +The MongoDB connector has [specific rules for defining an ID field](/orm/reference/prisma-schema-reference#mongodb) that differs from relational databases. An ID must be defined by a single field using the [`@id`](/orm/reference/prisma-schema-reference#id) attribute and must include `@map("_id")`. + +In the following example, the `User` ID is represented by the `id` string field that accepts an auto-generated `ObjectId`: + +```prisma highlight=2;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + +In the following example, the `User` ID is represented by the `id` string field that accepts something other than an `ObjectId` - for example, a unique username: + +```prisma highlight=2;normal +model User { + id String @id @map("_id") + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + + + +**MongoDB does not support `@@id`**
+MongoDB does not support composite IDs, which means you cannot identify a model with a `@@id` block. + +
+ +### Defining a default value + +You can define default values for scalar fields of your models using the [`@default`](/orm/reference/prisma-schema-reference#default) attribute: + + + + +```prisma highlight=3,5;normal +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int + categories Category[] @relation(references: [id]) +} +``` + + + + +```prisma highlight=3,5;normal +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + categories Category[] @relation(references: [id]) +} +``` + + + + +`@default` attributes either: + +- Represent `DEFAULT` values in the underlying database (relational databases only) _or_ +- Use a Prisma-level function. For example, `cuid()` and `uuid()` are provided by Prisma's [query engine](/orm/more/under-the-hood/engines) for all connectors. + +Default values can be: + +- Static values that correspond to the field type, such as `5` (`Int`), `Hello` (`String`), or `false` (`Boolean`) +- [Lists](/orm/reference/prisma-schema-reference#-modifier) of static values, such as `[5, 6, 8]` (`Int[]`) or `["Hello", "Goodbye"]` (`String`[]). These are available in versions `4.0.0` and later, when using databases where Prisma supports them (PostgreSQL, CockroachDB and MongoDB) +- [Functions](#using-functions), such as [`now()`](/orm/reference/prisma-schema-reference#now) or [`uuid()`](/orm/reference/prisma-schema-reference#uuid) + + + +Refer to the [attribute function reference documentation](/orm/reference/prisma-schema-reference#attribute-functions) for information about connector support for functions. + + + +### Defining a unique field + +You can add unique attributes to your models to be able to uniquely identify individual records of that model. Unique attributes can be defined on a single field using [`@unique`](/orm/reference/prisma-schema-reference#unique) attribute, or on multiple fields (also called composite or compound unique constraints) using the [`@@unique`](/orm/reference/prisma-schema-reference#unique-1) attribute. + +In the following example, the value of the `email` field must be unique: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? +} +``` + + + + +In the following example, a combination of `authorId` and `title` must be unique: + + + + +```prisma highlight=10;normal +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int + categories Category[] @relation(references: [id]) + + @@unique([authorId, title]) +} +``` + + + + +```prisma highlight=10;normal +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + categories Category[] @relation(references: [id]) + + @@unique([authorId, title]) +} +``` + + + + + + +**Constraint names in relational databases**
+You can optionally define a [custom unique constraint name](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) in the underlying database. + +
+ +By default, the name of this field in Prisma Client queries will be `authorId_title`. + +You can also provide your own name for the composite unique constraint using the [`@@unique`](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) attribute's `name` field: + +```prisma highlight=10;normal +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + categories Category[] @relation(references: [id]) + + @@unique(name: "authorTitle", [authorId, title]) +} +``` + +The `authorId_title` field will now be named `authorTitle` instead. + + + +Refer to the documentation on [working with composite unique identifiers](/orm/prisma-client/special-fields-and-types/working-with-composite-ids-and-constraints) to learn how to interact with a composite unique constraints in Prisma Client. + + + +#### Composite type unique constraints + +When using the MongoDB provider in version `3.12.0` and later, you can define a unique constraint on a field of a [composite type](#defining-composite-types) using the syntax `@@unique([compositeType.field])`. As with other fields, composite type fields can be used as part of a multi-column unique constraint. + +The following example defines a multi-column unique constraint based on the `email` field of the `User` model and the `number` field of the `Address` composite type which is used in `User.address`: + +```prisma file=schema.prisma +type Address { + street String + number Int +} + +model User { + id Int @id + email String + address Address + + @@unique([email, address.number]) +} +``` + +This notation can be chained if there is more than one nested composite type: + +```prisma file=schema.prisma +type City { + name String +} + +type Address { + number Int + city City +} + +model User { + id Int @id + address Address[] + + @@unique([address.city.name]) +} +``` + +### Defining an index + +You can define indexes on one or multiple fields of your models via the [`@@index`](/orm/reference/prisma-schema-reference#index) on a model. The following example defines a multi-column index based on the `title` and `content` field: + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + + @@index([title, content]) +} +``` + + + +**Index names in relational databases**
+You can optionally define a [custom index name](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) in the underlying database. + +
+ +#### Defining composite type indexes + +When using the MongoDB provider in version `3.12.0` and later, you can define an index on a field of a [composite type](#defining-composite-types) using the syntax `@@index([compositeType.field])`. As with other fields, composite type fields can be used as part of a multi-column index. + +The following example defines a multi-column index based on the `email` field of the `User` model and the `number` field of the `Address` composite type: + +```prisma file=schema.prisma +type Address { + street String + number Int +} + +model User { + id Int @id + email String + address Address + + @@index([email, address.number]) +} +``` + +This notation can be chained if there is more than one nested composite type: + +```prisma file=schema.prisma +type City { + name String +} + +type Address { + number Int + city City +} + +model User { + id Int @id + address Address[] + + @@index([address.city.name]) +} +``` + +## Defining enums + +You can define enums in your data model [if enums are supported for your database connector](/orm/reference/database-features#misc), either natively or at Prisma level. + +Enums are considered [scalar](#scalar-fields) types in the Prisma data model. They're therefore [by default](/orm/prisma-client/queries/select-fields#return-the-default-selection-set) included as return values in [Prisma Client queries](/orm/prisma-client/queries/crud). + +Enums are defined via the [`enum`](/orm/reference/prisma-schema-reference#enum) block. For example, a `User` has a `Role`: + + + + +```prisma highlight=5,8-11;normal +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + role Role @default(USER) +} + +enum Role { + USER + ADMIN +} +``` + + + + +```prisma highlight=5,8-11;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + role Role @default(USER) +} + +enum Role { + USER + ADMIN +} +``` + + + + +## Defining composite types + + + +Composite types were added in version `3.10.0` under the `mongodb` Preview feature flag and are in General Availability since version `3.12.0`. + + + + + +Composite types are currently only available on MongoDB. + + + +Composite types (known as [embedded documents](https://docs.mongodb.com/manual/core/data-model-design/#std-label-data-modeling-embedding) in MongoDB) provide support for embedding records inside other records, by allowing you to define new object types. Composite types are structured and typed in a similar way to [models](#defining-models). + +To define a composite type, use the `type` block. As an example, take the following schema: + +```prisma file=schema.prisma +model Product { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + photos Photo[] +} + +type Photo { + height Int + width Int + url String +} +``` + +In this case, the `Product` model has a list of `Photo` composite types stored in `photos`. + +### Considerations when using composite types + +Composite types only support a limited set of [attributes](/orm/reference/prisma-schema-reference#attributes). The following attributes are supported: + +- `@default` +- `@map` +- [Native types](/orm/reference/prisma-schema-reference#model-field-scalar-types), such as `@db.ObjectId` + +The following attributes are not supported inside composite types: + +- `@unique` +- `@id` +- `@relation` +- `@ignore` +- `@updatedAt` + +However, unique constraints can still be defined by using the `@@unique` attribute on the level of the model that uses the composite type. For more details, see [Composite type unique constraints](#composite-type-unique-constraints). + +Indexes can be defined by using the `@@index` attribute on the level of the model that uses the composite type. For more details, see [Composite type indexes](#defining-composite-type-indexes). + +## Using functions + +The Prisma schema supports a number of [functions](/orm/reference/prisma-schema-reference#attribute-functions) . These can be used to specify [default values](/orm/reference/prisma-schema-reference#default) on fields of a model. + +For example, the default value of `createdAt` is [`now()`](/orm/reference/prisma-schema-reference#now) : + + + + +```prisma +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) +} +``` + + + + +```prisma +model Post { + id String @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) +} +``` + + + + +[`cuid()`](/orm/reference/prisma-schema-reference#cuid) and [`uuid()`](/orm/reference/prisma-schema-reference#uuid) are implemented by Prisma and therefore are not "visible" in the underlying database schema. You can still use them when using [introspection](/orm/prisma-schema/introspection) by [manually changing your Prisma schema](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) and [generating Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client), in that case the values will be generated by Prisma's [query engine](/orm/more/under-the-hood/engines) + +Support for [`autoincrement()`](/orm/reference/prisma-schema-reference#autoincrement) , [`now()`](/orm/reference/prisma-schema-reference#now) and [`dbgenerated()`](/orm/reference/prisma-schema-reference#dbgenerated) differ between databases. + +**Relational database connectors** implement `autoincrement()`, `dbgenerated()`, and `now()` at database level. The **MongoDB connector** does not support `autoincrement()` or `dbgenerated()`, and `now()` is implemented at Prisma level. The [`auto()`](/orm/reference/prisma-schema-reference#auto) function is used to generate an `ObjectId`. + +## Relations + +Refer to the [relations documentation](relations) for more examples and information about relationships between models. + +## Models in Prisma Client + +### Queries (CRUD) + +Every model in the data model definition will result in a number of CRUD queries in the generated [Prisma Client API](/orm/prisma-client): + +- [`findMany`](/orm/reference/prisma-client-reference#findmany) +- [`findFirst`](/orm/reference/prisma-client-reference#findfirst) +- [`findFirstOrThrow`](/orm/reference/prisma-client-reference#findfirstorthrow) +- [`findUnique`](/orm/reference/prisma-client-reference#findunique) +- [`findUniqueOrThrow`](/orm/reference/prisma-client-reference#finduniqueorthrow) +- [`create`](/orm/reference/prisma-client-reference#create) +- [`update`](/orm/reference/prisma-client-reference#update) +- [`upsert`](/orm/reference/prisma-client-reference#upsert) +- [`delete`](/orm/reference/prisma-client-reference#delete) +- [`createMany`](/orm/reference/prisma-client-reference#createmany) +- [`updateMany`](/orm/reference/prisma-client-reference#updatemany) +- [`deleteMany`](/orm/reference/prisma-client-reference#deletemany) + +The operations are accessible via a generated property on the Prisma Client instance. By default the name of the property is the lowercase form of the model name, e.g. `user` for a `User` model or `post` for a `Post` model. + +Here is an example illustrating the use of a `user` property from the Prisma Client API: + +```js +const newUser = await prisma.user.create({ + data: { + name: 'Alice', + }, +}) +const allUsers = await prisma.user.findMany() +``` + +### Type definitions + +Prisma Client also generates **type definitions** that reflect your model structures. These are part of the generated [`@prisma/client`](/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-prismaclient-npm-package) node module. + +When using TypeScript, these type definitions ensure that all your database queries are entirely type safe and validated at compile-time (even partial queries using [`select`](/orm/reference/prisma-client-reference#select) or [`include`](/orm/reference/prisma-client-reference#include) ). + +Even when using plain JavaScript, the type definitions are still included in the `@prisma/client` node module, enabling features like [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense)/autocompletion in your editor. + +> **Note**: The actual types are stored in the `.prisma/client` folder. `@prisma/client/index.d.ts` exports the contents of this folder. + +For example, the type definition for the `User` model from above would look as follows: + +```ts +export type User = { + id: number + email: string + name: string | null + role: string +} +``` + +Note that the relation fields `posts` and `profile` are not included in the type definition by default. However, if you need variations of the `User` type you can still define them using some of [Prisma Client's generated helper types](/orm/prisma-client/setup-and-configuration/generating-prisma-client) (in this case, these helper types would be called `UserGetIncludePayload` and `UserGetSelectPayload`). + +## Limitations + +### Records must be uniquely identifiable + +Prisma currently only supports models that have at least one unique field or combination of fields. In practice, this means that every Prisma model must have either at least one of the following attributes: + +- `@id` or `@@id` for a single- or multi-field primary key constraint (max one per model) +- `@unique` or `@@unique` for a single- or multi-field unique constraint diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/100-one-to-one-relations.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/100-one-to-one-relations.mdx new file mode 100644 index 0000000000..4d1fe55b19 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/100-one-to-one-relations.mdx @@ -0,0 +1,306 @@ +--- +title: One-to-one relations +metaDescription: How to define and work with one-to-one relations in Prisma. +tocDepth: 3 +--- + + + +This page introduces one-to-one relations and explains how to use them in your Prisma schema. + + + +## Overview + +One-to-one (1-1) relations refer to relations where at most **one** record can be connected on both sides of the relation. In the example below, there is a one-to-one relation between `User` and `Profile`: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int @unique // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +The `userId` relation scalar is a direct representation of the foreign key in the underlying database. This one-to-one relation expresses the following: + +- "a user can have zero profiles or one profile" (because the `profile` field is [optional](/orm/prisma-schema/data-model/models#type-modifiers) on `User`) +- "a profile must always be connected to one user" + +In the previous example, the `user` relation field of the `Profile` model references the `id` field of the `User` model. You can also reference a different field. In this case, you need to mark the field with the `@unique` attribute, to guarantee that there is only a single `User` connected to each `Profile`. In the following example, the `user` field references an `email` field in the `User` model, which is marked with the `@unique` attribute: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique // <-- add unique attribute + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + user User @relation(fields: [userEmail], references: [email]) + userEmail String @unique // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique // <-- add unique attribute + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User @relation(fields: [userEmail], references: [email]) + userEmail String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + + + +In MySQL, you can create a foreign key with only an index on the referenced side, and not a unique constraint. In Prisma versions 4.0.0 and later, if you introspect a relation of this type it will trigger a validation error. To fix this, you will need to add a `@unique` constraint to the referenced field. + + + +## Multi-field relations in relational databases + +In **relational databases only**, you can also use [multi-field IDs](/orm/reference/prisma-schema-reference#id-1) to define a 1-1 relation: + +```prisma +model User { + firstName String + lastName String + profile Profile? + + @@id([firstName, lastName]) +} + +model Profile { + id Int @id @default(autoincrement()) + user User @relation(fields: [userFirstName, userLastName], references: [firstName, lastName]) + userFirstName String // relation scalar field (used in the `@relation` attribute above) + userLastName String // relation scalar field (used in the `@relation` attribute above) + + @@unique([userFirstName, userLastName]) +} +``` + +## 1-1 relations in the database + +### Relational databases + +The following example demonstrates how to create a 1-1 relation in SQL: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Profile" ( + id SERIAL PRIMARY KEY, + "userId" INTEGER NOT NULL UNIQUE, + FOREIGN KEY ("userId") REFERENCES "User"(id) +); +``` + +Notice that there is a `UNIQUE` constraint on the foreign key `userId`. If this `UNIQUE` constraint was missing, the relation would be considered a [1-n relation](one-to-many-relations). + +The following example demonstrates how to create a 1-1 relation in SQL using a composite key (`firstName` and `lastName`): + +```sql +CREATE TABLE "User" ( + firstName TEXT, + lastName TEXT, + PRIMARY KEY ("firstName","lastName") +); +CREATE TABLE "Profile" ( + id SERIAL PRIMARY KEY, + "userFirstName" TEXT NOT NULL, + "userLastName" TEXT NOT NULL, + UNIQUE ("userFirstName", "userLastName") + FOREIGN KEY ("userFirstName", "userLastName") REFERENCES "User"("firstName", "lastName") +); +``` + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following MongoDB document represents a `User`: + +```json +{ "_id": { "$oid": "60d58e130011041800d209e1" }, "name": "Bob" } +``` + +The following MongoDB document represents a `Profile` - notice the `userId` field, which references the `User` document's `$oid`: + +```json +{ + "_id": { "$oid": "60d58e140011041800d209e2" }, + "bio": "I'm Bob, and I like drawing.", + "userId": { "$oid": "60d58e130011041800d209e1" } +} +``` + +## Required and optional 1-1 relation fields + +In a one-to-one relation, the side of the relation _without_ a relation scalar (the field representing the foreign key in the database) _must_ be optional: + +```prisma highlight=3;normal +model User { + id Int @id @default(autoincrement()) + profile Profile? // No relation scalar - must be optional +} +``` + +This restriction was introduced in 2.12.0. + +However, you can choose if the side of the relation _with_ a relation scalar should be optional or mandatory. + +### Mandatory 1-1 relation + +In the following example, `profile` and `profileId` are mandatory. This means that you cannot create a `User` without connecting or creating a `Profile`: + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile @relation(fields: [profileId], references: [id]) // references `id` of `Profile` + profileId Int @unique // relation scalar field (used in the `@relation` attribute above) +} + +model Profile { + id Int @id @default(autoincrement()) + user User? +} +``` + +### Optional 1-1 relation + +In the following example, `profile` and `profileId` are optional. This means that you can create a user without connecting or creating a `Profile`: + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile? @relation(fields: [profileId], references: [id]) // references `id` of `Profile` + profileId Int? @unique // relation scalar field (used in the `@relation` attribute above) +} + +model Profile { + id Int @id @default(autoincrement()) + user User? +} +``` + +## Choosing which side should store the foreign key in a 1-1 relation + +In **1-1 relations**, you can decide yourself which side of the relation you want to annotate with the `@relation` attribute (and therefore holds the foreign key). + +In the following example, the relation field on the `Profile` model is annotated with the `@relation` attribute. `userId` is a direct representation of the foreign key in the underlying database: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int @unique // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId +} +``` + + + + +You can also annotate the other side of the relation with the `@relation` attribute. The following example annotates the relation field on the `User` model. `profileId` is a direct representation of the foreign key in the underlying database: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile? @relation(fields: [profileId], references: [id]) + profileId Int? @unique // relation scalar field (used in the `@relation` attribute above) +} + +model Profile { + id Int @id @default(autoincrement()) + user User? +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + profile Profile? @relation(fields: [profileId], references: [id]) + profileId String? @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User? +} +``` + + + diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/200-one-to-many-relations.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/200-one-to-many-relations.mdx new file mode 100644 index 0000000000..7c5fea1548 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/200-one-to-many-relations.mdx @@ -0,0 +1,280 @@ +--- +title: One-to-many relations +metaDescription: How to define and work with one-to-many relations in Prisma. +tocDepth: 3 +--- + + + +This page introduces one-to-many relations and explains how to use them in your Prisma schema. + + + +## Overview + +One-to-many (1-n) relations refer to relations where one record on one side of the relation can be connected to zero or more records on the other side. In the following example, there is one one-to-many relation between the `User` and `Post` models: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId +} +``` + + + + +> **Note** The `posts` field does not "manifest" in the underlying database schema. On the other side of the relation, the [annotated relation field](/orm/prisma-schema/data-model/relations#relation-fields) `author` and its relation scalar `authorId` represent the side of the relation that stores the foreign key in the underlying database. + +This one-to-many relation expresses the following: + +- "a user can have zero or more posts" +- "a post must always have an author" + +In the previous example, the `author` relation field of the `Post` model references the `id` field of the `User` model. You can also reference a different field. In this case, you need to mark the field with the `@unique` attribute, to guarantee that there is only a single `User` connected to each `Post`. In the following example, the `author` field references an `email` field in the `User` model, which is marked with the `@unique` attribute: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique // <-- add unique attribute + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + authorEmail String + author User @relation(fields: [authorEmail], references: [email]) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique // <-- add unique attribute + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + authorEmail String + author User @relation(fields: [authorEmail], references: [email]) +} +``` + + + + + + +In MySQL, you can create a foreign key with only an index on the referenced side, and not a unique constraint. In Prisma versions 4.0.0 and later, if you introspect a relation of this type it will trigger a validation error. To fix this, you will need to add a `@unique` constraint to the referenced field. + + + +## Multi-field relations in relational databases + +In **relational databases only**, you can also define this relation using [multi-field IDs](/orm/reference/prisma-schema-reference#id-1)/composite key: + +```prisma +model User { + firstName String + lastName String + post Post[] + + @@id([firstName, lastName]) +} + +model Post { + id Int @id @default(autoincrement()) + author User @relation(fields: [authorFirstName, authorLastName], references: [firstName, lastName]) + authorFirstName String // relation scalar field (used in the `@relation` attribute above) + authorLastName String // relation scalar field (used in the `@relation` attribute above) +} +``` + +## 1-n relations in the database + +### Relational databases + +The following example demonstrates how to create a 1-n relation in SQL: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + "authorId" integer NOT NULL, + FOREIGN KEY ("authorId") REFERENCES "User"(id) +); +``` + +Since there's no `UNIQUE` constraint on the `authorId` column (the foreign key), you can create **multiple `Post` records that point to the same `User` record**. This makes the relation a one-to-many rather than a one-to-one. + +The following example demonstrates how to create a 1-n relation in SQL using a composite key (`firstName` and `lastName`): + +```sql +CREATE TABLE "User" ( + firstName TEXT, + lastName TEXT, + PRIMARY KEY ("firstName","lastName") +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + "authorFirstName" TEXT NOT NULL, + "authorLastName" TEXT NOT NULL, + FOREIGN KEY ("authorFirstName", "authorLastName") REFERENCES "User"("firstName", "lastName") +); +``` + +#### Comparing one-to-one and one-to-many relations + +In relational databases, the main difference between a 1-1 and a 1-n-relation is that in a 1-1-relation the foreign key must have a `UNIQUE` constraint defined on it. + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following MongoDB document represents a `User`: + +```json +{ "_id": { "$oid": "60d5922d00581b8f0062e3a8" }, "name": "Ella" } +``` + +Each of the following `Post` MongoDB documents has an `authorId` field which references the same user: + +```json +[ + { + "_id": { "$oid": "60d5922e00581b8f0062e3a9" }, + "title": "How to make sushi", + "authorId": { "$oid": "60d5922d00581b8f0062e3a8" } + }, + { + "_id": { "$oid": "60d5922e00581b8f0062e3aa" }, + "title": "How to re-install Windows", + "authorId": { "$oid": "60d5922d00581b8f0062e3a8" } + } +] +``` + +#### Comparing one-to-one and one-to-many relations + +In MongoDB, the only difference between a 1-1 and a 1-n is the number of documents referencing another document in the database - there are no constraints. + +## Required and optional relation fields in one-to-many relations + +A 1-n-relation always has two relation fields: + +- a [list](/orm/prisma-schema/data-model/models#type-modifiers) relation field which is _not_ annotated with `@relation` +- the [annotated relation field](/orm/prisma-schema/data-model/relations#annotated-relation-fields) (including its relation scalar) + +The annotated relation field and relation scalar of a 1-n relation can either _both_ be optional, or _both_ be mandatory. On the other side of the relation, the list is **always mandatory**. + +### Optional one-to-many relation + +In the following example, you can create a `Post` without assigning a `User`: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User? @relation(fields: [authorId], references: [id]) + authorId String? @db.ObjectId +} +``` + + + + +### Mandatory one-to-many relation + +In the following example, you must assign a `User` when you create a `Post`: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId +} +``` + + + diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/300-many-to-many-relations.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/300-many-to-many-relations.mdx new file mode 100644 index 0000000000..5c8761f826 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/300-many-to-many-relations.mdx @@ -0,0 +1,549 @@ +--- +title: Many-to-many relations +metaDescription: How to define and work with many-to-many relations in Prisma. +tocDepth: 3 +--- + + + +Many-to-many (m-n) relations refer to relations where zero or more records on one side of the relation can be connected to zero or more records on the other side. + +Prisma schema syntax and the implementation in the underlying database differs between [relational databases](#relational-databases) and [MongoDB](#mongodb). + + + +## Relational databases + +In relational databases, m-n-relations are typically modelled via [relation tables](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables). m-n-relations can be either [explicit](#explicit-many-to-many-relations) or [implicit](#implicit-many-to-many-relations) in the Prisma schema. We recommend using [implicit](#implicit-many-to-many-relations) m-n-relations if you do not need to store any additional meta-data in the relation table itself. You can always migrate to an [explicit](#explicit-many-to-many-relations) m-n-relation later if needed. + +### Explicit many-to-many relations + +In an explicit m-n relation, the **relation table is represented as a model in the Prisma schema** and can be used in queries. Explicit m-n relations define three models: + +- Two models with m-n relation, such as `Category` and `Post`. +- One model that represents the [relation table](#relation-tables), such as `CategoriesOnPosts` (also sometimes called _JOIN_, _link_ or _pivot_ table) in the underlying database. The fields of a relation table model are both annotated relation fields (`post` and `category`) with a corresponding relation scalar field (`postId` and `categoryId`). + +The relation table `CategoriesOnPosts` connects related `Post` and `Category` records. In this example, the model representing the relation table also **defines additional fields** that describe the `Post`/`Category` relationship - who assigned the category (`assignedBy`), and when the category was assigned (`assignedAt`): + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + categories CategoriesOnPosts[] +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts CategoriesOnPosts[] +} + +model CategoriesOnPosts { + post Post @relation(fields: [postId], references: [id]) + postId Int // relation scalar field (used in the `@relation` attribute above) + category Category @relation(fields: [categoryId], references: [id]) + categoryId Int // relation scalar field (used in the `@relation` attribute above) + assignedAt DateTime @default(now()) + assignedBy String + + @@id([postId, categoryId]) +} +``` + +The underlying SQL looks like this: + +```sql +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "Category" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Category_pkey" PRIMARY KEY ("id") +); + + +-- Relation table + indexes -- + +CREATE TABLE "CategoriesOnPosts" ( + "postId" INTEGER NOT NULL, + "categoryId" INTEGER NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CategoriesOnPosts_pkey" PRIMARY KEY ("postId","categoryId") +); + +ALTER TABLE "CategoriesOnPosts" ADD CONSTRAINT "CategoriesOnPosts_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "CategoriesOnPosts" ADD CONSTRAINT "CategoriesOnPosts_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +``` + +Note that the same rules as for [1-n relations](one-to-many-relations) apply (because `Post`↔ `CategoriesOnPosts` and `Category` ↔ `CategoriesOnPosts` are both in fact 1-n relations), which means one side of the relation needs to be annotated with the `@relation` attribute. + +When you don't need to attach additional information to the relation, you can model m-n-relations as [implicit m-n-relations](#implicit-many-to-many-relations). If you're not using Prisma Migrate but obtain your data model from [introspection](/orm/prisma-schema/introspection), you can still make use of implicit m-n-relations by following Prisma's [conventions for relation tables](#conventions-for-relation-tables-in-implicit-m-n-relations). + +#### Querying an explicit many-to-many + +The following section demonstrates how to query an explicit m-n-relation. You can query the relation model directly (`prisma.categoriesOnPosts(...)`), or use nested queries to go from `Post` -> `CategoriesOnPosts` -> `Category` or the other way. + +The following query does three things: + +1. Creates a `Post` +2. Creates a new record in the relation table `CategoriesOnPosts` +3. Creates a new `Category` that is associated with the newly created `Post` record + +```ts +const createCategory = await prisma.post.create({ + data: { + title: 'How to be Bob', + categories: { + create: [ + { + assignedBy: 'Bob', + assignedAt: new Date(), + category: { + create: { + name: 'New category', + }, + }, + }, + ], + }, + }, +}) +``` + +The following query: + +- Creates a new `Post` +- Creates a new record in the relation table `CategoriesOnPosts` +- Connects the category assignment to existing categories (with IDs `9` and `22`) + +```ts +const assignCategories = await prisma.post.create({ + data: { + title: 'How to be Bob', + categories: { + create: [ + { + assignedBy: 'Bob', + assignedAt: new Date(), + category: { + connect: { + id: 9, + }, + }, + }, + { + assignedBy: 'Bob', + assignedAt: new Date(), + category: { + connect: { + id: 22, + }, + }, + }, + ], + }, + }, +}) +``` + +Sometimes you might not know if a `Category` record exists. If the `Category` record exists, you want to connect a new `Post` record to that category. If the `Category` record does not exist, you want to create the record first and then connect it to the new `Post` record. The following query: + +1. Creates a new `Post` +2. Creates a new record in the relation table `CategoriesOnPosts` +3. Connects the category assignment to an existing category (with ID `9`), or creates a new category first if it does not exist + +```ts +const assignCategories = await prisma.post.create({ + data: { + title: 'How to be Bob', + categories: { + create: [ + { + assignedBy: 'Bob', + assignedAt: new Date(), + category: { + connectOrCreate: { + where: { + id: 9, + }, + create: { + name: 'New Category', + id: 9, + }, + }, + }, + }, + ], + }, + }, +}) +``` + +The following query returns all `Post` records where at least one (`some`) category assignment (`categories`) refers to a category named `"New category"`: + +```ts +const getPosts = await prisma.post.findMany({ + where: { + categories: { + some: { + category: { + name: 'New Category', + }, + }, + }, + }, +}) +``` + +The following query returns all categories where at least one (`some`) related `Post` record titles contain the words `"Cool stuff"` _and_ the category was assigned by Bob. + +```ts +const getAssignments = await prisma.category.findMany({ + where: { + posts: { + some: { + assignedBy: 'Bob', + post: { + title: { + contains: 'Cool stuff', + }, + }, + }, + }, + }, +}) +``` + +The following query gets all category assignments (`CategoriesOnPosts`) records that were assigned by `"Bob"` to one of 5 posts: + +```ts +const getAssignments = await prisma.categoriesOnPosts.findMany({ + where: { + assignedBy: 'Bob', + post: { + id: { + in: [9, 4, 10, 12, 22], + }, + }, + }, +}) +``` + +### Implicit many-to-many relations + +Implicit m-n relations define relation fields as lists on both sides of the relation. Although the relation table exists in the underlying database, **it is managed by Prisma and does not manifest in the Prisma schema**. Implicit relation tables follow a [specific convention](#conventions-for-relation-tables-in-implicit-m-n-relations). + +Implicit m-n-relations makes the [Prisma Client API](/orm/prisma-client) for m-n-relations a bit simpler (since you have one fewer level of nesting inside of [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes)). + +In the example below, there's one _implicit_ m-n-relation between `Post` and `Category`: + + + + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] +} +``` + + + + +```sql +CREATE TABLE "Category" ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + title TEXT NOT NULL +); +-- Relation table + indexes ------------------------------------------------------- +CREATE TABLE "_CategoryToPost" ( + "A" integer NOT NULL REFERENCES "Category"(id), + "B" integer NOT NULL REFERENCES "Post"(id) +); +CREATE UNIQUE INDEX "_CategoryToPost_AB_unique" ON "_CategoryToPost"("A" int4_ops,"B" int4_ops); +CREATE INDEX "_CategoryToPost_B_index" ON "_CategoryToPost"("B" int4_ops); +``` + + + + +#### Querying an implicit many-to-many + +The following section demonstrates how to query an [implicit m-n](#implicit-many-to-many-relations) relation. The queries require less nesting than [explicit m-n queries](#querying-an-explicit-many-to-many). + +The following query creates a single `Post` and multiple `Category` records: + +```ts +const createPostAndCategory = await prisma.post.create({ + data: { + title: 'How to become a butterfly', + categories: { + create: [{ name: 'Magic' }, { name: 'Butterflies' }], + }, + }, +}) +``` + +The following query creates a single `Category` and multiple `Post` records: + +```ts +const createCategoryAndPosts = await prisma.category.create({ + data: { + name: 'Stories', + posts: { + create: [ + { title: 'That one time with the stuff' }, + { title: 'The story of planet Earth' }, + ], + }, + }, +}) +``` + +The following query returns all `Post` records with a list of that post's assigned categories: + +```ts +const getPostsAndCategories = await prisma.post.findMany({ + include: { + categories: true, + }, +}) +``` + +#### Rules for defining an implicit m-n relation + +Implicit m-n relations: + +- Use a specific [convention for relation tables](#conventions-for-relation-tables-in-implicit-m-n-relations) +- Do **not** require the `@relation` attribute unless you need to [disambiguate relations](/orm/prisma-schema/data-model/relations#disambiguating-relations) with a name, e.g. `@relation("MyRelation")` or `@relation(name: "MyRelation")`. +- If you do use the `@relation` attribute, you cannot use the `references`, `fields`, `onUpdate` or `onDelete` arguments. This is because these take a fixed value for implicit m-n-relations and cannot be changed. +- Require both models to have a single `@id`. Be aware that: + + - You cannot use a [multi-field ID](/orm/reference/prisma-schema-reference#id-1) + - You cannot use a `@unique` in place of an `@id` + + + + To use either of these features, you must use an [explicit m-n instead](#explicit-many-to-many-relations). + + + +#### Conventions for relation tables in implicit m-n relations + +If you obtain your data model from [introspection](/orm/prisma-schema/introspection), you can still use implicit m-n-relations by following Prisma's [conventions for relation tables](#conventions-for-relation-tables-in-implicit-m-n-relations). The following example assumes you want to create a relation table to get an implicit m-n-relation for two models called `Post` and `Category`. + +##### Relation table + +If you want a relation table to be picked up by introspection as an implicit m-n-relation, the name must follow this exact structure: + +- It must start with an underscore `_` +- Then the name of the first model in alphabetical order (in this case `Category`) +- Then the relationship (in this case `To`) +- Then the name of the second model in alphabetical order (in this case `Post`) + +In the example, the correct table name is `_CategoryToPost`. + +When creating an implicit m-n-relation yourself in the Prisma schema file, you can [configure the relation](#configuring-the-name-of-the-relation-table-in-implicit-many-to-many-relations) to have a different name. This will change the name given to the relation table in the database. For example, for a relation named `"MyRelation"` the corresponding table will be called `_MyRelation`. + +###### Multi-schema + +If your implicit many-to-many relationship spans multiple database schemas (using the [`multiSchema` preview feature](/orm/prisma-schema/data-model/multi-schema)), the relation table (with the name defined directly above, in the example `_CategoryToPost`) must be present in the same database schema as the first model in alphabetical order (in this case `Category`). + +##### Columns + +A relation table for an implicit m-n-relation must have exactly two columns: + +- A foreign key column that points to `Category` called `A` +- A foreign key column that points to `Post` called `B` + +The columns must be called `A` and `B` where `A` points to the model that comes first in the alphabet and `B` points to the model which comes last in the alphabet. + +##### Indexes + +There further must be: + +- A unique index defined on both foreign key columns: + + ```sql + CREATE UNIQUE INDEX "_CategoryToPost_AB_unique" ON "_CategoryToPost"("A" int4_ops,"B" int4_ops); + ``` + +- A non-unique index defined on B: + + ```sql + CREATE INDEX "_CategoryToPost_B_index" ON "_CategoryToPost"("B" int4_ops); + ``` + +##### Example + +This is a sample SQL statement that would create the three tables including indexes (in PostgreSQL dialect) that are picked up as a implicit m-n-relation by Prisma Introspection: + +```sql +CREATE TABLE "_CategoryToPost" ( + "A" integer NOT NULL REFERENCES "Category"(id) , + "B" integer NOT NULL REFERENCES "Post"(id) +); +CREATE UNIQUE INDEX "_CategoryToPost_AB_unique" ON "_CategoryToPost"("A" int4_ops,"B" int4_ops); +CREATE INDEX "_CategoryToPost_B_index" ON "_CategoryToPost"("B" int4_ops); + +CREATE TABLE "Category" ( + id integer SERIAL PRIMARY KEY +); + +CREATE TABLE "Post" ( + id integer SERIAL PRIMARY KEY +); +``` + +And you can define multiple many-to-many relations between two tables by using the different relationship name. This example shows how the Prisma introspection works under such case: + +```sql +CREATE TABLE IF NOT EXISTS "User" ( + "id" SERIAL PRIMARY KEY +); +CREATE TABLE IF NOT EXISTS "Video" ( + "id" SERIAL PRIMARY KEY +); +CREATE TABLE IF NOT EXISTS "_UserLikedVideos" ( + "A" SERIAL NOT NULL, + "B" SERIAL NOT NULL, + CONSTRAINT "_UserLikedVideos_A_fkey" FOREIGN KEY ("A") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "_UserLikedVideos_B_fkey" FOREIGN KEY ("B") REFERENCES "Video" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); +CREATE TABLE IF NOT EXISTS "_UserDislikedVideos" ( + "A" SERIAL NOT NULL, + "B" SERIAL NOT NULL, + CONSTRAINT "_UserDislikedVideos_A_fkey" FOREIGN KEY ("A") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "_UserDislikedVideos_B_fkey" FOREIGN KEY ("B") REFERENCES "Video" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); +CREATE UNIQUE INDEX "_UserLikedVideos_AB_unique" ON "_UserLikedVideos"("A", "B"); +CREATE INDEX "_UserLikedVideos_B_index" ON "_UserLikedVideos"("B"); +CREATE UNIQUE INDEX "_UserDislikedVideos_AB_unique" ON "_UserDislikedVideos"("A", "B"); +CREATE INDEX "_UserDislikedVideos_B_index" ON "_UserDislikedVideos"("B"); +``` + +If you run `prisma db pull` on this database, the Prisma CLI will generate the following schema through introspection: + +```prisma +model User { + id Int @id @default(autoincrement()) + Video_UserDislikedVideos Video[] @relation("UserDislikedVideos") + Video_UserLikedVideos Video[] @relation("UserLikedVideos") +} + +model Video { + id Int @id @default(autoincrement()) + User_UserDislikedVideos User[] @relation("UserDislikedVideos") + User_UserLikedVideos User[] @relation("UserLikedVideos") +} +``` + +#### Configuring the name of the relation table in implicit many-to-many relations + +When using Prisma Migrate, you can configure the name of the relation table that's managed by Prisma using the `@relation` attribute. For example, if you want the relation table to be called `_MyRelationTable` instead of the default name `_CategoryToPost`, you can specify it as follows: + +```prisma +model Post { + id Int @id @default(autoincrement()) + categories Category[] @relation("MyRelationTable") +} + +model Category { + id Int @id @default(autoincrement()) + posts Post[] @relation("MyRelationTable") +} +``` + +### Relation tables + +A relation table (also sometimes called a _JOIN_, _link_ or _pivot_ table) connects two or more other tables and therefore creates a _relation_ between them. Creating relation tables is a common data modelling practice in SQL to represent relationships between different entities. In essence it means that "one m-n relation is modeled as two 1-n relations in the database". + +We recommend using [implicit](#implicit-many-to-many-relations) m-n-relations, where Prisma automatically generates the relation table in the underlying database. [Explicit](#explicit-many-to-many-relations) m-n-relations should be used when you need to store additional data in the relations, such as the date the relation was created. + +## MongoDB + +In MongoDB, m-n-relations are represented by: + +- relation fields on both sides, that each have a `@relation` attribute, with mandatory `fields` and `references` arguments +- a scalar list of referenced IDs on each side, with a type that matches the ID field on the other side + +The following example demonstrates a m-n-relation between posts and categories: + +```prisma +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + categoryIDs String[] @db.ObjectId + categories Category[] @relation(fields: [categoryIDs], references: [id]) +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + postIDs String[] @db.ObjectId + posts Post[] @relation(fields: [postIDs], references: [id]) +} +``` + +Prisma validates m-n-relations in MongoDB with the following rules: + +- The fields on both sides of the relation must have a list type (in the example above, `categories` have a type of `Category[]` and `posts` have a type of `Post[]`) +- The `@relation` attribute must define `fields` and `references` arguments on both sides +- The `fields` argument must have only one scalar field defined, which must be of a list type +- The `references` argument must have only one scalar field defined. This scalar field must exist on the referenced model and must be of the same type as the scalar field in the `fields` argument, but singular (no list) +- The scalar field to which `references` points must have the `@id` attribute +- No [referential actions](/orm/prisma-schema/data-model/relations/referential-actions) are allowed in `@relation` + +The implicit m-n-relations [used in relational databases](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) are not supported on MongoDB. + +### Querying MongoDB many-to-many relations + +This section demonstrates how to query m-n-relations in MongoDB, using the example schema above. + +The following query finds posts with specific matching category IDs: + +```ts +const newId1 = new ObjectId() +const newId2 = new ObjectId() + +const posts = await prisma.post.findMany({ + where: { + categoryIDs: { + hasSome: [newId1.toHexString(), newId2.toHexString()], + }, + }, +}) +``` + +The following query finds posts where the category name contains the string `'Servers'`: + +```ts +const posts = await prisma.post.findMany({ + where: { + categories: { + some: { + name: { + contains: 'Servers', + }, + }, + }, + }, +}) +``` diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/400-self-relations.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/400-self-relations.mdx new file mode 100644 index 0000000000..c1d39811e7 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/400-self-relations.mdx @@ -0,0 +1,434 @@ +--- +title: Self-relations +metaDescription: How to define and work with self-relations in Prisma. +--- + + + +A relation field can also reference its own model, in this case the relation is called a _self-relation_. Self-relations can be of any cardinality, 1-1, 1-n and m-n. + +Note that self-relations always require the `@relation` attribute. + + + +## One-to-one self-relations + +The following example models a one-to-one self-relation: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + successorId Int? @unique + successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id]) + predecessor User? @relation("BlogOwnerHistory") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + successorId String? @unique @db.ObjectId + successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id]) + predecessor User? @relation("BlogOwnerHistory") +} +``` + + + + +This relation expresses the following: + +- "a user can have one or zero predecessors" (for example, Sarah is Mary's predecessor as blog owner) +- "a user can have one or zero successors" (for example, Mary is Sarah's successor as blog owner) + +> **Note**: One-to-one self-relations cannot be made required on both sides. One or both sides must be optional, otherwise it becomes impossible to create the first `User` record. + +To create a one-to-one self-relation: + +- Both sides of the relation must define a `@relation` attribute that share the same name - in this case, **BlogOwnerHistory**. +- One relation field must be a [fully annotated](/orm/prisma-schema/data-model/relations#relation-fields). In this example, the `successor` field defines both the `field` and `references` arguments. +- One relation field must be backed by a foreign key. The `successor` field is backed by the `successorId` foreign key, which references a value in the `id` field. The `successorId` scalar relation field also requires a `@unique` attribute to guarantee a one-to-one relation. + +> **Note**: One-to-one self relations require two sides even if both sides are equal in the relationship. For example, to model a 'best friends' relation, you would need to create two relation fields: `bestfriend1` and a `bestfriend2`. + +Either side of the relation can be backed by a foreign key. In the previous example, repeated below, `successor` is backed by `successorId`: + + + + +```prisma highlight=4;normal +model User { + id Int @id @default(autoincrement()) + name String? + successorId Int? @unique + successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id]) + predecessor User? @relation("BlogOwnerHistory") +} +``` + + + + +```prisma highlight=4;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + successorId String? @unique @db.ObjectId + successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id]) + predecessor User? @relation("BlogOwnerHistory") +} +``` + + + + +Alternatively, you could rewrite this so that `predecessor` is backed by `predecessorId`: + + + + +```prisma highlight=5,6;normal +model User { + id Int @id @default(autoincrement()) + name String? + successor User? @relation("BlogOwnerHistory") + predecessorId Int? @unique + predecessor User? @relation("BlogOwnerHistory", fields: [predecessorId], references: [id]) +} +``` + + + + +```prisma highlight=5,6;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + successor User? @relation("BlogOwnerHistory") + predecessorId String? @unique @db.ObjectId + predecessor User? @relation("BlogOwnerHistory", fields: [predecessorId], references: [id]) +} +``` + + + + +No matter which side is backed by a foreign key, Prisma Client surfaces both the `predecessor` and `successor` fields: + +```ts line-number +const x = await prisma.user.create({ + data: { + name: "Bob McBob", +| successor: { + connect: { + id: 2, + }, + }, +| predecessor: { + connect: { + id: 4, + }, + }, + }, +}); +``` + +### One-to-one self relations in the database + +### Relational databases + +In **relational databases only**, a one-to-one self-relation is represented by the following SQL: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY, + "name" TEXT, + "successorId" INTEGER +); + +ALTER TABLE "User" ADD CONSTRAINT fk_successor_user FOREIGN KEY ("successorId") REFERENCES "User" (id); + +ALTER TABLE "User" ADD CONSTRAINT successor_unique UNIQUE ("successorId"); +``` + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following MongoDB documents represent a one-to-one self-relation between two users: + +```json +{ "_id": { "$oid": "60d97df70080618f000e3ca9" }, "name": "Elsa the Elder" } +``` + +```json +{ + "_id": { "$oid": "60d97df70080618f000e3caa" }, + "name": "Elsa", + "successorId": { "$oid": "60d97df70080618f000e3ca9" } +} +``` + +## One-to-many self relations + +A one-to-many self-relation looks as follows: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + teacherId Int? + teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id]) + students User[] @relation("TeacherStudents") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + teacherId String? @db.ObjectId + teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id]) + students User[] @relation("TeacherStudents") +} +``` + + + + +This relation expresses the following: + +- "a user has zero or one _teachers_ " +- "a user can have zero or more _students_" + +Note that you can also require each user to have a teacher by making the `teacher` field [required](/orm/prisma-schema/data-model/models#optional-and-mandatory-fields). + +### One-to-many self-relations in the database + +### Relational databases + +In relational databases, a one-to-many self-relation is represented by the following SQL: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY, + "name" TEXT, + "teacherId" INTEGER +); + +ALTER TABLE "User" ADD CONSTRAINT fk_teacherid_user FOREIGN KEY ("teacherId") REFERENCES "User" (id); +``` + +Notice the lack of `UNIQUE` constraint on `teacherId` - multiple students can have the same teacher. + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following MongoDB documents represent a one-to-many self-relation between three users - one teacher and two students with the same `teacherId`: + +```json +{ + "_id": { "$oid": "60d9b9e600fe3d470079d6f9" }, + "name": "Ms. Roberts" +} +``` + +```json +{ + "_id": { "$oid": "60d9b9e600fe3d470079d6fa" }, + "name": "Student 8", + "teacherId": { "$oid": "60d9b9e600fe3d470079d6f9" } +} +``` + +```json +{ + "_id": { "$oid": "60d9b9e600fe3d470079d6fb" }, + "name": "Student 9", + "teacherId": { "$oid": "60d9b9e600fe3d470079d6f9" } +} +``` + +## Many-to-many self relations + +A many-to-many self-relation looks as follows: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + followedBy User[] @relation("UserFollows") + following User[] @relation("UserFollows") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + followedBy User[] @relation("UserFollows", fields: [followedByIDs], references: [id]) + followedByIDs String[] @db.ObjectId + following User[] @relation("UserFollows", fields: [followingIDs], references: [id]) + followingIDs String[] @db.ObjectId +} +``` + + + + +This relation expresses the following: + +- "a user can be followed by zero or more users" +- "a user can follow zero or more users" + +Note that for relational databases, this many-to-many-relation is [implicit](many-to-many-relations#implicit-many-to-many-relations). This means Prisma maintains a [relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) for it in the underlying database. + +If you need the relation to hold other fields, you can create an [explicit](many-to-many-relations#explicit-many-to-many-relations) many-to-many self relation as well. The explicit version of the self relation shown previously is as follows: + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + followedBy Follows[] @relation("followedBy") + following Follows[] @relation("following") +} + +model Follows { + followedBy User @relation("followedBy", fields: [followedById], references: [id]) + followedById Int + following User @relation("following", fields: [followingId], references: [id]) + followingId Int + + @@id([followingId, followedById]) +} +``` + +### Many-to-many self-relations in the database + +### Relational databases + +In relational databases, a many-to-many self-relation (implicit) is represented by the following SQL: + +```sql +CREATE TABLE "User" ( + id integer DEFAULT nextval('"User_id_seq"'::regclass) PRIMARY KEY, + name text +); +CREATE TABLE "_UserFollows" ( + "A" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE, + "B" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE +); +``` + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following MongoDB documents represent a many-to-many self-relation between five users - two users that follow `"Bob"`, and two users that follow him: + +```json +{ + "_id": { "$oid": "60d9866f00a3e930009a6cdd" }, + "name": "Bob", + "followedByIDs": [ + { "$oid": "60d9866f00a3e930009a6cde" }, + { "$oid": "60d9867000a3e930009a6cdf" } + ], + "followingIDs": [ + { "$oid": "60d9867000a3e930009a6ce0" }, + { "$oid": "60d9867000a3e930009a6ce1" } + ] +} +``` + +```json +{ + "_id": { "$oid": "60d9866f00a3e930009a6cde" }, + "name": "Follower1", + "followingIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }] +} +``` + +```json +{ + "_id": { "$oid": "60d9867000a3e930009a6cdf" }, + "name": "Follower2", + "followingIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }] +} +``` + +```json +{ + "_id": { "$oid": "60d9867000a3e930009a6ce0" }, + "name": "CoolPerson1", + "followedByIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }] +} +``` + +```json +{ + "_id": { "$oid": "60d9867000a3e930009a6ce1" }, + "name": "CoolPerson2", + "followedByIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }] +} +``` + +## Defining multiple self-relations on the same model + +You can also define multiple self-relations on the same model at once. Taking all relations from the previous sections as example, you could define a `User` model as follows: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + teacherId Int? + teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id]) + students User[] @relation("TeacherStudents") + followedBy User[] @relation("UserFollows") + following User[] @relation("UserFollows") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + teacherId String? @db.ObjectId + teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id]) + students User[] @relation("TeacherStudents") + followedBy User[] @relation("UserFollows", fields: [followedByIDs]) + followedByIDs String[] @db.ObjectId + following User[] @relation("UserFollows", fields: [followingIDs]) + followingIDs String[] @db.ObjectId +} +``` + + + diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/100-special-rules-for-referential-actions.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/100-special-rules-for-referential-actions.mdx new file mode 100644 index 0000000000..64174bd2c9 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/100-special-rules-for-referential-actions.mdx @@ -0,0 +1,241 @@ +--- +title: 'Special rules for referential actions in SQL Server and MongoDB' +metaTitle: 'Special rules for referential actions in SQL Server and MongoDB' +metaDescription: 'Circular references or multiple cascade paths can cause validation errors on Microsoft SQL Server and MongoDB. Since the database does not handle these situations out of the box, learn how to solve this problem.' +tocDepth: 3 +--- + + + +Some databases have specific requirements that you should consider if you are using referential actions. + +- Microsoft SQL Server doesn't allow cascading referential actions on a foreign key, if the relation chain causes a cycle or multiple cascade paths. If the referential actions on the foreign key are set to something other than `NO ACTION` (or `NoAction` if Prisma is managing referential integrity), the server will check for cycles or multiple cascade paths and return an error when executing the SQL. + +- With MongoDB, using referential actions in Prisma requires that for any data model with self-referential relations or cycles between three models, you must set the referential action of `NoAction` to prevent the referential action emulations from looping infinitely. Be aware that by default, the `relationMode = "prisma"` mode is used for MongoDB, which means that Prisma manages [referential integrity](/orm/prisma-schema/data-model/relations/relation-mode). + +Given the SQL: + +```sql +CREATE TABLE [dbo].[Employee] ( + [id] INT NOT NULL IDENTITY(1,1), + [managerId] INT, + CONSTRAINT [PK__Employee__id] PRIMARY KEY ([id]) +); + +ALTER TABLE [dbo].[Employee] + ADD CONSTRAINT [FK__Employee__managerId] + FOREIGN KEY ([managerId]) REFERENCES [dbo].[Employee]([id]) + ON DELETE CASCADE ON UPDATE CASCADE; +``` + +When the SQL is run, the database would throw the following error: + +```terminal wrap +Introducing FOREIGN KEY constraint 'FK__Employee__managerId' on table 'Employee' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. +``` + +In more complicated data models, finding the cascade paths can get complex. Therefore in Prisma, the data model is validated _before_ generating any SQL to be run during any migrations, highlighting relations that are part of the paths. This makes it much easier to find and break these action chains. + + + +## Self-relation (SQL Server and MongoDB) + +The following model describes a self-relation where an `Employee` can have a manager and managees, referencing entries of the same model. + +```prisma +model Employee { + id Int @id @default(autoincrement()) + manager Employee? @relation(name: "management", fields: [managerId], references: [id]) + managees Employee[] @relation(name: "management") + managerId Int? +} +``` + +This will result in the following error: + +```terminal wrap +Error parsing attribute "@relation": A self-relation must have `onDelete` and `onUpdate` referential actions set to `NoAction` in one of the @relation attributes. (Implicit default `onDelete`: `SetNull`, and `onUpdate`: `Cascade`) +``` + +By not defining any actions, Prisma will use the following default values depending if the underlying [scalar fields](/orm/prisma-schema/data-model/models#scalar-fields) are set to be optional or required. + +| Clause | All of the scalar fields are optional | At least one scalar field is required | +| :--------- | :------------------------------------ | :------------------------------------ | +| `onDelete` | `SetNull` | `NoAction` | +| `onUpdate` | `Cascade` | `Cascade` | + +Since the default referential action for `onUpdate` in the above relation would be `Cascade` and for `onDelete` it would be `SetNull`, it creates a cycle and the solution is to explicitly set the `onUpdate` and `onDelete` values to `NoAction`. + +```prisma highlight=3;delete|4;add +model Employee { + id Int @id @default(autoincrement()) + manager Employee @relation(name: "management", fields: [managerId], references: [id]) + manager Employee @relation(name: "management", fields: [managerId], references: [id], onDelete: NoAction, onUpdate: NoAction) + managees Employee[] @relation(name: "management") + managerId Int +} +``` + +## Cyclic relation between three tables (SQL Server and MongoDB) + +The following models describe a cyclic relation between a `Chicken`, an `Egg` and a `Fox`, where each model references the other. + +```prisma +model Chicken { + id Int @id @default(autoincrement()) + egg Egg @relation(fields: [eggId], references: [id]) + eggId Int + predators Fox[] +} + +model Egg { + id Int @id @default(autoincrement()) + predator Fox @relation(fields: [predatorId], references: [id]) + predatorId Int + parents Chicken[] +} + +model Fox { + id Int @id @default(autoincrement()) + meal Chicken @relation(fields: [mealId], references: [id]) + mealId Int + foodStore Egg[] +} +``` + +This will result in three validation errors in every relation field that is part of the cycle. + +The first one is in the relation `egg` in the `Chicken` model: + +```terminal wrap +Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Chicken.egg → Egg.predator → Fox.meal. (Implicit default `onUpdate`: `Cascade`) +``` + +The second one is in the relation `predator` in the `Egg` model: + +```terminal wrap +Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Egg.predator → Fox.meal → Chicken.egg. (Implicit default `onUpdate`: `Cascade`) +``` + +And the third one is in the relation `meal` in the `Fox` model: + +```terminal wrap +Error parsing attribute "@relation": Reference causes a cycle. One of the @relation attributes in this cycle must have `onDelete` and `onUpdate` referential actions set to `NoAction`. Cycle path: Fox.meal → Chicken.egg → Egg.predator. (Implicit default `onUpdate`: `Cascade`) +``` + +As the relation fields are required, the default referential action for `onDelete` is `NoAction` but for `onUpdate` it is `Cascade`, which causes a referential action cycle. The solution is to set the `onUpdate` value to `NoAction` in any one of the relations. + +```prisma highlight=3;delete|4;add +model Chicken { + id Int @id @default(autoincrement()) + egg Egg @relation(fields: [eggId], references: [id]) + egg Egg @relation(fields: [eggId], references: [id], onUpdate: NoAction) + eggId Int + predators Fox[] +} +``` + +or + +```prisma highlight=3;delete|4;add +model Egg { + id Int @id @default(autoincrement()) + predator Fox @relation(fields: [predatorId], references: [id]) + predator Fox @relation(fields: [predatorId], references: [id], onUpdate: NoAction) + predatorId Int + parents Chicken[] +} +``` + +or + +```prisma highlight=3;delete|4;add +model Fox { + id Int @id @default(autoincrement()) + meal Chicken @relation(fields: [mealId], references: [id]) + meal Chicken @relation(fields: [mealId], references: [id], onUpdate: NoAction) + mealId Int + foodStore Egg[] +} +``` + +## Multiple cascade paths between two models (SQL Server only) + +The data model describes two different paths between same models, with both relations triggering cascading referential actions. + +```prisma +model User { + id Int @id @default(autoincrement()) + comments Comment[] + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + authorId Int + author User @relation(fields: [authorId], references: [id]) + comments Comment[] +} + +model Comment { + id Int @id @default(autoincrement()) + writtenById Int + postId Int + writtenBy User @relation(fields: [writtenById], references: [id]) + post Post @relation(fields: [postId], references: [id]) +} +``` + +The problem in this data model is how there are two paths from `Comment` to the `User`, and how the default `onUpdate` action in both relations is `Cascade`. This leads into two validation errors: + +The first one is in the relation `writtenBy`: + +```terminal wrap +Error parsing attribute "@relation": When any of the records in model `User` is updated or deleted, the referential actions on the relations cascade to model `Comment` through multiple paths. Please break one of these paths by setting the `onUpdate` and `onDelete` to `NoAction`. (Implicit default `onUpdate`: `Cascade`) +``` + +The second one is in the relation `post`: + +```terminal wrap +Error parsing attribute "@relation": When any of the records in model `User` is updated or deleted, the referential actions on the relations cascade to model `Comment` through multiple paths. Please break one of these paths by setting the `onUpdate` and `onDelete` to `NoAction`. (Implicit default `onUpdate`: `Cascade`) +``` + +The error means that by updating a primary key in a record in the `User` model, the update will cascade once between the `Comment` and `User` through the `writtenBy` relation, and again through the `Post` model from the `post` relation due to `Post` being related with the `Comment` model. + +The fix is to set the `onUpdate` referential action to `NoAction` in the `writtenBy` or `post` relation fields, or from the `Post` model by changing the actions in the `author` relation: + +```prisma highlight=5;delete|6;add +model Comment { + id Int @id @default(autoincrement()) + writtenById Int + postId Int + writtenBy User @relation(fields: [writtenById], references: [id]) + writtenBy User @relation(fields: [writtenById], references: [id], onUpdate: NoAction) + post Post @relation(fields: [postId], references: [id]) +} +``` + +or + +```prisma highlight=6;delete|7;add +model Comment { + id Int @id @default(autoincrement()) + writtenById Int + postId Int + writtenBy User @relation(fields: [writtenById], references: [id]) + post Post @relation(fields: [postId], references: [id]) + post Post @relation(fields: [postId], references: [id], onUpdate: NoAction) +} +``` + +or + +```prisma highlight=4;delete|5;add +model Post { + id Int @id @default(autoincrement()) + authorId Int + author User @relation(fields: [authorId], references: [id]) + author User @relation(fields: [authorId], references: [id], onUpdate: NoAction) + comments Comment[] +} +``` diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/index.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/index.mdx new file mode 100644 index 0000000000..d0ee27729a --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/410-referential-actions/index.mdx @@ -0,0 +1,508 @@ +--- +title: 'Referential actions' +metaTitle: 'Referential actions' +metaDescription: 'Referential actions let you define the update and delete behavior of related models on the database level' +tocDepth: 3 +--- + + + +Referential actions determine what happens to a record when your application deletes or updates a related record. + +From version 2.26.0, you can define referential actions on the relation fields in your Prisma schema. This allows you to define referential actions like cascading deletes and cascading updates at a Prisma level. + + + +**Version differences** + +- If you use version 3.0.1 or later, you can use referential actions as described on this page. +- If you use a version between 2.26.0 and 3.0.0, you can use referential actions as described on this page, but you must [enable the preview feature flag](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature) `referentialActions`. +- If you use version 2.25.0 or earlier, you can configure cascading deletes manually in your database. + + + +In the following example, adding `onDelete: Cascade` to the `author` field on the `Post` model means that deleting the `User` record will also delete all related `Post` records. + +```prisma file=schema.prisma highlight=4;normal +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +If you do not specify a referential action, Prisma [uses a default](#referential-action-defaults). + + + + + +If you upgrade from a version earlier than 2.26.0: +It is extremely important that you check the [upgrade paths for referential actions](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3/referential-actions) section. Prisma support of referential actions **removes the safety net in Prisma Client that prevents cascading deletes at runtime**. If you use the feature _without upgrading your database_, the [old default action](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3/referential-actions#prisma-2x-default-referential-actions) - `ON DELETE CASCADE` - becomes active. This might result in cascading deletes that you did not expect. + + + +## What are referential actions? + +Referential actions are policies that define how a referenced record is handled by the database when you run an [`update`](/orm/prisma-client/queries/crud#update) or [`delete`](/orm/prisma-client/queries/crud#delete) query. + +
+ +Referential actions on the database level + +Referential actions are features of foreign key constraints that exist to preserve referential integrity in your database. + +When you define relationships between data models in your Prisma schema, you use [relation fields](/orm/prisma-schema/data-model/relations#relation-fields), **which do not exist on the database**, and [scalar fields](/orm/prisma-schema/data-model/models#scalar-fields), **which do exist on the database**. These foreign keys connect the models on the database level. + +Referential integrity states that these foreign keys must reference an existing primary key value in the related database table. In your Prisma schema, this is generally represented by the `id` field on the related model. + +By default a database will reject any operation that violates the referential integrity, for example, by deleting referenced records. + +
+ +### How to use referential actions + +Referential actions are defined in the [`@relation`](/orm/reference/prisma-schema-reference#relation) attribute and map to the actions on the **foreign key constraint** in the underlying database. If you do not specify a referential action, [Prisma falls back to a default](#referential-action-defaults). + +The following model defines a one-to-many relation between `User` and `Post` and a many-to-many relation between `Post` and `Tag`, with explicitly defined referential actions: + +```prisma file=schema.prisma highlight=10,16-17;normal +model User { + id Int @id @default(autoincrement()) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + tags TagOnPosts[] + User User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade) + userId Int? +} + +model TagOnPosts { + id Int @id @default(autoincrement()) + post Post? @relation(fields: [postId], references: [id], onUpdate: Cascade, onDelete: Cascade) + tag Tag? @relation(fields: [tagId], references: [id], onUpdate: Cascade, onDelete: Cascade) + postId Int? + tagId Int? +} + +model Tag { + id Int @id @default(autoincrement()) + name String @unique + posts TagOnPosts[] +} +``` + +This model explicitly defines the following referential actions: + +- If you delete a `Tag`, the corresponding tag assignment is also deleted in `TagOnPosts`, using the `Cascade` referential action +- If you delete a `User`, the author is removed from all posts by setting the field value to `Null`, because of the `SetNull` referential action. To allow this, `User` and `userId` must be optional fields in `Post`. + +Prisma supports the following referential actions: + +- [`Cascade`](#cascade) +- [`Restrict`](#restrict) +- [`NoAction`](#noaction) +- [`SetNull`](#setnull) +- [`SetDefault`](#setdefault) + +### Referential action defaults + +If you do not specify a referential action, Prisma uses the following defaults: + +| Clause | Optional relations | Mandatory relations | +| :--------- | :----------------- | :------------------ | +| `onDelete` | `SetNull` | `Restrict` | +| `onUpdate` | `Cascade` | `Cascade` | + +For example, in the following schema all `Post` records must be connected to a `User` via the `author` relation: + +```prisma highlight=4;normal +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +The schema does not explicitly define referential actions on the mandatory `author` relation field, which means that the default referential actions of `Restrict` for `onDelete` and `Cascade` for `onUpdate` apply. + +## Caveats + +The following caveats apply: + +- Referential actions are **not** supported on [implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations). To use referential actions, you must define an explicit many-to-many relation and define your referential actions on the [join table](/orm/prisma-schema/data-model/relations/troubleshooting-relations#how-to-use-a-relation-table-with-a-many-to-many-relationship). +- Certain combinations of referential actions and required/optional relations are incompatible. For example, using `SetNull` on a required relation will lead to database errors when deleting referenced records because the non-nullable constraint would be violated. See [this GitHub issue](https://github.com/prisma/prisma/issues/7909) for more information. + +## Types of referential actions + +The following table shows which referential action each database supports. + +| Database | Cascade | Restrict | NoAction | SetNull | SetDefault | +| :---------- | :------ | :------- | :------- | :------ | :--------- | +| PostgreSQL | ✔️ | ✔️ | ✔️ | ✔️⌘ | ✔️ | +| MySQL | ✔️ | ✔️ | ✔️ | ✔️ | ❌ (✔️†) | +| SQLite | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| SQL Server | ✔️ | ❌‡ | ✔️ | ✔️ | ✔️ | +| CockroachDB | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| MongoDB†† | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | + +- † See [special cases for MySQL](#mysql). +- ⌘ See [special cases for PostgreSQL](#postgresql). +- ‡ See [special cases for SQL Server](#sql-server). +- †† Referential actions for MongoDB are available in Prisma versions 3.7.0 and later. + +### Special cases for referential actions + +Referential actions are part of the ANSI SQL standard. However, there are special cases where some relational databases diverge from the standard. + +#### MySQL + +MySQL, and the underlying InnoDB storage engine, does not support `SetDefault`. The exact behavior depends on the database version: + +- In MySQL versions 8 and later, and MariaDB versions 10.5 and later, `SetDefault` effectively acts as an alias for `NoAction`. You can define tables using the `SET DEFAULT` referential action, but a foreign key constraint error is triggered at runtime. +- In MySQL versions 5.6 and later, and MariaDB versions before 10.5, attempting to create a table definition with the `SET DEFAULT` referential action fails with a syntax error. + +For this reason, when you set `mysql` as the database provider, Prisma warns users to replace `SetDefault` referential actions in the Prisma schema with another action. + +#### PostgreSQL + +PostgreSQL is the only database supported by Prisma that allows you to define a `SetNull` referential action that refers to a non-nullable field. However, this raises a foreign key constraint error when the action is triggered at runtime. + +For this reason, when you set `postgres` as the database provider in the (default) `foreignKeys` relation mode, Prisma warns users to mark as optional any fields that are included in a `@relation` attribute with a `SetNull` referential action. For all other database providers, Prisma rejects the schema with a validation error. + +#### SQL Server + +[`Restrict`](#restrict) is not available for SQL Server databases, but you can use [`NoAction`](#noaction) instead. + +### `Cascade` + +- `onDelete: Cascade` Deleting a referenced record will trigger the deletion of referencing record. +- `onUpdate: Cascade` Updates the relation scalar fields if the referenced scalar fields of the dependent record are updated. + +#### Example usage + +```prisma file=schema.prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +##### Result of using `Cascade` + +If a `User` record is deleted, then their posts are deleted too. If the user's `id` is updated, then the corresponding `authorId` is also updated. + +##### How to use cascading deletes + +
+ +
+ +### `Restrict` + +- `onDelete: Restrict` Prevents the deletion if any referencing records exist. +- `onUpdate: Restrict` Prevents the identifier of a referenced record from being changed. + +#### Example usage + +```prisma file=schema.prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Restrict) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +##### Result of using `Restrict` + +`User`s with posts **cannot** be deleted. The `User`'s `id` **cannot** be changed. + + + +The `Restrict` action is **not** available on [Microsoft SQL Server](/orm/overview/databases/sql-server) and triggers a schema validation error. Instead, you can use [`NoAction`](#noaction), which produces the same result and is compatible with SQL Server. + + + +### `NoAction` + +The `NoAction` action is similar to `Restrict`, the difference between the two is dependent on the database being used: + +- **PostgreSQL**: `NoAction` allows the check (if a referenced row on the table exists) to be deferred until later in the transaction. See [the PostgreSQL docs](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-FK) for more information. +- **MySQL**: `NoAction` behaves exactly the same as `Restrict`. See [the MySQL docs](https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html#foreign-key-referential-actions) for more information. +- **SQLite**: When a related primary key is modified or deleted, no action is taken. See [the SQLite docs](https://www.sqlite.org/foreignkeys.html#fk_actions) for more information. +- **SQL Server**: When a referenced record is deleted or modified, an error is raised. See [the SQL Server docs](https://docs.microsoft.com/en-us/sql/relational-databases/tables/graph-edge-constraints?view=sql-server-ver15#on-delete-referential-actions-on-edge-constraints) for more information. +- **MongoDB** (in preview from version 3.6.0): When a record is modified or deleted, nothing is done to any related records. + + + +If you are [managing relations in Prisma Client](/orm/prisma-schema/data-model/relations/relation-mode#emulate-relations-in-prisma-with-the-prisma-relation-mode) rather than using foreign keys in the database, you should be aware that currently Prisma only implements the referential actions. Foreign keys also create constraints, which make it impossible to manipulate data in a way that would violate these constraints: instead of executing the query, the database responds with an error. These constraints will not be created if you emulate referential integrity in Prisma Client, so if you set the referential action to `NoAction` there will be no checks to prevent you from breaking the referential integrity. + + + +#### Example usage + +```prisma file=schema.prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: NoAction, onUpdate: NoAction) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +##### Result of using `NoAction` + +`User`'s with posts **cannot** be deleted. The `User`'s `id` **cannot** be changed. + +### `SetNull` + +- `onDelete: SetNull` The scalar field of the referencing object will be set to `NULL`. + +- `onUpdate: SetNull` When updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to `NULL`. + +`SetNull` will only work on optional relations. On required relations, a runtime error will be thrown since the scalar fields cannot be null. + +```prisma file=schema.prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: SetNull) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +##### Result of using `SetNull` + +When deleting a `User`, the `authorId` will be set to `NULL` for all its authored posts. + +When changing a `User`'s `id`, the `authorId` will be set to `NULL` for all its authored posts. + +### `SetDefault` + +- `onDelete: SetDefault` The scalar field of the referencing object will be set to the fields default value. + +- `onUpdate: SetDefault` The scalar field of the referencing object will be set to the fields default value. + +These require setting a default for the relation scalar field with [`@default`](/orm/reference/prisma-schema-reference#default). If no defaults are provided for any of the scalar fields, a runtime error will be thrown. + +```prisma file=schema.prisma highlight=4,5;add +model Post { + id Int @id @default(autoincrement()) + title String + authorUsername String? @default("anonymous") + author User? @relation(fields: [authorUsername], references: [username], onDelete: SetDefault, onUpdate: SetDefault) +} + +model User { + username String @id + posts Post[] +} +``` + +##### Result of using `SetDefault` + +When deleting a `User`, its existing posts' `authorUsername` field values will be set to 'anonymous'. + +When the `username` of a `User` changes, its existing posts' `authorUsername` field values will be set to 'anonymous'. + +### Database-specific requirements + +MongoDB and SQL Server have specific requirements for referential actions if you have [self-relations](/orm/prisma-schema/data-model/relations/referential-actions/special-rules-for-referential-actions#self-relation-sql-server-and-mongodb) or [cyclic relations](/orm/prisma-schema/data-model/relations/referential-actions/special-rules-for-referential-actions#cyclic-relation-between-three-tables-sql-server-and-mongodb) in your data model. SQL Server also has specific requirements if you have relations with [multiple cascade paths](/orm/prisma-schema/data-model/relations/referential-actions/special-rules-for-referential-actions#multiple-cascade-paths-between-two-models-sql-server-only). + +## Upgrade paths from versions 2.25.0 and earlier + +There are a couple of paths you can take when upgrading which will give different results depending on the desired outcome. + +If you currently use the migration workflow, you can run an introspection to check how the defaults are reflected in your schema. You can then manually update your database if you need to. + +You can also decide to skip checking the defaults and run a migration to update your database with the [new default values](#referential-action-defaults). + +The following assumes you have upgraded to 2.26.0 or newer and enabled the preview feature flag, or upgraded to 3.0.0 or newer: + +### Using Introspection + +If you [Introspect](/orm/prisma-schema/introspection) your database, the referential actions configured at the database level will be reflected in your Prisma Schema. If you have been using Prisma Migrate or `prisma db push` to manage the database schema, these are likely to be the [default values](#referential-action-defaults) from 2.25.0 and earlier. + +When you run an Introspection, Prisma compares all the foreign keys in the database with the schema, if the SQL statements `ON DELETE` and `ON UPDATE` do **not** match the default values, they will be explicitly set in the schema file. + +After introspecting, you can review the non-default clauses in your schema. The most important clause to review is `onDelete`, which defaults to `Cascade` in 2.25.0 and earlier. + + + +If you are using either the [`delete()`](/orm/prisma-client/queries/crud#delete-a-single-record) or [`deleteMany()`](/orm/prisma-client/queries/crud#delete-all-records) methods, **[cascading deletes](#how-to-use-cascading-deletes) will now be performed** as the `referentialActions` preview feature **removed the safety net in Prisma Client that previously prevented cascading deletes at runtime**. Be sure to check your code and make any adjustments accordingly. + + + +Make sure you are happy with every case of `onDelete: Cascade` in your schema. If not, either: + +- Modify your Prisma schema and `db push` or `dev migrate` to change the database + +_or_ + +- Manually update the underlying database if you use an introspection-only workflow + +The following example would result in a cascading delete, if the `User` is deleted then all of their `Post`'s will be deleted too. + +#### A blog schema example + +```prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +### Using Migration + +When running a [Migration](/orm/prisma-migrate) (or the [`prisma db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) command) the [new defaults](#referential-action-defaults) will be applied to your database. + + + +Unlike when you run an Introspect for the first time, the new referential actions clause and property, will **not** automatically be added to your prisma schema by the Prisma VSCode extension. +You will have to manually add them if you wish to use anything other than the new defaults. + + + +Explicitly defining referential actions in your Prisma schema is optional. If you do not explicitly define a referential action for a relation, Prisma uses the [new defaults](#referential-action-defaults). + +Note that referential actions can be added on a case by case basis. This means that you can add them to one single relation and leave the rest set to the defaults by not manually specifying anything. + +### Checking for errors + +**Before** upgrading to 2.26.0 and enabling the referential actions **preview feature**, Prisma prevented the deletion of records while using `delete()` or `deleteMany()` to preserve referential integrity. A custom runtime error would be thrown by Prisma Client with the error code `P2014`. + +**After** upgrading and enabling the referential actions **preview feature**, Prisma no longer performs runtime checks. You can instead specify a custom referential action to preserve the referential integrity between relations. + +When you use [`NoAction`](#noaction) or [`Restrict`](#restrict) to prevent the deletion of records, the error messages will be different post 2.26.0 compared to pre 2.26.0. This is because they are now triggered by the database and **not** Prisma Client. The new error code that can be expected is `P2003`. + +To make sure you catch these new errors you can adjust your code accordingly. + +#### Example of catching errors + +The following example uses the below blog schema with a one-to-many relationship between `Post` and `User` and sets a [`Restrict`](#restrict) referential actions on the `author` field. + +This means that if a user has a post, that user (and their posts) **cannot** be deleted. + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Restrict) + authorId String +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +Prior to upgrading and enabling the referential actions **preview feature**, the error code you would receive when trying to delete a user which has posts would be `P2014` and it's message: + +> "The change you are trying to make would violate the required relation '\{relation_name}' between the \{model_a_name\} and \{model_b_name\} models." + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + try { + await prisma.user.delete({ + where: { + id: 'some-long-id', + }, + }) + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2014') { + console.log(error.message) + } + } + } +} + +main() +``` + +To make sure you are checking for the correct errors in your code, modify your check to look for `P2003`, which will deliver the message: + +> "Foreign key constraint failed on the field: \{field_name\}" + +```ts highlight=14;delete|15;add +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + try { + await prisma.user.delete({ + where: { + id: 'some-long-id' + } + }) + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2014') { + if (error.code === 'P2003') { + console.log(error.message) + } + } + } +} + +main() +``` diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/420-relation-mode.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/420-relation-mode.mdx new file mode 100644 index 0000000000..6bebf42a7c --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/420-relation-mode.mdx @@ -0,0 +1,257 @@ +--- +title: 'Relation mode' +metaTitle: 'Manage relations between records with relation modes in Prisma' +metaDescription: 'Manage relations between records with relation modes in Prisma' +tocDepth: 3 +--- + + + +In Prisma, relations between records are defined with the [`@relation`](/orm/reference/prisma-schema-reference#relation) attribute. For example, in the following schema there is a one-to-many relation between the `User` and `Post` models: + +```prisma file=schema.prisma highlight=4,5,10;normal +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +Prisma has two _relation modes_, `foreignKeys` and `prisma`, that specify how relations between records are enforced. + +If you use Prisma with a relational database, then by default Prisma uses the [`foreignKeys` relation mode](#handle-relations-in-your-relational-database-with-the-foreignkeys-relation-mode), which enforces relations between records at the database level with foreign keys. A foreign key is a column or group of columns in one table that take values based on the primary key in another table. Foreign keys allow you to: + +- set constraints that prevent you from making changes that break references +- set [referential actions](/orm/prisma-schema/data-model/relations/referential-actions) that define how changes to records are handled + +Together these constraints and referential actions guarantee the _referential integrity_ of the data. + +For the example schema above, Prisma Migrate will generate the following SQL by default if you use the PostgreSQL connector: + +```sql highlight=19-22;normal + +-- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "authorId" INTEGER NOT NULL, + + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Post" + ADD CONSTRAINT "Post_authorId_fkey" + FOREIGN KEY ("authorId") + REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; +``` + +In this case, the foreign key constraint on the `authorId` column of the `Post` table references the `id` column of the `User` table, and guarantees that a post must have an author that exists. If you update or delete a user then the `ON DELETE` and `ON UPDATE` referential actions specify the `CASCADE` option, which will also delete or update all posts belonging to the user. + +Some databases, such as MongoDB or [PlanetScale](/orm/overview/databases/planetscale#differences-to-consider), do not support foreign keys. Additionally, in some cases developers may prefer not to use foreign keys in their relational database that usually does support foreign keys. For these situations, Prisma offers [the `prisma` relation mode](#emulate-relations-in-prisma-with-the-prisma-relation-mode), which emulates some properties of relations in relational databases. When you use Prisma Client with the `prisma` relation mode enabled, the behavior of queries is identical or similar, but referential actions and some constraints are handled by the Prisma engine rather than in the database. + + + There are performance implications to emulation of referential integrity and + referential actions in Prisma Client. In cases where the underlying database + supports foreign keys, it is usually the preferred choice. + + + + +## How to set the relation mode in your Prisma schema + +To set the relation mode, add the `relationMode` field in the `datasource` block: + +```prisma file=schema.prisma highlight=4,9;add +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" +} +``` + + + +The ability to set the relation mode was introduced as part of the `referentialIntegrity` preview feature in Prisma version 3.1.1, and is generally available in Prisma versions 4.8.0 and later.

The `relationMode` field was renamed in Prisma version 4.5.0, and was previously named `referentialIntegrity`. + +
+ +For relational databases, the available options are: + +- `foreignKeys`: this handles relations in the database with foreign keys. This is the default option for all relational database connectors and is active if no `relationMode` is explicitly set in the `datasource` block. +- `prisma`: this emulates relations in Prisma Client. You should also [enable this option](/orm/overview/databases/planetscale#how-to-emulate-relations-in-prisma-client) when you use the MySQL connector with a PlanetScale database. + +For MongoDB, the only available option is the `prisma` relation mode. This mode is also active if no `relationMode` is explicitly set in the `datasource` block. + + + +If you switch between relation modes, Prisma will add or remove foreign keys to your database next time you apply changes to your schema with Prisma Migrate or `db push`. See [Switch between relation modes](#switch-between-relation-modes) for more information. + + + +## Handle relations in your relational database with the `foreignKeys` relation mode + +The `foreignKeys` relation mode handles relations in your relational database with foreign keys. This is the default option when you use a relational database connector (PostgreSQL, MySQL, SQLite, SQL Server, CockroachDB). + +The `foreignKeys` relation mode is not available when you use the MongoDB connector. Some relational databases, [such as PlanetScale](/orm/overview/databases/planetscale#how-to-emulate-relations-in-prisma-client), also forbid the use of foreign keys. In these cases, you should instead [emulate relations in Prisma with the `prisma` relation mode](#emulate-relations-in-prisma-with-the-prisma-relation-mode). + +### Referential integrity + +The `foreignKeys` relation mode maintains referential integrity at the database level with foreign key constraints and referential actions. + +#### Foreign key constraints + +When you _create_ or _update_ a record with a relation to another record, the related record needs to exist. Foreign key constraints enforce this behavior in the database. If the record does not exist, the database will return an error message. + +#### Referential actions + +When you _update_ or _delete_ a record with a relation to another record, referential actions are triggered in the database. To maintain referential integrity in related records, referential actions prevent changes that would break referential integrity, cascade changes through to related records, or set the value of fields that reference the updated or deleted records to a `null` or default value. + +For more information, see the [referential actions](/orm/prisma-schema/data-model/relations/referential-actions) page. + +### Introspection + +When you introspect a relational database with the `db pull` command with the `foreignKeys` relation mode enabled, a `@relation` attribute will be added to your Prisma schema for relations where foreign keys exist. + +### Prisma Migrate and `db push` + +When you apply changes to your Prisma schema with Prisma Migrate or `db push` with the `foreignKeys` relation mode enabled, foreign keys will be created in your database for all `@relation` attributes in your schema. + +## Emulate relations in Prisma with the `prisma` relation mode + +The `prisma` relation mode emulates some foreign key constraints and referential actions for each Prisma Client query to maintain referential integrity, using some additional database queries and logic. + +The `prisma` relation mode is the default option for the MongoDB connector. It should also be set if you use a relational database that does not support foreign keys. For example, [if you use PlanetScale](/orm/overview/databases/planetscale#how-to-emulate-relations-in-prisma-client) you should use the `prisma` relation mode. + + + There are performance implications to emulation of referential integrity in + Prisma Client, because it uses additional database queries to maintain + referential integrity. In cases where the underlying database can handle + referential integrity with foreign keys, it is usually the preferred choice. + + +Emulation of relations is only available for Prisma Client queries and does not apply to raw queries. + +### Which foreign key constraints are emulated? + +When you _update_ a record, Prisma will emulate foreign key constraints. This means that when you update a record with a relation to another record, the related record needs to exist. If the record does not exist, Prisma Client will return an error message. + +However, when you _create_ a record, Prisma does not emulate any foreign key constraints. You will be able to create invalid data. + +### Which referential actions are emulated? + +When you _update_ or _delete_ a record with related records, Prisma will emulate referential actions. + +The following table shows which emulated referential actions are available for each database connector: + +| Database | Cascade | Restrict | NoAction | SetNull | SetDefault | +| :---------- | :------ | :------- | :------- | :------ | :--------- | +| PostgreSQL | **✔️** | **✔️** | **❌**‡ | **✔️** | **❌**† | +| MySQL | **✔️** | **✔️** | **✔️** | **✔️** | **❌**† | +| SQLite | **✔️** | **✔️** | **❌**‡ | **✔️** | **❌**† | +| SQL Server | **✔️** | **✔️** | **✔️** | **✔️** | **❌**† | +| CockroachDB | **✔️** | **✔️** | **✔️** | **✔️** | **❌**† | +| MongoDB | **✔️** | **✔️** | **✔️** | **✔️** | **❌**† | + +- † The `SetDefault` referential action is not supported in the `prisma` relation mode. +- ‡ The `NoAction` referential action is not supported in the `prisma` relation mode for PostgreSQL and SQLite. Instead, use the `Restrict` action. + +### Error messages + +Error messages returned by emulated constraints and referential actions in the `prisma` relation mode are generated by Prisma Client and differ slightly from the error messages in the `foreignKeys` relation mode: + +```jsx +Example: +// foreignKeys: +... Foreign key constraint failed on the field: `ProfileOneToOne_userId_fkey (index)` +// prisma: +... The change you are trying to make would violate the required relation 'ProfileOneToOneToUserOneToOne' between the `ProfileOneToOne` and `UserOneToOne` models. +``` + +### Introspection + +When you introspect a database with the `db pull` command with the `prisma` relation mode enabled, relations will not be automatically added to your schema. You will instead need to add any relations manually with the `@relation` attribute. This only needs to be done once – next time you introspect your database, Prisma will keep your added `@relation` attributes. + +### Prisma Migrate and `db push` + +When you apply changes to your Prisma schema with Prisma Migrate or `db push` with the `prisma` relation mode enabled, Prisma will not use foreign keys in your database. + +### Indexes + +In relational databases that use foreign key constraints, the database usually also implicitly creates an index for the foreign key columns. For example, [MySQL will create an index on all foreign key columns](https://dev.mysql.com/doc/refman/8.0/en/constraint-foreign-key.html#:~:text=MySQL%20requires%20that%20foreign%20key%20columns%20be%20indexed%3B%20if%20you%20create%20a%20table%20with%20a%20foreign%20key%20constraint%20but%20no%20index%20on%20a%20given%20column%2C%20an%20index%20is%20created.). This is to allow foreign key checks to run fast and not require a table scan. + +The `prisma` relation mode does not use foreign keys, so no indexes are created when you use Prisma Migrate or `db push` to apply changes to your database. You instead need to manually add an index on your relation scalar fields with the [`@@index`](/orm/reference/prisma-schema-reference#index) attribute (or the [`@unique`](/orm/reference/prisma-schema-reference#unique), [`@@unique`](/orm/reference/prisma-schema-reference#unique-1) or [`@@id`](/orm/reference/prisma-schema-reference#id-1) attributes, if applicable). + +#### Index validation + +If you do not add the index manually, queries might require full table scans. This can be slow, and also expensive on database providers that bill per accessed row. To help avoid this, Prisma warns you when your schema contains fields that are used in a `@relation` that does not have an index defined. For example, take the following schema with a relation between the `User` and `Post` models: + +```prisma file=schema.prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" +} + +model User { + id Int @id + posts Post[] +} + +model Post { + id Int @id + userId Int + user User @relation(fields: [userId], references: [id]) +} +``` + +Prisma displays the following warning when you run `prisma format` or `prisma validate`: + +```terminal wrap +With `relationMode = "prisma"`, no foreign keys are used, so relation fields will not benefit from the index usually created by the relational database under the hood. This can lead to poor performance when querying these fields. We recommend adding an index manually. +``` + +To fix this, add an index to your `Post` model: + +```prisma file=schema.prisma highlight=6;add +model Post { + id Int @id + userId Int + user User @relation(fields: [userId], references: [id]) + + @@index([userId]) +} +``` + +If you use the [Prisma VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) (or our [language server in another editor](/orm/more/development-environment/editor-setup)), the warning is augmented with a Quick Fix that adds the required index for you: + +![The Quick Fix pop-up for adding an index on a relation scalar field in VS Code](quick-fix-index.png) + +## Switch between relation modes + +It is only possible to switch between relation modes when you use a relational database connector (PostgreSQL, MySQL, SQLite, SQL Server, CockroachDB). + +### Switch from `foreignKeys` to `prisma` + +The default relation mode if you use a relational database and do not include the `relationMode` field in your `datasource` block is `foreignKeys`. To switch to the `prisma` relation mode, add the `relationMode` field with a value of `prisma`, or update the `relationMode` field value to `prisma` if it already exists. + +When you switch the relation mode from `foreignKeys` to `prisma`, after you first apply changes to your schema with Prisma Migrate or `db push` Prisma will remove all previously created foreign keys in the next migration. + +If you keep the same database, you can then continue to work as normal. If you switch to a database that does not support foreign keys at all, your existing migration history contains SQL DDL that creates foreign keys, which might trigger errors if you ever have to rerun these migrations. In this case, we recommend that you delete the `migrations` directory. (If you use PlanetScale, which does not support foreign keys, we generally recommend that you [use `db push` rather than Prisma Migrate](/orm/overview/databases/planetscale#differences-to-consider).) + +### Switch from `prisma` to `foreignKeys` + +To switch from the `prisma` relation mode to the `foreignKeys` relation mode, update the `relationMode` field value from `prisma` to `foreignKeys`. To do this, the database must support foreign keys. When you apply changes to your schema with Prisma Migrate or `db push` for the first time after you switch relation modes, Prisma will create foreign keys for all relations in the next migration. diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/500-troubleshooting-relations.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/500-troubleshooting-relations.mdx new file mode 100644 index 0000000000..1e6d09215a --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/500-troubleshooting-relations.mdx @@ -0,0 +1,271 @@ +--- +title: Troubleshooting relations +metaDescriptions: Common problems and solutions when defining relations in the Prisma schema. +--- + + + +Modelling your schema can sometimes offer up some unexpected results. This section aims to cover the most prominent of those. + + + +## Implicit many-to-many self-relations return incorrect data if order of relation fields change + +### Problem + +In the following implicit many-to-many self-relation, the lexicographic order of relation fields in `a_eats` (1) and `b_eatenBy` (2): + +```prisma highlight=4,5;normal +model Animal { + id Int @id @default(autoincrement()) + name String + a_eats Animal[] @relation(name: "FoodChain") + b_eatenBy Animal[] @relation(name: "FoodChain") +} +``` + +The resulting relation table in SQL looks as follows, where `A` represents prey (`a_eats`) and `B` represents predators (`b_eatenBy`): + +| A | B | +| :----------- | :--------- | +| 8 (Plankton) | 7 (Salmon) | +| 7 (Salmon) | 9 (Bear) | + +The following query returns a salmon's prey and predators: + + + + +```ts +const getAnimals = await prisma.animal.findMany({ + where: { + name: 'Salmon', + }, + include: { + b_eats: true, + a_eatenBy: true, + }, +}) +``` + + + + +```js no-copy +{ + "id": 7, + "name": "Salmon", + "b_eats": [ + { + "id": 8, + "name": "Plankton" + } + ], + "a_eatenBy": [ + { + "id": 9, + "name": "Bear" + } + ] +} +``` + + + + +Now change the order of the relation fields: + +```prisma highlight=4,5;normal +model Animal { + id Int @id @default(autoincrement()) + name String + b_eats Animal[] @relation(name: "FoodChain") + a_eatenBy Animal[] @relation(name: "FoodChain") +} +``` + +Migrate your changes and re-generate Prisma Client. When you run the same query with the updated field names, Prisma Client returns incorrect data (salmon now eats bears and gets eaten by plankton): + + + + +```ts +const getAnimals = await prisma.animal.findMany({ + where: { + name: 'Salmon', + }, + include: { + b_eats: true, + a_eatenBy: true, + }, +}) +``` + + + + +```js no-copy +{ + "id": 1, + "name": "Salmon", + "b_eats": [ + { + "id": 3, + "name": "Bear" + } + ], + "a_eatenBy": [ + { + "id": 2, + "name": "Plankton" + } + ] +} +``` + + + + +Although the lexicographic order of the relation fields in the Prisma schema changed, columns `A` and `B` in the database **did not change** (they were not renamed and data was not moved). Therefore, `A` now represents predators (`a_eatenBy`) and `B` represents prey (`b_eats`): + +| A | B | +| :----------- | :--------- | +| 8 (Plankton) | 7 (Salmon) | +| 7 (Salmon) | 9 (Bear) | + +### Solution + +If you rename relation fields in an implicit many-to-many self-relations, make sure that you maintain the alphabetic order of the fields - for example, by prefixing with `a_` and `_b`. + +## How to use a relation table with a many-to-many relationship + +There are a couple of ways to define a m-n relationship, implicitly or explicitly. Implicitly means letting Prisma handle the relation table (JOIN table) under the hood, all you have to do is define an array/list for the non scalar types on each model, see [implicit many-to-many relations](many-to-many-relations#implicit-many-to-many-relations). + +Where you might run into trouble is when creating an [explicit m-n relationship](many-to-many-relations#explicit-many-to-many-relations), that is, to create and handle the relation table yourself. **It can be overlooked that Prisma requires both sides of the relation to be present**. + +Take the following example, here a relation table is created to act as the JOIN between the `Post` and `Category` tables. This will not work however as the relation table (`PostCategories`) must form a 1-to-many relationship with the other two models respectively. + +The back relation fields are missing from the `Post` to `PostCategories` and `Category` to `PostCategories` models. + + +```prisma +// This example schema shows how NOT to define an explicit m-n relation + +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] // This should refer to PostCategories +} + +model PostCategories { + post Post @relation(fields: [postId], references: [id]) + postId Int + category Category @relation(fields: [categoryId], references: [id]) + categoryId Int + @@id([postId, categoryId]) +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] // This should refer to PostCategories +} +``` + + +To fix this the `Post` model needs to have a many relation field defined with the relation table `PostCategories`. The same applies to the `Category` model. + +This is because the relation model forms a 1-to-many relationship with the other two models its joining. + +```prisma highlight=5,21;add|4,20;delete +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] + postCategories PostCategories[] +} + +model PostCategories { + post Post @relation(fields: [postId], references: [id]) + postId Int + category Category @relation(fields: [categoryId], references: [id]) + categoryId Int + + @@id([postId, categoryId]) +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] + postCategories PostCategories[] +} +``` + +## Using the `@relation` attribute with a many-to-many relationship + +It might seem logical to add a `@relation("Post")` annotation to a relation field on your model when composing an implicit many-to-many relationship. + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] @relation("Category") + Category Category? @relation("Post", fields: [categoryId], references: [id]) + categoryId Int? +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] @relation("Post") + Post Post? @relation("Category", fields: [postId], references: [id]) + postId Int? +} +``` + +This however tells Prisma to expect **two** separate one-to-many relationships. See [disambiguating relations](/orm/prisma-schema/data-model/relations#disambiguating-relations) for more information on using the `@relation` attribute. + +The following example is the correct way to define an implicit many-to-many relationship. + +```prisma highlight=4,11;delete|5,12;add +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] @relation("Category") + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] @relation("Post") + posts Post[] +} +``` + +The `@relation` annotation can also be used to [name the underlying relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#configuring-the-name-of-the-relation-table-in-implicit-many-to-many-relations) created on a implicit many-to-many relationship. + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + categories Category[] @relation("CategoryPostRelation") +} + +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] @relation("CategoryPostRelation") +} +``` + +## Using m-n relations in databases with enforced primary keys + +### Problem + +Some cloud providers enforce the existence of primary keys in all tables. However, any relation tables (JOIN tables) created by Prisma (expressed via `@relation`) for many-to-many relations using implicit syntax do not have primary keys. + +### Solution + +You need to use [explicit relation syntax](/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations), manually create the join model, and verify that this join model has a primary key. diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/index.mdx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/index.mdx new file mode 100644 index 0000000000..7ade8e95f3 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/index.mdx @@ -0,0 +1,579 @@ +--- +title: 'Relations' +metaTitle: 'Relations (Reference)' +metaDescription: 'A relation is a connection between two models in the Prisma schema. This page explains how you can define one-to-one, one-to-many and many-to-many relations in Prisma.' +tocDepth: 3 +--- + + + +A relation is a _connection_ between two models in the Prisma schema. For example, there is a one-to-many relation between `User` and `Post` because one user can have many blog posts. + + + +The following Prisma schema defines a one-to-many relation between the `User` and `Post` models. The fields involved in defining the relation are highlighted: + + + + +```prisma highlight=3,8,9;normal +model User { + id Int @id @default(autoincrement()) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + author User @relation(fields: [authorId], references: [id]) + authorId Int // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +```prisma highlight=3,8,9;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +At a Prisma level, the `User` / `Post` relation is made up of: + +- Two [relation fields](#relation-fields): `author` and `posts`. Relation fields define connections between models at the Prisma level and **do not exist in the database**. These fields are used to generate Prisma Client. +- The scalar `authorId` field, which is referenced by the `@relation` attribute. This field **does exist in the database** - it is the foreign key that connects `Post` and `User`. + +At a Prisma level, a connection between two models is **always** represented by a [relation field](#relation-fields) on **each side** of the relation. + +
+ + + +
+ +## Relations in the database + +### Relational databases + +The following entity relationship diagram defines the same one-to-many relation between the `User` and `Post` tables in a **relational database**: + +![A one-to-many relationship between a user and posts table.](./one-to-many.png) + +In SQL, you use a _foreign key_ to create a relation between two tables. Foreign keys are stored on **one side** of the relation. Our example is made up of: + +- A foreign key column in the `Post` table named `authorId`. +- A primary key column in the `User` table named `id`. The `authorId` column in the `Post` table references the `id` column in the `User` table. + +In the Prisma schema, the foreign key / primary key relationship is represented by the `@relation` attribute on the `author` field: + +```prisma +author User @relation(fields: [authorId], references: [id]) +``` + +> **Note**: Relations in the Prisma schema represent relationships that exist between tables in the database. If the relationship does not exist in the database, it does not exist in the Prisma schema. + +### MongoDB + +For MongoDB, Prisma currently uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. + +The following document represents a `User` (in the `User` collection): + +```json +{ "_id": { "$oid": "60d5922d00581b8f0062e3a8" }, "name": "Ella" } +``` + +The following list of `Post` documents (in the `Post` collection) each have a `authorId` field which reference the same user: + +```json +[ + { + "_id": { "$oid": "60d5922e00581b8f0062e3a9" }, + "title": "How to make sushi", + "authorId": { "$oid": "60d5922d00581b8f0062e3a8" } + }, + { + "_id": { "$oid": "60d5922e00581b8f0062e3aa" }, + "title": "How to re-install Windows", + "authorId": { "$oid": "60d5922d00581b8f0062e3a8" } + } +] +``` + +This data structure represents a one-to-many relation because multiple `Post` documents refer to the same `User` document. + +#### `@db.ObjectId` on IDs and relation scalar fields + +If your model's ID is an `ObjectId` (represented by a `String` field), you must add `@db.ObjectId` to the model's ID _and_ the relation scalar field on the other side of the relation: + +```prisma highlight=3,9;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} +``` + +## Relations in Prisma Client + +Prisma Client is generated from the Prisma schema. The following examples demonstrate how relations manifest when you use Prisma Client to get, create, and update records. + +### Create a record and nested records + +The following query creates a `User` record and two connected `Post` records: + +```ts +const userAndPosts = await prisma.user.create({ + data: { + posts: { + create: [ + { title: 'Prisma Day 2020' }, // Populates authorId with user's id + { title: 'How to write a Prisma schema' }, // Populates authorId with user's id + ], + }, + }, +}) +``` + +In the underlying database, this query: + +1. Creates a `User` with an auto-generated `id` (for example, `20`) +2. Creates two new `Post` records and sets the `authorId` of both records to `20` + +### Retrieve a record and include related records + +The following query retrieves a `User` by `id` and includes any related `Post` records: + +```ts +const getAuthor = await prisma.user.findUnique({ + where: { + id: "20", + }, + include: { +| posts: true, // All posts where authorId == 20 + }, +}); +``` + +In the underlying database, this query: + +1. Retrieves the `User` record with an `id` of `20` +2. Retrieves all `Post` records with an `authorId` of `20` + +### Associate an existing record to another existing record + +The following query associates an existing `Post` record with an existing `User` record: + +```ts +const updateAuthor = await prisma.user.update({ + where: { + id: 20, + }, + data: { + posts: { + connect: { + id: 4, + }, + }, + }, +}) +``` + +In the underlying database, this query uses a [nested `connect` query](/orm/reference/prisma-client-reference#connect) to link the post with an `id` of 4 to the user with an `id` of 20. The query does this with the following steps: + +- The query first looks for the user with an `id` of `20`. +- The query then sets the `authorID` foreign key to `20`. This links the post with an `id` of `4` to the user with an `id` of `20`. + +In this query, the current value of `authorID` does not matter. The query changes `authorID` to `20`, no matter its current value. + +## Types of relations + +There are three different types (or [cardinalities]()) of relations in Prisma: + +- [One-to-one](one-to-one-relations) (also called 1-1 relations) +- [One-to-many](one-to-many-relations) (also called 1-n relations) +- [Many-to-many](many-to-many-relations) (also called m-n relations) + +The following Prisma schema includes every type of relation: + +- one-to-one: `User` ↔ `Profile` +- one-to-many: `User` ↔ `Post` +- many-to-many: `Post` ↔ `Category` + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + posts Post[] + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int @unique // relation scalar field (used in the `@relation` attribute above) +} + +model Post { + id Int @id @default(autoincrement()) + author User @relation(fields: [authorId], references: [id]) + authorId Int // relation scalar field (used in the `@relation` attribute above) + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above) + categories Category[] @relation(fields: [categoryIds], references: [id]) + categoryIds String[] @db.ObjectId +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] @relation(fields: [postIds], references: [id]) + postIds String[] @db.ObjectId +} +``` + + + + + + +This schema is the same as the [example data model](/orm/prisma-schema/data-model/models) but has all [scalar fields](/orm/prisma-schema/data-model/models#scalar-fields) removed (except for the required [relation scalars](/orm/prisma-schema/data-model/relations#relation-scalar-fields)) so you can focus on the [relation fields](#relation-fields). + + + + + +This example uses [implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations). These relations do not require the `@relation` attribute unless you need to [disambiguate relations](#disambiguating-relations). + + + +Notice that the syntax is slightly different between relational databases and MongoDB - particularly for [many-to-many relations](many-to-many-relations). + +For relational databases, the following entity relationship diagram represents the database that corresponds to the sample Prisma schema: + +![The sample schema as an entity relationship diagram](sample-schema.png) + +For MongoDB, Prisma uses a [normalized data model design](https://docs.mongodb.com/manual/core/data-model-design/), which means that documents reference each other by ID in a similar way to relational databases. See [the MongoDB section](#mongodb) for more details. + +### Implicit and explicit many-to-many relations + +Many-to-many relations in relational databases can be modelled in two ways: + +- [explicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations), where the relation table is represented as an explicit model in your Prisma schema +- [implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations), where Prisma manages the relation table and it does not appear in the Prisma schema. + +Implicit many-to-many relations require both models to have a single `@id`. Be aware of the following: + +- You cannot use a [multi-field ID](/orm/reference/prisma-schema-reference#id-1) +- You cannot use a `@unique` in place of an `@id` + +To use either of these features, you must set up an explicit many-to-many instead. + +The implicit many-to-many relation still manifests in a relation table in the underlying database. However, Prisma manages this relation table. + +If you use an implicit many-to-many relation instead of an explicit one, it makes the [Prisma Client API](/orm/prisma-client) simpler (because, for example, you have one fewer level of nesting inside of [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes)). + +If you're not using Prisma Migrate but obtain your data model from [introspection](/orm/prisma-schema/introspection), you can still make use of implicit many-to-many relations by following Prisma's [conventions for relation tables](many-to-many-relations#conventions-for-relation-tables-in-implicit-m-n-relations). + +## Relation fields + +Relation [fields](/orm/prisma-schema/data-model/models#defining-fields) are fields on a Prisma [model](/orm/prisma-schema/data-model/models#defining-models) that do _not_ have a [scalar type](/orm/prisma-schema/data-model/models#scalar-fields). Instead, their type is another model. + +Every relation must have exactly two relation fields, one on each model. In the case of one-to-one and one-to-many relations, an additional _relation scalar field_ is required which gets linked by one of the two relation fields in the `@relation` attribute. This relation scalar is the direct representation of the _foreign key_ in the underlying database. + + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + role Role @default(USER) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + role Role @default(USER) + posts Post[] +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above) +} +``` + + + + +These models have the following fields: + + + + +| Model | Field | Relational | Relation field | +| :----- | :--------- | :--------- | :--------------------------- | +| `User` | `id` | `Int` | No | +| | `email` | `String` | No | +| | `role` | `Role` | No | +| | `posts` | `Post[]` | **Yes** (Prisma-level) | +| `Post` | `id` | `Int` | No | +| | `title` | `String` | No | +| | `authorId` | `Int` | No (_relation scalar field_) | +| | `author` | `User` | **Yes** (_annotated_) | + + + + +| Model | Field | Relational | Relation field | Notes | +| :----- | :--------- | :--------- | :--------------------------- | -------------------------------------- | +| `User` | `id` | `String` | No | Underlying database type is `ObjectId` | +| | `email` | `String` | No | +| | `role` | `Role` | No | +| | `posts` | `Post[]` | **Yes** (Prisma-level) | +| `Post` | `id` | `String` | No | +| | `title` | `String` | No | +| | `authorId` | `String` | No (_relation scalar field_) | Underlying database type is `ObjectId` | +| | `author` | `User` | **Yes** (_annotated_) | + + + + +Both `posts` and `author` are relation fields because their types are not scalar types but other models. + +Also note that the annotated relation field `author` needs to link the relation scalar field `authorId` on the `Post` model inside the `@relation` attribute. The relation scalar represents the foreign key in the underlying database. + +The other relation field called `posts` is defined purely on a Prisma-level, it doesn't manifest in the database. + +### Annotated relation fields + +Relations that require one side of the relation to be _annotated_ with the `@relation` attribute are referred to as _annotated relation fields_. This includes: + +- one-to-one relations +- one-to-many relations +- many-to-many relations for MongoDB only + +The side of the relation which is annotated with the `@relation` attribute represents the side that **stores the foreign key in the underlying database**. The "actual" field that represents the foreign key is required on that side of the relation as well, it's called _relation scalar field_, and is referenced inside `@relation` attribute: + + + + +```prisma +author User @relation(fields: [authorId], references: [id]) +authorId Int +``` + + + + +```prisma +author User @relation(fields: [authorId], references: [id]) +authorId String @db.ObjectId +``` + + + + +A scalar field _becomes_ a relation scalar field when it's used in the `fields` of a `@relation` attribute. + +### Relation scalar fields + + + +Relation scalar fields are read-only in the generated [Prisma Client API](/orm/prisma-client). If you want to update a relation in your code, you can do so using [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes). + + + +#### Relation scalar naming conventions + +Because a relation scalar field always _belongs_ to a relation field, the following naming convention is common: + +- Relation field: `author` +- Relation scalar field: `authorId` (relation field name + `Id`) + +## The `@relation` attribute + +The [`@relation`](/orm/reference/prisma-schema-reference#relation) attribute can only be applied to the [relation fields](#relation-fields), not to [scalar fields](/orm/prisma-schema/data-model/models#scalar-fields). + +The `@relation` attribute is required when: + +- you define a one-to-one or one-to-many relation, it is required on _one side_ of the relation (with the corresponding relation scalar field) +- you need to disambiguate a relation (that's e.g. the case when you have two relations between the same models) +- you define a [self-relation](self-relations) +- you define [a many-to-many relation for MongoDB](many-to-many-relations#mongodb) +- you need to control how the relation table is represented in the underlying database (e.g. use a specific name for a relation table) + +> **Note**: [Implicit many-to-many relations](many-to-many-relations#implicit-many-to-many-relations) in relational databases do not require the `@relation` attribute. + +## Disambiguating relations + +When you define two relations between the same two models, you need to add the `name` argument in the `@relation` attribute to disambiguate them. As an example for why that's needed, consider the following models: + + + + +```prisma no-copy +// NOTE: This schema is intentionally incorrect. See below for a working solution. + +model User { + id Int @id @default(autoincrement()) + name String? + writtenPosts Post[] + pinnedPost Post? +} + +model Post { + id Int @id @default(autoincrement()) + title String? + author User @relation(fields: [authorId], references: [id]) + authorId Int + pinnedBy User? @relation(fields: [pinnedById], references: [id]) + pinnedById Int? +} +``` + + + + +```prisma no-copy +// NOTE: This schema is intentionally incorrect. See below for a working solution. + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + writtenPosts Post[] + pinnedPost Post? +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String? + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + pinnedBy User? @relation(fields: [pinnedById], references: [id]) + pinnedById String? @db.ObjectId +} +``` + + + + +In that case, the relations are ambiguous, there are four different ways to interpret them: + +- `User.writtenPosts` ↔ `Post.author` + `Post.authorId` +- `User.writtenPosts` ↔ `Post.pinnedBy` + `Post.pinnedById` +- `User.pinnedPost` ↔ `Post.author` + `Post.authorId` +- `User.pinnedPost` ↔ `Post.pinnedBy` + `Post.pinnedById` + +To disambiguate these relations, you need to annotate the relation fields with the `@relation` attribute and provide the `name` argument. You can set any `name` (except for the empty string `""`), but it must be the same on both sides of the relation: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + writtenPosts Post[] @relation("WrittenPosts") + pinnedPost Post? @relation("PinnedPost") +} + +model Post { + id Int @id @default(autoincrement()) + title String? + author User @relation("WrittenPosts", fields: [authorId], references: [id]) + authorId Int + pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id]) + pinnedById Int? @unique +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + writtenPosts Post[] @relation("WrittenPosts") + pinnedPost Post? @relation("PinnedPost") +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String? + author User @relation("WrittenPosts", fields: [authorId], references: [id]) + authorId String @db.ObjectId + pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id]) + pinnedById String? @unique @db.ObjectId +} +``` + + + diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/one-to-many.png b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/one-to-many.png new file mode 100644 index 0000000000..e0f44cd37a Binary files /dev/null and b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/one-to-many.png differ diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.png b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.png new file mode 100644 index 0000000000..c82ed1a7ac Binary files /dev/null and b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.png differ diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.snagx b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.snagx new file mode 100644 index 0000000000..c8ab225559 Binary files /dev/null and b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/quick-fix-index.snagx differ diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/relations-intro.png b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/relations-intro.png new file mode 100644 index 0000000000..47bb895580 Binary files /dev/null and b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/relations-intro.png differ diff --git a/docs/200-orm/100-prisma-schema/20-data-model/20-relations/sample-schema.png b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/sample-schema.png new file mode 100644 index 0000000000..c669b19721 Binary files /dev/null and b/docs/200-orm/100-prisma-schema/20-data-model/20-relations/sample-schema.png differ diff --git a/docs/200-orm/100-prisma-schema/20-data-model/30-indexes.mdx b/docs/200-orm/100-prisma-schema/20-data-model/30-indexes.mdx new file mode 100644 index 0000000000..6c27d1487f --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/30-indexes.mdx @@ -0,0 +1,534 @@ +--- +title: 'Indexes' +metaDescription: 'How to configure index functionality and add full text indexes' +hidePage: false +tocDepth: 3 +--- + + + +Prisma allows configuration of database indexes, unique constraints and primary key constraints. This is in General Availability in versions `4.0.0` and later. You can enable this with the `extendedIndexes` Preview feature in versions `3.5.0` and later. + +Version `3.6.0` also introduces support for introspection and migration of full text indexes in MySQL and MongoDB through a new `@@fulltext` attribute, available through the `fullTextIndex` Preview feature. + + + +If you are upgrading from a version earlier than 4.0.0, these changes to index configuration and full text indexes might be **breaking changes** if you have a database that already uses these features. See [Upgrading from previous versions](#upgrading-from-previous-versions) for more information on how to upgrade. + + + + + +## Index configuration + +You can configure indexes, unique constraints, and primary key constraints with the following attribute arguments: + +- The [`length` argument](#configuring-the-length-of-indexes-with-length-mysql) allows you to specify a maximum length for the subpart of the value to be indexed on `String` and `Bytes` types + + - Available on the `@id`, `@@id`, `@unique`, `@@unique` and `@@index` attributes + - MySQL only + +- The [`sort` argument](#configuring-the-index-sort-order-with-sort) allows you to specify the order that the entries of the constraint or index are stored in the database + + - Available on the `@unique`, `@@unique` and `@@index` attributes in all databases, and on the `@id` and `@@id` attributes in SQL Server + +- The [`type` argument](#configuring-the-access-type-of-indexes-with-type-postgresql) allows you to support index access methods other than PostgreSQL's default `BTree` access method + + - Available on the `@@index` attribute + - PostgreSQL only + - Supported index access methods: `Hash`, `Gist`, `Gin`, `SpGist` and `Brin` + +- The [`clustered` argument](#configuring-if-indexes-are-clustered-or-non-clustered-with-clustered-sql-server) allows you to configure whether a constraint or index is clustered or non-clustered + - Available on the `@id`, `@@id`, `@unique`, `@@unique` and `@@index` attributes + - SQL Server only + +See the linked sections for details of which version each feature was first introduced in. + +### Configuring the length of indexes with `length` (MySQL) + +The `length` argument is specific to MySQL and allows you to define indexes and constraints on columns of `String` and `Byte` types. For these types, MySQL requires you to specify a maximum length for the subpart of the value to be indexed in cases where the full value would exceed MySQL's limits for index sizes. See [the MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html) for more details. + +The `length` argument is available on the `@id`, `@@id`, `@unique`, `@@unique` and `@@index` attributes. It is generally available in versions 4.0.0 and later, and available as part of the `extendedIndexes` preview feature in versions 3.5.0 and later. + +As an example, the following data model declares an `id` field with a maximum length of 3000 characters: + +```prisma file=schema.prisma +model Id { + id String @id @db.VarChar(3000) +} +``` + +This is not valid in MySQL because it exceeds MySQL's index storage limit and therefore Prisma rejects the data model. The generated SQL would be rejected by the database. + +```sql +CREATE TABLE `Id` ( + `id` VARCHAR(3000) PRIMARY KEY +) +``` + +The `length` argument allows you to specify that only a subpart of the `id` value represents the primary key. In the example below, the first 100 characters are used: + +```prisma file=schema.prisma +model Id { + id String @id(length: 100) @db.VarChar(3000) +} +``` + +Prisma Migrate is able to create constraints and indexes with the `length` argument if specified in your data model. This means that you can create indexes and constraints on values of Prisma type `Byte` and `String`. If you don't specify the argument the index is treated as covering the full value as before. + +Introspection will fetch these limits where they are present in your existing database. This allows Prisma to support indexes and constraints that were previously suppressed and results in better support of existing MySQL databases that are making use of this feature. + +The `length` argument can also be used on compound primary keys, using the `@@id` attribute, as in the example below: + +```prisma file=schema.prisma +model CompoundId { + id_1 String @db.VarChar(3000) + id_2 String @db.VarChar(3000) + + @@id([id_1(length: 100), id_2(length: 10)]) +} +``` + +A similar syntax can be used for the `@@unique` and `@@index` attributes. + +### Configuring the index sort order with `sort` + +The `sort` argument is available for all databases supported by Prisma. It allows you to specify the order that the entries of the index or constraint are stored in the database. This can have an effect on whether the database is able to use an index for specific queries. + +The `sort` argument is available for all databases on `@unique`, `@@unique` and `@@index`. Additionally, SQL Server also allows it on `@id` and `@@id`. It is generally available in versions 4.0.0 and later, and available as part of the `extendedIndexes` preview feature in versions 3.5.0 and later. + +As an example, the following table + +```sql +CREATE TABLE `Unique` ( + `unique` INT, + CONSTRAINT `Unique_unique_key` UNIQUE (`unique` DESC) +) +``` + +is now introspected as + +```prisma file=schema.prisma +model Unique { + unique Int @unique(sort: Desc) +} +``` + +The `sort` argument can also be used on compound indexes: + +```prisma file=schema.prisma +model CompoundUnique { + unique_1 Int + unique_2 Int + + @@unique([unique_1(sort: Desc), unique_2]) +} +``` + +### Example: using `sort` and `length` together + +The following example demonstrates the use of the `sort` and `length` arguments to configure indexes and constraints for a `Post` model: + +```prisma file=schema.prisma +model Post { + title String @db.VarChar(300) + abstract String @db.VarChar(3000) + slug String @unique(sort: Desc, length: 42) @db.VarChar(3000) + author String + created_at DateTime + + @@id([title(length: 100, sort: Desc), abstract(length: 10)]) + @@index([author, created_at(sort: Desc)]) +} +``` + +### Configuring the access type of indexes with `type` (PostgreSQL) + +The `type` argument is available for configuring the index type in PostgreSQL with the `@@index` attribute. The index access methods available are `Hash`, `Gist`, `Gin`, `SpGist` and `Brin`, as well as the default `BTree` index access method. The `type` argument is generally available in versions 4.0.0 and later. The `Hash` index access method is available as part of the `extendedIndexes` preview feature in versions 3.6.0 and later, and the `Gist`, `Gin`, `SpGist` and `Brin` index access methods are available in preview in versions 3.14.0 and later. + +#### Hash + +The `Hash` type will store the index data in a format that is much faster to search and insert, and that will use less disk space. However, only the `=` and `<>` comparisons can use the index, so other comparison operators such as `<` and `>` will be much slower with `Hash` than when using the default `BTree` type. + +As an example, the following model adds an index with a `type` of `Hash` to the `value` field: + +```prisma file=schema.prisma +model Example { + id Int @id + value Int + + @@index([value], type: Hash) +} +``` + +This translates to the following SQL commands: + +```sql +CREATE TABLE "Example" ( + id INT PRIMARY KEY, + value INT NOT NULL +); + +CREATE INDEX "Example_value_idx" ON "Example" USING HASH (value); +``` + +#### Generalized Inverted Index (GIN) + +The GIN index stores composite values, such as arrays or `JsonB` data. This is useful for speeding up querying whether one object is part of another object. It is commonly used for full-text searches. + +An indexed field can define the operator class, which defines the operators handled by the index. + + + +Indexes using a function (such as `to_tsvector`) to determine the indexed value are not yet supported by Prisma. Indexes defined in this way will not be visible with `prisma db pull`. + + + +As an example, the following model adds a `Gin` index to the `value` field, with `JsonbPathOps` as the class of operators allowed to use the index: + +```prisma file=schema.prisma +model Example { + id Int @id + value Json + // ^ field type matching the operator class + // ^ operator class ^ index type + + @@index([value(ops: JsonbPathOps)], type: Gin) +} +``` + +This translates to the following SQL commands: + +```sql +CREATE TABLE "Example" ( + id INT PRIMARY KEY, + value JSONB NOT NULL +); + +CREATE INDEX "Example_value_idx" ON "Example" USING GIN (value jsonb_path_ops); +``` + +As part of the `JsonbPathOps` the `@>` operator is handled by the index, speeding up queries such as `value @> '{"foo": 2}'`. + +##### Supported Operator Classes for GIN + +Prisma generally supports operator classes provided by PostgreSQL in versions 10 and later. If the operator class requires the field type to be of a type Prisma does not yet support, using the `raw` function with a string input allows you to use these operator classes without validation. + +The default operator class (marked with ✅) can be omitted from the index definition. + +| Operator class | Allowed field type (native types) | Default | Other | +| -------------- | --------------------------------- | ------- | ----------------------------- | +| `ArrayOps` | Any array | ✅ | Also available in CockroachDB | +| `JsonbOps` | `Json` (`@db.JsonB`) | ✅ | Also available in CockroachDB | +| `JsonbPathOps` | `Json` (`@db.JsonB`) | | | +| `raw("other")` | | | | + +Read more about built-in operator classes in the [official PostgreSQL documentation](https://www.postgresql.org/docs/14/gin-builtin-opclasses.html). + +##### CockroachDB + +GIN and BTree are the only index types supported by CockroachDB. The operator classes marked to work with CockroachDB are the only ones allowed on that database and supported by Prisma. The operator class cannot be defined in the Prisma Schema Language: the `ops` argument is not necessary or allowed on CockroachDB. + +#### Generalized Search Tree (GiST) + +The GiST index type is used for implementing indexing schemes for user-defined types. By default there are not many direct uses for GiST indexes, but for example the B-Tree index type is built using a GiST index. + +As an example, the following model adds a `Gist` index to the `value` field with `InetOps` as the operators that will be using the index: + +```prisma file=schema.prisma +model Example { + id Int @id + value String @db.Inet + // ^ native type matching the operator class + // ^ index type + // ^ operator class + + @@index([value(ops: InetOps)], type: Gist) +} +``` + +This translates to the following SQL commands: + +```sql +CREATE TABLE "Example" ( + id INT PRIMARY KEY, + value INET NOT NULL +); + +CREATE INDEX "Example_value_idx" ON "Example" USING GIST (value inet_ops); +``` + +Queries comparing IP addresses, such as `value > '10.0.0.2'`, will use the index. + +##### Supported Operator Classes for GiST + +Prisma generally supports operator classes provided by PostgreSQL in versions 10 and later. If the operator class requires the field type to be of a type Prisma does not yet support, using the `raw` function with a string input allows you to use these operator classes without validation. + +| Operator class | Allowed field type (allowed native types) | +| -------------- | ----------------------------------------- | +| `InetOps` | `String` (`@db.Inet`) | +| `raw("other")` | | + +Read more about built-in operator classes in the [official PostgreSQL documentation](https://www.postgresql.org/docs/14/gist-builtin-opclasses.html). + +#### Space-Partitioned GiST (SP-GiST) + +The SP-GiST index is a good choice for many different non-balanced data structures. If the query matches the partitioning rule, it can be very fast. + +As with GiST, SP-GiST is important as a building block for user-defined types, allowing implementation of custom search operators directly with the database. + +As an example, the following model adds a `SpGist` index to the `value` field with `TextOps` as the operators using the index: + + +```prisma file=schema.prisma +model Example { + id Int @id + value String + // ^ field type matching the operator class + + @@index([value], type: SpGist) + // ^ index type + // ^ using the default ops: TextOps +} +``` + + +This translates to the following SQL commands: + +```sql +CREATE TABLE "Example" ( + id INT PRIMARY KEY, + value TEXT NOT NULL +); + +CREATE INDEX "Example_value_idx" ON "Example" USING SPGIST (value); +``` + +Queries such as `value LIKE 'something%'` will be sped up by the index. + +##### Supported Operator Classes for SP-GiST + +Prisma generally supports operator classes provided by PostgreSQL in versions 10 and later. If the operator class requires the field type to be of a type Prisma does not yet support, using the `raw` function with a string input allows you to use these operator classes without validation. + +The default operator class (marked with ✅) can be omitted from the index definition. + +| Operator class | Allowed field type (native types) | Default | Supported PostgreSQL versions | +| -------------- | ------------------------------------ | ------- | ----------------------------- | +| `InetOps` | `String` (`@db.Inet`) | ✅ | 10+ | +| `TextOps` | `String` (`@db.Text`, `@db.VarChar`) | ✅ | | +| `raw("other")` | | | | + +Read more about built-in operator classes from [official PostgreSQL documentation](https://www.postgresql.org/docs/14/spgist-builtin-opclasses.html). + +#### Block Range Index (BRIN) + +The BRIN index type is useful if you have lots of data that does not change after it is inserted, such as date and time values. If your data is a good fit for the index, it can store large datasets in a minimal space. + +As an example, the following model adds a `Brin` index to the `value` field with `Int4BloomOps` as the operators that will be using the index: + +```prisma file=schema.prisma +model Example { + id Int @id + value Int + // ^ field type matching the operator class + // ^ operator class ^ index type + + @@index([value(ops: Int4BloomOps)], type: Brin) +} +``` + +This translates to the following SQL commands: + +```sql +CREATE TABLE "Example" ( + id INT PRIMARY KEY, + value INT4 NOT NULL +); + +CREATE INDEX "Example_value_idx" ON "Example" USING BRIN (value int4_bloom_ops); +``` + +Queries like `value = 2` will now use the index, which uses a fraction of the space used by the `BTree` or `Hash` indexes. + +##### Supported Operator Classes for BRIN + +Prisma generally supports operator classes provided by PostgreSQL in versions 10 and later, and some supported operators are only available from PostgreSQL versions 14 and later. If the operator class requires the field type to be of a type Prisma does not yet support, using the `raw` function with a string input allows you to use these operator classes without validation. + +The default operator class (marked with ✅) can be omitted from the index definition. + +| Operator class | Allowed field type (native types) | Default | Supported PostgreSQL versions | +| --------------------------- | ------------------------------------ | ------- | ----------------------------- | +| `BitMinMaxOps` | `String` (`@db.Bit`) | ✅ | | +| `VarBitMinMaxOps` | `String` (`@db.VarBit`) | ✅ | | +| `BpcharBloomOps` | `String` (`@db.Char`) | | 14+ | +| `BpcharMinMaxOps` | `String` (`@db.Char`) | ✅ | | +| `ByteaBloomOps` | `Bytes` (`@db.Bytea`) | | 14+ | +| `ByteaMinMaxOps` | `Bytes` (`@db.Bytea`) | ✅ | | +| `DateBloomOps` | `DateTime` (`@db.Date`) | | 14+ | +| `DateMinMaxOps` | `DateTime` (`@db.Date`) | ✅ | | +| `DateMinMaxMultiOps` | `DateTime` (`@db.Date`) | | 14+ | +| `Float4BloomOps` | `Float` (`@db.Real`) | | 14+ | +| `Float4MinMaxOps` | `Float` (`@db.Real`) | ✅ | | +| `Float4MinMaxMultiOps` | `Float` (`@db.Real`) | | 14+ | +| `Float8BloomOps` | `Float` (`@db.DoublePrecision`) | | 14+ | +| `Float8MinMaxOps` | `Float` (`@db.DoublePrecision`) | ✅ | | +| `Float8MinMaxMultiOps` | `Float` (`@db.DoublePrecision`) | | 14+ | +| `InetInclusionOps` | `String` (`@db.Inet`) | ✅ | 14+ | +| `InetBloomOps` | `String` (`@db.Inet`) | | 14+ | +| `InetMinMaxOps` | `String` (`@db.Inet`) | | | +| `InetMinMaxMultiOps` | `String` (`@db.Inet`) | | 14+ | +| `Int2BloomOps` | `Int` (`@db.SmallInt`) | | 14+ | +| `Int2MinMaxOps` | `Int` (`@db.SmallInt`) | ✅ | | +| `Int2MinMaxMultiOps` | `Int` (`@db.SmallInt`) | | 14+ | +| `Int4BloomOps` | `Int` (`@db.Integer`) | | 14+ | +| `Int4MinMaxOps` | `Int` (`@db.Integer`) | ✅ | | +| `Int4MinMaxMultiOps` | `Int` (`@db.Integer`) | | 14+ | +| `Int8BloomOps` | `BigInt` (`@db.BigInt`) | | 14+ | +| `Int8MinMaxOps` | `BigInt` (`@db.BigInt`) | ✅ | | +| `Int8MinMaxMultiOps` | `BigInt` (`@db.BigInt`) | | 14+ | +| `NumericBloomOps` | `Decimal` (`@db.Decimal`) | | 14+ | +| `NumericMinMaxOps` | `Decimal` (`@db.Decimal`) | ✅ | | +| `NumericMinMaxMultiOps` | `Decimal` (`@db.Decimal`) | | 14+ | +| `OidBloomOps` | `Int` (`@db.Oid`) | | 14+ | +| `OidMinMaxOps` | `Int` (`@db.Oid`) | ✅ | | +| `OidMinMaxMultiOps` | `Int` (`@db.Oid`) | | 14+ | +| `TextBloomOps` | `String` (`@db.Text`, `@db.VarChar`) | | 14+ | +| `TextMinMaxOps` | `String` (`@db.Text`, `@db.VarChar`) | ✅ | | +| `TextMinMaxMultiOps` | `String` (`@db.Text`, `@db.VarChar`) | | 14+ | +| `TimestampBloomOps` | `DateTime` (`@db.Timestamp`) | | 14+ | +| `TimestampMinMaxOps` | `DateTime` (`@db.Timestamp`) | ✅ | | +| `TimestampMinMaxMultiOps` | `DateTime` (`@db.Timestamp`) | | 14+ | +| `TimestampTzBloomOps` | `DateTime` (`@db.Timestamptz`) | | 14+ | +| `TimestampTzMinMaxOps` | `DateTime` (`@db.Timestamptz`) | ✅ | | +| `TimestampTzMinMaxMultiOps` | `DateTime` (`@db.Timestamptz`) | | 14+ | +| `TimeBloomOps` | `DateTime` (`@db.Time`) | | 14+ | +| `TimeMinMaxOps` | `DateTime` (`@db.Time`) | ✅ | | +| `TimeMinMaxMultiOps` | `DateTime` (`@db.Time`) | | 14+ | +| `TimeTzBloomOps` | `DateTime` (`@db.Timetz`) | | 14+ | +| `TimeTzMinMaxOps` | `DateTime` (`@db.Timetz`) | ✅ | | +| `TimeTzMinMaxMultiOps` | `DateTime` (`@db.Timetz`) | | 14+ | +| `UuidBloomOps` | `String` (`@db.Uuid`) | | 14+ | +| `UuidMinMaxOps` | `String` (`@db.Uuid`) | ✅ | | +| `UuidMinMaxMultiOps` | `String` (`@db.Uuid`) | | 14+ | +| `raw("other")` | | | | + +Read more about built-in operator classes in the [official PostgreSQL documentation](https://www.postgresql.org/docs/14/brin-builtin-opclasses.html). + +### Configuring if indexes are clustered or non-clustered with `clustered` (SQL Server) + +The `clustered` argument is available to configure (non)clustered indexes in SQL Server. It can be used on the `@id`, `@@id`, `@unique`, `@@unique` and `@@index` attributes. It is generally available in versions 4.0.0 and later, and available as part of the `extendedIndexes` preview feature in versions 3.13.0 and later. + +As an example, the following model configures the `@id` to be non-clustered (instead of the clustered default): + +```prisma file=schema.prisma +model Example { + id Int @id(clustered: false) + value Int +} +``` + +This translates to the following SQL commands: + +```sql +CREATE TABLE [Example] ( + id INT NOT NULL, + value INT, + CONSTRAINT [Example_pkey] PRIMARY KEY NONCLUSTERED (id) +) +``` + +The default value of `clustered` for each attribute is as follows: + +| Attribute | Value | +| ---------- | ------- | +| `@id` | `true` | +| `@@id` | `true` | +| `@unique` | `false` | +| `@@unique` | `false` | +| `@@index` | `false` | + +A table can have at most one clustered index. + +### Upgrading from previous versions + + + +These index configuration changes can be **breaking changes** when activating the functionality for certain, existing Prisma schemas for existing databases. After enabling the preview features required to use them, run `prisma db pull` to introspect the existing database to update your Prisma schema before using Prisma Migrate again. + + + +A breaking change can occur in the following situations: + +- **Existing sort constraints and indexes:** earlier versions of Prisma will assume that the desired sort order is _ascending_ if no order is specified explicitly. This means that this is a breaking change if you have existing constraints or indexes that are using descending sort order and migrate your database without first specifying this in your data model. +- **Existing length constraints and indexes:** in earlier versions of Prisma, indexes and constraints that were length constrained in MySQL could not be represented in the Prisma schema. Therefore `prisma db pull` was not fetching these and you could not manually specify them. When you ran `prisma db push` or `prisma migrate dev` they were ignored if already present in your database. Since you are now able to specify these, migrate commands will now drop them if they are missing from your data model but present in the database. +- **Existing indexes other than `BTree` (PostgreSQL):** earlier versions of Prisma only supported the default `BTree` index type. Other supported indexes (`Hash`, `Gist`, `Gin`, `SpGist` and `Brin`) need to be added before migrating your database. +- **Existing (non-)clustered indexes (SQL Server):** earlier versions of Prisma did not support configuring an index as clustered or non-clustered. For indexes that do not use the default, these need to be added before migrating your database. + +In each of the cases above unwanted changes to your database can be prevented by properly specifying these properties in your data model where necessary. **The easiest way to do this is to use `prisma db pull` to retrieve any existing constraints or configuration.** Alternatively, you could also add these arguments manually. This should be done before using `prisma db push` or `prisma migrate dev` the first time after the upgrade. + +## Full text indexes (MySQL and MongoDB) + +The `fullTextIndex` preview feature provides support for introspection and migration of full text indexes in MySQL and MongoDB in version 3.6.0 and later. This can be configured using the `@@fulltext` attribute. Existing full text indexes in the database are added to your Prisma schema after introspecting with `db pull`, and new full text indexes added in the Prisma schema are created in the database when using Prisma Migrate. This also prevents validation errors in some database schemas that were not working before. + + + +For now we do not enable the full text search commands in Prisma Client for MongoDB; the progress can be followed in the [MongoDB](https://github.com/prisma/prisma/issues/9413) issue. + + + +### Enabling the `fullTextIndex` preview feature + +To enable the `fullTextIndex` preview feature, add the `fullTextIndex` feature flag to the `generator` block of the `schema.prisma` file: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextIndex"] +} +``` + +### Examples + +The following example demonstrates adding a `@@fulltext` index to the `title` and `content` fields of a `Post` model: + +```prisma file=schema.prisma +model Post { + id Int @id + title String @db.VarChar(255) + content String @db.Text + + @@fulltext([title, content]) +} +``` + +On MongoDB, you can use the `@@fulltext` index attribute (via the `fullTextIndex` preview feature) with the `sort` argument to add fields to your full-text index in ascending or descending order. The following example adds a `@@fulltext` index to the `title` and `content` fields of the `Post` model, and sorts the `title` field in descending order: + +```prisma file=schema.prisma +generator js { + provider = "prisma-client-js" + previewFeatures = ["fullTextIndex"] +} + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +model Post { + id String @id @map("_id") @db.ObjectId + title String + content String + + @@fulltext([title(sort: Desc), content]) +} +``` + +### Upgrading from previous versions + + + +This can be a **breaking change** when activating the functionality for certain, existing Prisma schemas for existing databases. After enabling the preview features required to use them, run `prisma db pull` to introspect the existing database to update your Prisma schema before using Prisma Migrate again. + + + +Earlier versions of Prisma converted full text indexes using the `@@index` attribute rather than the `@@fulltext` attribute. After enabling the `fullTextIndex` preview feature, run `prisma db pull` to convert these indexes to `@@fulltext` before migrating again with Prisma Migrate. If you do not do this, the existing indexes will be dropped instead and normal indexes will be created in their place. diff --git a/docs/200-orm/100-prisma-schema/20-data-model/40-views.mdx b/docs/200-orm/100-prisma-schema/20-data-model/40-views.mdx new file mode 100644 index 0000000000..7cd1fe2083 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/40-views.mdx @@ -0,0 +1,369 @@ +--- +title: 'Views' +metaTitle: 'How to include views in your Prisma schema' +metaDescription: 'How to include views in your Prisma schema' +hidePage: false +preview: true +tocDepth: 3 +--- + + + + + +Support for views is currently a very early [Preview](/orm/more/releases#preview) feature. You can add a view to your Prisma schema with the `view` keyword or introspect the views in your database schema with `db pull`. You cannot yet apply views in your schema to your database with Prisma Migrate and `db push` unless the changes are added manually to your migration file using the `--create-only` flag.

For updates on progress with this feature, follow [our GitHub issue](https://github.com/prisma/prisma/issues/17335). + +
+ +Database views allow you to name and store queries. In relational databases, views are [stored SQL queries](https://www.postgresql.org/docs/current/sql-createview.html) that might include columns in multiple tables, or calculated values such as aggregates. In MongoDB, views are queryable objects where the contents are defined by an [aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline) on other collections. + +The `views` preview feature allows you to represent views in your Prisma schema with the `view` keyword. To use views in Prisma, follow these steps: + +- [Enable the `views` preview feature](#enable-the-views-preview-feature) +- [Create a view in the underlying database](#create-a-view-in-the-underlying-database), either directly or as a [manual addition to a Prisma Migrate migration file](#use-views-with-prisma-migrate-and-db-push), or use an existing view +- [Represent the view in your Prisma schema](#add-views-to-your-prisma-schema) +- [Query the view in Prisma Client](#query-views-in-prisma-client) + +
+ +## Enable the `views` preview feature + +Support for views is currently in an early preview. To enable the `views` preview feature, add the `views` feature flag to the `previewFeatures` field of the `generator` block in your Prisma schema file: + +```prisma file=schema.prisma highlight=3;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["views"] +} +``` + +Please leave feedback about this preview feature in our dedicated [preview feature feedback issue for `views`](https://github.com/prisma/prisma/issues/17335). + +## Create a view in the underlying database + +Currently, you cannot apply views that you define in your Prisma schema to your database with Prisma Migrate and `db push`. Instead, you must first create the view in the underlying database, either manually or [as part of a migration](#use-views-with-prisma-migrate-and-db-push). + +For example, take the following Prisma schema with a `User` model and a related `Profile` model: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + user User @relation(fields: [userId], references: [id]) + userId Int @unique +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + profile Profile? +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + bio String + User User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId +} +``` + + + + +Next, take a `UserInfo` view in the underlying database that combines the `email` and `name` fields from the `User` model and the `bio` field from the `Profile` model. + +For a relational database, the SQL statement to create this view is: + +```sql +CREATE VIEW "UserInfo" AS + SELECT u.id, email, name, bio + FROM "User" u + LEFT JOIN "Profile" p ON u.id = p."userId"; +``` + +For MongoDB, you can [create a view](https://www.mongodb.com/docs/manual/core/views/join-collections-with-view/) with the following command: + +```ts +db.createView('UserInfo', 'User', [ + { + $lookup: { + from: 'Profile', + localField: '_id', + foreignField: 'userId', + as: 'ProfileData', + }, + }, + { + $project: { + _id: 1, + email: 1, + name: 1, + bio: '$ProfileData.bio', + }, + }, + { $unwind: '$bio' }, +]) +``` + +## Use views with Prisma Migrate and `db push` + +If you apply changes to your Prisma schema with Prisma Migrate or `db push`, Prisma does not create or run any SQL related to views. + +To include views in a migration, run `migrate dev --create-only` and then manually add the SQL for views to your migration file. Alternatively, you can create views manually in the database. + +## Add views to your Prisma schema + +To add a view to your Prisma schema, use the `view` keyword. + +You can represent the `UserInfo` view from the example above in your Prisma schema as follows: + + + + + + +```prisma +view UserInfo { + id Int @unique + email String + name String + bio String +} +``` + + + + +```prisma +view UserInfo { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + name String + bio String +} +``` + + + + + + +### Write by hand + +A `view` block is comprised of two main pieces: + +- The `view` block definition +- The view's field definitions + +These two pieces allow you to define the name of your view in the generated Prisma Client and the columns present in your view's query results. + +#### Define a `view` block + +To define the `UserInfo` view from the example above, begin by using the `view` keyword to define a `view` block in your schema named `UserInfo`: + + +```prisma +view UserInfo { + // Fields +} +``` + +#### Define fields + +The properties of a view are called _fields_, which consist of: + +- A field name +- A field type + +The fields of the `UserInfo` example view can be defined as follows: + + + + + +```prisma highlight=2-5;normal +view UserInfo { + id Int @unique + email String + name String + bio String +} +``` + + + + + +```prisma highlight=2-5;normal +view UserInfo { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String + name String + bio String +} +``` + + + + +Each _field_ of a `view` block represents a column in the query results of the view in the underlying database. + +### Use introspection + + + Currently only available for PostgreSQL, MySQL, SQL Server and CockroachDB. + + +If you have an existing view or views defined in your database, [introspection](/orm/prisma-schema/introspection) will automatically generate `view` blocks in your Prisma schema that represent those views. + +Assuming the example `UserInfo` view exists in your underlying database, running the following command will generate a `view` block in your Prisma schema representing that view: + +```terminal copy +npx prisma db pull +``` + +The resulting `view` block will be defined as follows: + + +```prisma +/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +view UserInfo { + id Int? + email String? + name String? + bio String? + + @@ignore +} +``` + +The `view` block is generated initially with a `@@ignore` attribute because [there is no unique identifier defined](#unique-identifier) (which is currently a [limitation](#unique-identifier) of the views preview feature). + + + +Please note for now `db pull` will only introspect views in your schema when using PostgreSQL, MySQL, SQL Server or CockroachDB. Support for this workflow will be extended to other database providers. + + + +#### Adding a unique identifier to an introspected view + +To be able to use the introspected view in Prisma Client, you will need to select and define one or multiple of the fields as the unique identifier. + +In the above view's case, the `id` column refers to a uniquely identifiable field in the underlying `User` table so that field can also be used as the uniquely identifiable field in the `view` block. + +In order to make this `view` block valid you will need to: + +- Remove the _optional_ flag `?` from the `id` field +- Add the `@unique` attribute to the `id` field +- Remove the `@@ignore` attribute +- Remove the comment Prisma generated warning about an invalid view + + +```prisma highlight=4;add|1,3,8,9;delete +/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +view UserInfo { + id Int? + id Int @unique + email String? + name String? + bio String? + + @@ignore +} +``` + +When re-introspecting your database, any custom changes to your view definitions will be preserved. + +#### The `views` directory + +Introspection of a database with one or more existing views will also create a new `views` directory within your `prisma` directory (starting with Prisma version 4.12.0). This directory will contain a subdirectory named after your database's schema which contains a `.sql` file for each view that was introspected in that schema. Each file will be named after an individual view and will contain the query the related view defines. + +For example, after introspecting a database with the default `public` schema using the model used above you will find a `prisma/views/public/UserInfo.sql` file was created with the following contents: + +```sql +SELECT + u.id, + u.email, + u.name, + p.bio +FROM + ( + "User" u + LEFT JOIN "Profile" p ON ((u.id = p."userId")) + ); +``` + +### Limitations + +#### Unique Identifier + +Currently, Prisma treats views in the same way as models. This means that a view needs to have at least one _unique identifier_, which can be represented by any of the following: + +- A unique constraint denoted with [`@unique`](/orm/prisma-schema/data-model/models#defining-a-unique-field) +- A composite unique constraint denoted with [`@@unique`](/orm/prisma-schema/data-model/models#defining-a-unique-field) +- An [`@id`](/orm/prisma-schema/data-model/models#defining-an-id-field) field +- A composite identifier denoted with [`@@id`](/orm/prisma-schema/data-model/models#composite-ids) + +In relational databases, a view's unique identifier can be defined as a `@unique` attribute on one field, or a `@@unique` attribute on multiple fields. When possible, it is preferable to use a `@unique` or `@@unique` constraint over an `@id` or `@@id` field. + +In MongoDB, however, the unique identifier must be an `@id` attribute that maps to the `_id` field in the underlying database with `@map("_id")`. + +In the example above, the `id` field has a `@unique` attribute. If another column in the underlying `User` table had been defined as uniquely identifiable and made available in the view's query results, that column could have been used as the unique identifier instead. + +#### Introspection + +Currently, introspection of views is only available for PostgreSQL, MySQL, SQL Server and CockroachDB. If you are using another database provider, your views must be added manually. + +This is a temporary limitation and support for introspection will be extended to the other supported datasource providers. + +## Query views in Prisma Client + +You can query views in Prisma Client in the same way that you query models. For example, the following query finds all users with a `name` of `'Alice'` in the `UserInfo` view defined above. + +```ts +const userinfo = await prisma.userInfo.findMany({ + where: { + name: 'Alice', + }, +}) +``` + +Currently, Prisma Client allows you to update a view if the underlying database allows it, without any additional validation. + +## Special types of views + +This section describes how to use Prisma with updatable and materialized views in your database. + +### Updatable views + +Some databases support updatable views (e.g. [PostgreSQL](https://www.postgresql.org/docs/current/sql-createview.html#SQL-CREATEVIEW-UPDATABLE-VIEWS), [MySQL](https://dev.mysql.com/doc/refman/8.0/en/view-updatability.html) and [SQL Server](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql?view=sql-server-ver16#updatable-views)). Updatable views allow you to create, update or delete entries. + +Currently Prisma treats all `view`s as updatable views. If the underlying database supports this functionality for the view, the operation should succeed. If the view is not marked as updatable, the database will return an error, and Prisma Client will then throw this error. + +In the future, Prisma Client might support marking individual views as updatable or not updatable. Please comment on our [`views` feedback issue](https://github.com/prisma/prisma/issues/17335) with your use case. + +### Materialized views + +Some databases support materialized views, e.g. [PostgreSQL](https://www.postgresql.org/docs/current/rules-materializedviews.html), [CockroachDB](https://www.cockroachlabs.com/docs/stable/views.html#materialized-views), [MongoDB](https://www.mongodb.com/docs/manual/core/materialized-views/), and [SQL Server](https://learn.microsoft.com/en-us/sql/relational-databases/views/create-indexed-views?view=sql-server-ver16) (where they're called "indexed views"). + +Materialized views persist the result of the view query for faster access and only update it on demand. + +Currently Prisma has no understanding of materialized views, but when you [manually create a view](#create-a-view-in-the-underlying-database) you can also create a materialized view by using the corresponding command in the underlying database. You can then use Prisma's [raw query functionality](/orm/prisma-client/queries/raw-database-access) to execute the command to refresh the view manually. + +In the future Prisma Client might support marking individual views as materialized and add a Prisma Client method to refresh the materialized view. Please comment on our [`views` feedback issue](https://github.com/prisma/prisma/issues/17335) with your use case. diff --git a/docs/200-orm/100-prisma-schema/20-data-model/50-database-mapping.mdx b/docs/200-orm/100-prisma-schema/20-data-model/50-database-mapping.mdx new file mode 100644 index 0000000000..a7f5471434 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/50-database-mapping.mdx @@ -0,0 +1,226 @@ +--- +title: 'Database mapping' +metaTitle: 'Database mapping' +metaDescription: '' +tocDepth: 3 +--- + + + +The [Prisma schema](/orm/prisma-schema) includes mechanisms that allow you to define names of certain database objects. You can: + +- [Map model and field names to different collection/table and field/column names](#mapping-collectiontable-and-fieldcolumn-names) +- [Define constraint and index names](#constraint-and-index-names) + + + +## Mapping collection/table and field/column names + +Sometimes the names used to describe entities in your database might not match the names you would prefer in your generated API. Mapping names in the Prisma schema allows you to influence the naming in your Client API without having to change the underlying database names. + +A common approach for naming tables/collections in databases for example is to use plural form and [snake_case](https://en.wikipedia.org/wiki/Snake_case) notation. Prisma on the other hand has recommended model [naming conventions (singular form, PascalCase)](/orm/reference/prisma-schema-reference#naming-conventions) which differ from that. + +`@map` and `@@map` allow you to [tune the shape of your Prisma Client API](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) by decoupling model and field names from table and column names in the underlying database. + +### Map collection / table names + +As an example, when you [introspect](/orm/prisma-schema/introspection) a database with a table named `comments`, the resulting Prisma model will look like this: + +```prisma +model comments { + // Fields +} +``` + +However, you can still choose `Comment` as the name of the model (e.g. to follow the naming convention) without renaming the underlying `comments` table in the database by using the [`@@map`](/orm/reference/prisma-schema-reference#map-1) attribute: + +```prisma highlight=4;normal +model Comment { + // Fields + + @@map("comments") +} +``` + +With this modified model definition, Prisma automatically maps the `Comment` model to the `comments` table in the underlying database. + +### Map field / column names + +You can also [`@map`](/orm/reference/prisma-schema-reference#map) a column/field name: + +```prisma highlight=2-4;normal +model Comment { + content String @map("comment_text") + email String @map("commenter_email") + type Enum @map("comment_type") + + @@map("comments") +} +``` + +This way the `comment_text` column is not available under `prisma.comment.comment_text` in the Prisma Client API, but can be accessed via `prisma.comment.content`. + +### Map enum names and values + +You can also `@map` an enum value, or `@@map` an enum: + +```prisma highlight=3,5;normal +enum Type { + Blog, + Twitter @map("comment_twitter") + + @@map("comment_source_enum") +} +``` + +## Constraint and index names + +In [2.29.0](https://github.com/prisma/prisma/releases/tag/2.29.0) and later, you can optionally use the `map` argument to define the **underlying constraint and index names** in the Prisma schema for the attributes [`@id`](/orm/reference/prisma-schema-reference#id), [`@@id`](/orm/reference/prisma-schema-reference#id-1), [`@unique`](/orm/reference/prisma-schema-reference#unique), [`@@unique`](/orm/reference/prisma-schema-reference#unique-1), [`@@index`](/orm/reference/prisma-schema-reference#index) and [`@relation`](/orm/reference/prisma-schema-reference#relation). + +When introspecting a database, the `map` argument will _only_ be rendered in the schema if the name differs from Prisma's [default constraint naming convention for indexes and constraints](#prismas-default-naming-conventions-for-indexes-and-constraints). + + + +If you use Prisma Migrate in a version earlier than 2.29.0 and want to maintain your existing constraint and index names after upgrading to a newer version, **do not** immediately run `prisma migrate` or `prisma db push`. This will **change any underlying constraint name that does not follow Prisma's convention**. Follow the [upgrade path that allows you to maintain existing constraint and index names](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3/named-constraints#option-1-i-want-to-maintain-my-existing-constraint-and-index-names). + + + +### Use cases for named constraints + +Some use cases for explicitly named constraints include: + +- Company policy +- Conventions of other tools + +#### Prisma's default naming conventions for indexes and constraints + +Prisma naming convention was chosen to align with PostgreSQL since it is deterministic. It also helps to maximize the amount of times where names do not need to be rendered because many databases out there they already align with the convention. + +We always use the database names of entities when generating the default names. So if a model is remapped to a different name in the data model, the default name generation will still take the name of the table in the database as input. The same is true for fields and columns. + +| Entity | Convention | Example | +| ----------------- | --------------------------------- | ------------------------------ | +| Primary Key | \{tablename}\_pkey | `User_pkey` | +| Unique Constraint | \{tablename}\_\{column_names}\_key | `User_firstName_last_Name_key` | +| Non-Unique Index | \{tablename}\_\{column_names}\_idx | `User_age_idx` | +| Foreign Key | \{tablename}\_\{column_names}\_fkey | `User_childName_fkey` | + +Since most databases have a length limit for entity names, the names will be trimmed if necessary to not violate the database limits. We will shorten the part before the `_suffix` as necessary so that the full name is at most the maximum length permitted. + +### Using default constraint names + +When no explicit names are provided via `map` arguments Prisma will assume they follow the default naming convention. + +If you introspect a database the names for indexes and constraints will be added to your schema unless they follow Prisma's naming convention. If they do, the names are not rendered to keep the schema more readable. When you migrate such a schema Prisma will infer the default names and persist them in the database. + +#### Example + +The following schema defines three constraints (`@id`, `@unique`, and `@relation`) and one index (`@@index`) that will + +```prisma highlight=2,8,11,13;normal +model User { + id Int @id @default(autoincrement()) + name String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name]) + + @@index([title, authorName]) +} +``` + +Since no explicit names are provided via `map` arguments Prisma will assume they follow our default naming convention. + +### Prisma's default naming conventions for indexes and constraints + +If you introspect a database the names for indexes and constraints will be added to your schema unless they follow Prisma's naming convention. If they do, the names are not rendered to keep the schema more readable. When you migrate such a schema Prisma will infer the default names and persist them in the database. + +We chose our naming convention to align with PostgreSQL since it is deterministic and helps us maximize the amount of times where we do not need +to render names because they already align with the convention. + +| Constraint or index | Follows convention | Underlying constraint or index names | +| ---------------------------------- | ------------------ | ------------------------------------ | +| `@id` (on `User` > `id` field) | Yes | `User_pk` | +| `@@index` (on `Post`) | Yes | `Post_title_authorName_idx` | +| `@id` (on `Post` > `id` field) | Yes | `Post_pk` | +| `@relation` (on `Post` > `author`) | Yes | `Post_authorName_fkey` | + +### Using custom constraint / index names + +You can use the `map` argument to define **custom constraint and index names** in the underlying database. + +#### Example + +The following example adds custom names to one `@id` and the `@@index`: + +```prisma highlight=2,13;normal +model User { + id Int @id(map: "Custom_Primary_Key_Constraint_Name") @default(autoincrement()) + name String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name]) + + @@index([title, authorName], map: "My_Custom_Index_Name") +} +``` + +The following table lists the name of each constraint and index in the underlying database: + +| Constraint or index | Follows convention | Underlying constraint or index names | +| ---------------------------------- | ------------------ | ------------------------------------ | +| `@id` (on `User` > `id` field) | No | `Custom_Primary_Key_Constraint_Name` | +| `@@index` (on `Post`) | No | `My_Custom_Index_Name` | +| `@id` (on `Post` > `id` field) | Yes | `Post_pk` | +| `@relation` (on `Post` > `author`) | Yes | `Post_authorName_fkey` | + +### Related: Naming indexes and primary keys for Prisma Client + +Additionally to `map`, the `@@id` and `@@unique` attributes take an optional `name` argument that allows you to customize your Prisma Client API. + +On a model like: + +```prisma +model User { + firstName String + lastName String + + @@id([firstName, lastName]) +} +``` + +the default API for selecting on that primary key uses a generated combination of the fields: + +```ts +const user = await prisma.user.findUnique({ + where: { + firstName_lastName: { + firstName: 'Paul', + lastName: 'Panther', + }, + }, +}) +``` + +Specifying `@@id([firstName, lastName], name: "fullName")` will change the Prisma Client API to this instead: + +```ts highlight=3;edit +const user = await prisma.user.findUnique({ + where: { + fullName: { + firstName: 'Paul', + lastName: 'Panther', + }, + }, +}) +``` diff --git a/docs/200-orm/100-prisma-schema/20-data-model/60-multi-schema.mdx b/docs/200-orm/100-prisma-schema/20-data-model/60-multi-schema.mdx new file mode 100644 index 0000000000..0a4848f1e6 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/60-multi-schema.mdx @@ -0,0 +1,190 @@ +--- +title: How to use Prisma with multiple database schemas +metaTitle: How to use Prisma with multiple database schemas +metaDescription: How to use Prisma with multiple database schemas +tocDepth: 3 +--- + + + + + +Multiple database schema support is currently available with the PostgreSQL, CockroachDB, and SQL Server connectors. + + + +Many database providers allow you to organize database tables into named groups. You can use this to make the logical structure of the data model easier to understand, or to avoid naming collisions between tables. + +In PostgreSQL, CockroachDB, and SQL Server, these groups are known as schemas. We will refer to them as _database schemas_ to distinguish them from Prisma's own schema file. + +This guide explains how to: + +- include multiple database schemas in your Prisma schema +- apply your schema changes to your database with Prisma Migrate and `db push` +- introspect an existing database with multiple database schemas +- query across multiple database schemas with Prisma Client + + + +## How to enable the `multiSchema` preview feature + +Multi-schema support is currently in preview. To enable the `multiSchema` preview feature, add the `multiSchema` feature flag to the `previewFeatures` field of the `generator` block in your Prisma schema file: + +```prisma file=schema.prisma highlight=3;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +## How to include multiple database schemas in your Prisma schema + +To use multiple database schemas in your Prisma schema file, add the names of your database schemas to an array in the `schemas` field, in the `datasource` block. The following example adds a `"base"` and a `"transactional"` schema: + +```prisma file=schema.prisma highlight=9;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + schemas = ["base", "transactional"] +} +``` + +You do not need to change your connection string. The `schema` value of your connection string is the default database schema that Prisma Client connects to and uses for raw queries. All other Prisma Client queries use the schema of the model or enum that you are querying. + +To designate that a model or enum belongs to a specific database schema, add the `@@schema` attribute with the name of the database schema as a parameter. In the following example, the `User` model is part of the `"base"` schema, and the `Order` model and `Size` enum are part of the `"transactional"` schema: + +```prisma file=schema.prisma highlight=5,13;add +model User { + id Int @id + orders Order[] + + @@schema("base") +} + +model Order { + id Int @id + user User @relation(fields: [id], references: [id]) + user_id Int + + @@schema("transactional") +} + +enum Size { + Small + Medium + Large + + @@schema("transactional") +} +``` + +### Tables with the same name in different database schemas + +If you have tables with the same name in different database schemas, you will need to map the table names to unique model names in your Prisma schema. This avoids name conflicts when you query models in Prisma Client. + +For example, consider a situation where the `config` table in the `base` database schema has the same name as the `config` table in the `users` database schema. To avoid name conflicts, give the models in your Prisma schema unique names (`BaseConfig` and `UserConfig`) and use the `@@map` attribute to map each model to the corresponding table name: + +```prisma file=schema.prisma +model BaseConfig { + id Int @id + + @@map("config") + @@schema("base") +} + +model UserConfig { + id Int @id + + @@map("config") + @@schema("users") +} +``` + +## How to apply your schema changes with Prisma Migrate and `db push` + +You can use Prisma Migrate or `db push` to apply changes to a Prisma schema with multiple database schemas. + +As an example, add a `Profile` model to the `base` schema of the blog post model above: + +```prisma file=schema.prisma highlight=4,9-16;add +model User { + id Int @id + orders Order[] + profile Profile? + + @@schema("base") +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + user User @relation(fields: [userId], references: [id]) + userId Int @unique + + @@schema("base") +} + +model Order { + id Int @id + user User @relation(fields: [id], references: [id]) + user_id Int + + @@schema("transactional") +} + +enum Size { + Small + Medium + Large + + @@schema("transactional") +} +``` + +You can then apply this schema change to your database. For example, you can use `migrate dev` to create and apply your schema changes as a migration: + +```terminal +npx prisma migrate dev --name add_profile +``` + +Note that if you move a model or enum from one schema to another, Prisma deletes the model or enum from the source schema and creates a new one in the target schema. + +## How to introspect an existing database with multiple database schemas + +You can introspect an existing database that has multiple database schemas in the same way that you introspect a database that has a single database schema, using `db pull`: + +```terminal +npx prisma db pull +``` + +This updates your Prisma schema to match the current state of the database. + +If you have tables with the same name in different database schemas, Prisma shows a validation error pointing out the conflict. To fix this, [rename the introspected models with the `@map` attribute](#tables-with-the-same-name-in-different-database-schemas). + +## How to query across multiple database schemas with Prisma Client + +You can query models in multiple database schemas without any change to your Prisma Client query syntax. For example, the following query finds all orders for a given user, using the Prisma schema above: + +```ts +const orders = await prisma.order.findMany({ + where: { + user: { + id: 1, + }, + }, +}) +``` + +## Learn more about the `multiSchema` preview feature + +To learn more about future plans for the `multiSchema` preview feature, or to give feedback, refer to [our Github issue](https://github.com/prisma/prisma/issues/1122). diff --git a/docs/200-orm/100-prisma-schema/20-data-model/70-unsupported-database-features.mdx b/docs/200-orm/100-prisma-schema/20-data-model/70-unsupported-database-features.mdx new file mode 100644 index 0000000000..8daa975dc6 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/70-unsupported-database-features.mdx @@ -0,0 +1,99 @@ +--- +title: 'Unsupported database features' +metaDescription: How to support database features that do not have an equivalent syntax in Prisma Schema Language. +tocDepth: 2 +--- + + + +Not all database functions and features of all of Prisma's supported databases have a Prisma Schema Language equivalent. Refer to the [database features matrix](/orm/reference/database-features) for a complete list of supported features. + + + +## Native database functions + +Prisma Schema Language supports several [functions](/orm/reference/prisma-schema-reference#attribute-functions) that you can use to set the default value of a field. The following example uses the Prisma-level `uuid()` function to set the value of the `id` field: + +```prisma +model Post { + id String @id @default(uuid()) +} +``` + +However, you can also use **native database functions** to define default values with [`dbgenerated()`](/orm/reference/prisma-schema-reference#dbgenerated) on relational databases (MongoDB does not have the concept of database-level functions). The following example uses the PostgreSQL `gen_random_uuid()` function to populate the `id` field: + +```prisma +model User { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid +} +``` + +### When to use a database-level function + +There are two reasons to use a database-level function: + +- There is no equivalent Prisma function (for example, `gen_random_bytes` in PostgreSQL). +- You cannot or do not want to rely on functions such `uuid()` and `cuid()`, which are only implemented at Prisma level and do not manifest in the database. + + Consider the following example, which sets the `id` field to a randomly generated `UUID`: + + ```prisma + model Post { + id String @id @default(uuid()) + } + ``` + + The UUID is _only_ generated if you use Prisma Client to create the `Post`. If you create posts in any other way, such as a bulk import script written in plain SQL, you must generate the UUID yourself. + +### Enable PostgreSQL extensions for native database functions + +In PostgreSQL, some native database functions are part of an extension. For example, in PostgreSQL versions 12.13 and earlier, the `gen_random_uuid()` function is part of the [`pgcrypto`](https://www.postgresql.org/docs/10/pgcrypto.html) extension. + +To use a PostgreSQL extension, you must first install it on the file system of your database server. + +In Prisma versions 4.5.0 and later, you can then activate the extension by declaring it in your Prisma schema with the [`postgresqlExtensions` preview feature](/orm/prisma-schema/postgresql-extensions): + +```prisma file=schema.prisma highlight=3,9;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [pgcrypto] +} +``` + +In earlier versions of Prisma, you must instead run a SQL command to activate the extension: + +```sql +CREATE EXTENSION IF NOT EXISTS pgcrypto; +``` + +If your project uses [Prisma Migrate](/orm/prisma-migrate), you must [install the extension as part of a migration](/orm/prisma-migrate/workflows/native-database-functions) . Do not install the extension manually, because it is also required by the shadow database. + +Prisma Migrate returns the following error if the extension is not available: + +``` +Migration `20210221102106_failed_migration` failed to apply cleanly to a temporary database. +Database error: Error querying the database: db error: ERROR: type "pgcrypto" does not exist +``` + +## Unsupported field types + +Some database types of relational databases, such as `polygon` or `geometry`, do not have a Prisma Schema Language equivalent. Use the [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) field type to represent the field in your Prisma schema: + +```prisma highlight=3;normal +model Star { + id Int @id @default(autoincrement()) + position Unsupported("circle")? @default(dbgenerated("'<(10,4),11>'::circle")) +} +``` + +The `prisma migrate dev` and `prisma db push` command will both create a `position` field of type `circle` in the database. However, the field will not be available in the generated Prisma Client. + +## Unsupported database features + +Some features, like SQL views or partial indexes, cannot be represented in the Prisma schema. If your project uses [Prisma Migrate](/orm/prisma-migrate), you must [include unsupported features as part of a migration](/orm/prisma-migrate/workflows/unsupported-database-features) . diff --git a/docs/200-orm/100-prisma-schema/20-data-model/80-table-inheritance.mdx b/docs/200-orm/100-prisma-schema/20-data-model/80-table-inheritance.mdx new file mode 100644 index 0000000000..32170bbb82 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/80-table-inheritance.mdx @@ -0,0 +1,328 @@ +--- +title: Table inheritance +metaTitle: Table inheritance +metaDescription: Learn about the use cases and patterns for table inheritance in Prisma ORM that enable usage of union types or polymorphic structures in your application. +tocDepth: 3 +--- + +## Overview + +Table inheritance is a software design pattern that allows the modeling of hierarchical relationships between entities. Using table inheritance on the database level can also enable the use of union types in your JavaScript/TypeScript application or share a set of common properties across multiple models. + +This page introduces two approaches to table inheritance and explains how to use them with Prisma ORM. + +A common use case for table inheritance may be when an application needs to display a _feed_ of some kind of _content activities_. A content activity in this case, could be a _video_ or an _article_. As an example, let's assume that: + +- a content activity always has an `id` and a `url` +- in addition to `id` and a `url`, a video also has a `duration` (modeled as an `Int`) +- in addition to `id` and a `url`, an article also a `body` (modeled as a `String`) + +### Use cases + +#### Union types + +Union types are a convenient feature in TypeScript that allows developers to work more flexibly with the types in their data model. + +In TypeScript, union types look as follows: + +```ts no-copy +type Activity = Video | Article +``` + +While [it's currently not possible to model union types in the Prisma schema](https://github.com/prisma/prisma/issues/2505), you can use them with Prisma ORM by using table inheritance and some additional type definitions. + +#### Sharing properties across multiple models + +If you have a use case where multiple models should share a particular set of properties, you can model this using table inheritance as well. + +For example, if both the `Video` and `Article` models from above should have a shared `title` property, you can achieve this with table inheritance as well. + +### Example + +In a simple Prisma schema, this would look as follows. Note that we're adding a `User` model as well to illustrate how this can work with relations: + +```prisma file=schema.prisma +model Video { + id Int @id + url String @unique + duration Int + + user User @relation(fields: [userId], references: [id]) + userId Int +} + +model Article { + id Int @id + url String @unique + body String + + user User @relation(fields: [userId], references: [id]) + userId Int +} + +model User { + id Int @id + name String + videos Video[] + articles Article[] +} +``` + +Let's investigate how we can model this using table inheritance. + +### Single-table vs multi-table inheritance + +Here is a quick comparison of the two main approaches for table inheritance: + +- **Single-table inheritance (STI)**: Uses a _single_ table to store data of _all_ the different entities in one location. In our example, there'd be a single `Activity` table with the `id`, `url` as well as te `duration` and `body` column. It also uses a `type` column that indicates whether an _activity_ is a _video_ or an _article_. +- **Multi-table inheritance (MTI)**: Uses _multiple_ tables to store the data of the different entities separately and links them via foreign keys. In our example, there'd be an `Activity` table with the `id`, `url` column, a `Video` table with the `duration` and a foreign key to `Activity` as well as an `Article` table with the `body` and a foreign key. There is also a `type` column that acts as a discriminator and indicates whether an _activity_ is a _video_ or an _article_. Note that multi-table inheritance is also sometimes called _delegated types_. + +You can learn about the tradeoffs of both approaches [below](#tradeoffs-between-sti-and-mti). + +## Single-table inheritance (STI) + +### Data model + +Using STI, the above scenario can be modeled as follows: + +```prisma +model Activity { + id Int @id // shared + url String @unique // shared + duration Int? // video-only + body String? // article-only + type ActivityType // discriminator + + owner User @relation(fields: [ownerId], references: [id]) + ownerId Int +} + +enum ActivityType { + Video + Article +} + +model User { + id Int @id @default(autoincrement()) + name String? + activities Activity[] +} +``` + +A few things to note: + +- The model-specific properties `duration` and `body` must be marked as optional (i.e., with `?`). That's because a record in the `Activity` table that represents a _video_ must not have a value for `body`. Conversely, an `Activity` record representing an _article_ can never have a `duration` set. +- The `type` discriminator column indicates whether each record represents a _video_ or an _article_ item. + +### Prisma Client API + +Due to how Prisma ORM generates types and an API for the data model, there will only to be an `Activity` type and the CRUD queries that belong to it (`create`, `update`, `delete`, ...) available to you. + +#### Querying for videos and articles + +You can now query for only _videos_ or _articles_ by filtering on the `type` column. For example: + +```ts +// Query all videos +const videos = await prisma.activity.findMany({ + where: { type: 'Video' }, +}) + +// Query all articles +const articles = await prisma.activity.findMany({ + where: { type: 'Article' }, +}) +``` + +#### Defining dedicated types + +When querying for videos and articles like that, TypeScript will still only recognize an `Activity` type. That can be annoying because even the objects in `videos` will have (optional) `body` and the objects in `articles` will have (optional) `duration` fields. + +If you want to have type safety for these objects, you need to define dedicated types for them. You can do this, for example, by using the generated `Activity` type and the TypeScript `Omit` utility type to remove properties from it: + +```ts +import { Activity } from '@prisma/client' + +type Video = Omit +type Article = Omit +``` + +In addition, it will be helpful to convert mapping functions that convert an object of type `Activity` to the `Video` and `Article` types: + +```ts +function activityToVideo(activity: Activity): Video { + return { + url: activity.url, + duration: activity.duration ? activity.duration : -1, + ownerId: activity.ownerId, + } as Video +} + +function activityToArticle(activity: Activity): Article { + return { + url: activity.url, + body: activity.body ? activity.body : '', + ownerId: activity.ownerId, + } as Article +} +``` + +Now you can turn an `Activity` into a more specific type (i.e., `Article` or `Video`) after querying: + +```ts +const videoActivities = await prisma.activity.findMany({ + where: { type: 'Video' }, +}) +const videos: Video[] = videoActivities.map(activityToVideo) +``` + +#### Using Prisma Client extension for a more convenient API + +You can use [Prisma Client extensions](/orm/prisma-client/client-extensions) to create a more convenient API for the table structures in your database. + +## Multi-table inheritance (MTI) + +### Data model + +Using MTI, the above scenario can be modeled as follows: + +```prisma +model Activity { + id Int @id @default(autoincrement()) + url String // shared + type ActivityType // discriminator + + video Video? // model-specific 1-1 relation + article Article? // model-specific 1-1 relation + + owner User @relation(fields: [ownerId], references: [id]) + ownerId Int +} + +model Video { + id Int @id @default(autoincrement()) + duration Int // video-only + activityId Int @unique + activity Activity @relation(fields: [activityId], references: [id]) +} + +model Article { + id Int @id @default(autoincrement()) + body String // article-only + activityId Int @unique + activity Activity @relation(fields: [activityId], references: [id]) +} + +enum ActivityType { + Video + Article +} + +model User { + id Int @id @default(autoincrement()) + name String? + activities Activity[] +} +``` + +A few things to note: + +- A 1-1 relation is needed between `Activity` and `Video` as well as `Activity` and `Article`. This relationship is used to fetch the specific information about a record when needed. +- The model-specific properties `duration` and `body` can be made _required_ with this approach. +- The `type` discriminator column indicates whether each record represents a _video_ or an _article_ item. + +### Prisma Client API + +This time, you can query for videos and articles directly via the `video` and `article` properties on your `PrismaClient`` instance. + +#### Querying for videos and articles + +If you want to access the shared properties, you need to use `include` to fetch the relation to `Activity`. + +```ts +// Query all videos +const videos = await prisma.video.findMany({ + include: { activity: true }, +}) + +// Query all articles +const articles = await prisma.article.findMany({ + include: { activity: true }, +}) +``` + +Depending on your needs, you may also query the other way around by filtering on the `type` discriminator column: + +```ts +// Query all articles +const videoActivities = await prisma.activity.findMany({ + where: { type: 'Video' } + include: { video: true } +}) +``` + +#### Defining dedicated types + +While a bit more convenient in terms of types compare STI, the generated typings likely still won't fit all your needs. + +Here's how you can define `Video` and `Article` types by combining Prisma ORM's generated `Video` and `Article` types with the `Activity` type. These combinations create a new type with the desired properties. Note that we're also omitting the `type` discriminator column because that's not needed anymore on the specific types: + +```ts +import { + Video as VideoDB, + Article as ArticleDB, + Activity, +} from '@prisma/client' + +type Video = Omit +type Article = Omit +``` + +Once these types are defined, you can define mapping functions to convert the types you receive from the queries above into the desired `Video` and `Article` types. Here's the example for the `Video` type: + +```ts +import { Prisma, Video as VideoDB, Activity } from '@prisma/client' + +type Video = Omit + +// Create `VideoWithActivity` typings for the objects returned above +const videoWithActivity = Prisma.validator()({ + include: { activity: true }, +}) +type VideoWithActivity = Prisma.VideoGetPayload + +// Map to `Video` type +function toVideo(a: VideoWithActivity): Video { + return { + id: a.id, + url: a.activity.url, + ownerId: a.activity.ownerId, + duration: a.duration, + activityId: a.activity.id, + } +} +``` + +Now you can take the objects returned by the queries above and transform them using `toVideo`: + +```ts +const videoWithActivities = await prisma.video.findMany({ + include: { activity: true }, +}) +const videos: Video[] = videoWithActivities.map(toVideo) +``` + +#### Using Prisma Client extension for a more convenient API + +You can use [Prisma Client extensions](/orm/prisma-client/client-extensions) to create a more convenient API for the table structures in your database. + +## Tradeoffs between STI and MTI + +- **Data model**: The data model may feel more clean with MTI. With STI, you may end up with very wide rows and lots of columns that have `NULL` values in them. +- **Performance**: MTI may come with a performance cost because you need to join the parent and child tables to access _all_ properties relevant for a model. +- **Typings**: With Prisma ORM, MTI gives you proper typings for the specific models (i.e., `Article` and `Video` in the examples above) already, while you need to create these from scratch with STI. +- **IDs / Primary keys**: With MTI, records have two IDs (one on the parent and another on the child table) that may not match. You need to consider this in the business logic of your application. + +## Third-party solutions + +While Prisma ORM doesn't natively support union types or polymorphism at the moment, you can check out [Zenstack](https://github.com/zenstackhq/zenstack) which is adding an extra layer of features to the Prisma schema. Read their [blog post about polymorphism in Prisma ORM](https://zenstack.dev/blog/polymorphism) to learn more. diff --git a/docs/200-orm/100-prisma-schema/20-data-model/index.mdx b/docs/200-orm/100-prisma-schema/20-data-model/index.mdx new file mode 100644 index 0000000000..38f3f46a38 --- /dev/null +++ b/docs/200-orm/100-prisma-schema/20-data-model/index.mdx @@ -0,0 +1,10 @@ +--- +title: 'Data model' +metaTitle: 'Data model' +metaDescription: 'Learn everything you need about the Prisma data model.' +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/100-prisma-schema/50-introspection.mdx b/docs/200-orm/100-prisma-schema/50-introspection.mdx new file mode 100644 index 0000000000..553f50b67f --- /dev/null +++ b/docs/200-orm/100-prisma-schema/50-introspection.mdx @@ -0,0 +1,398 @@ +--- +title: 'Introspection' +metaTitle: 'What is introspection? (Reference)' +metaDescription: 'Learn how you can introspect your database to generate a data model into your Prisma schema.' +--- + + + +You can introspect your database using the Prisma CLI in order to generate the [data model](/orm/prisma-schema/data-model) in your [Prisma schema](/orm/prisma-schema). The data model is needed to [generate Prisma Client](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names). + +Introspection is often used to generate an _initial_ version of the data model when [adding Prisma to an existing project](/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-postgresql). + +However, it can also be [used _repeatedly_ in an application](#introspection-with-an-existing-schema). This is most commonly the case when you're _not_ using [Prisma Migrate](/orm/prisma-migrate) but perform schema migrations using plain SQL or another migration tool. In that case, you also need to re-introspect your database and subsequently re-generate Prisma Client to reflect the schema changes in your [Prisma Client API](/orm/prisma-client). + + + +## What does introspection do? + +Introspection has one main function: Populate your Prisma schema with a data model that reflects the current database schema. + +![Introspect your database with Prisma](/img/prisma-db-pull-generate-schema.png) + +Here's an overview of its main functions on SQL databases: + +- Map _tables_ in the database to [Prisma models](/orm/prisma-schema/data-model/models#defining-models) +- Map _columns_ in the database to the [fields](/orm/prisma-schema/data-model/models#defining-fields) of Prisma models +- Map _indexes_ in the database to [indexes](/orm/prisma-schema/data-model/models#defining-an-index) in the Prisma schema +- Map _database constraints_ to [attributes](/orm/prisma-schema/data-model/models#defining-attributes) or [type modifiers](/orm/prisma-schema/data-model/models#type-modifiers) in the Prisma schema + +On MongoDB, the main functions are the following: + +- Map _collections_ in the database to [Prisma models](/orm/prisma-schema/data-model/models#defining-models) +- Map _documents_ in the collections to the [fields](/orm/prisma-schema/data-model/models#defining-fields) of Prisma models by _sampling them_ +- Map _indexes_ in the database to [indexes](/orm/prisma-schema/data-model/models#defining-an-index) in the Prisma schema, if the collection contains at least one document contains a field included in the index + +You can learn more about how Prisma maps types from the database to the types available in the Prisma schema on the respective docs page for the data source connector: + +- [PostgreSQL](/orm/overview/databases/postgresql#type-mapping-between-postgresql-and-prisma-schema) +- [MySQL](/orm/overview/databases/mysql#type-mapping-between-mysql-to-prisma-schema) +- [SQLite](/orm/overview/databases/sqlite#type-mapping-between-sqlite-to-prisma-schema) +- [Microsoft SQL Server](/orm/overview/databases/sql-server#type-mapping-between-microsoft-sql-server-to-prisma-schema) + +## The `prisma db pull` command + +You can introspect your database using the `prisma db pull` command of the [Prisma CLI](/orm/tools/prisma-cli#installation). Note that using this command requires your [connection URL](/orm/reference/connection-urls) to be set in your Prisma schema [`datasource`](/orm/prisma-schema/overview/data-sources). + +Here's a high-level overview of the steps that `prisma db pull` performs internally: + +1. Read the [connection URL](/orm/reference/connection-urls) from the `datasource` configuration in the Prisma schema +1. Open a connection to the database +1. Introspect database schema (i.e. read tables, columns and other structures ...) +1. Transform database schema into Prisma data model +1. Write data model into Prisma schema or [update existing schema](#introspection-with-an-existing-schema) + +## Introspection workflow + +The typical workflow for projects that are not using Prisma Migrate, but instead use plain SQL or another migration tool looks as follows: + +1. Change the database schema (e.g. using plain SQL) +1. Run `prisma db pull` to update the Prisma schema +1. Run `prisma generate` to update Prisma Client +1. Use the updated Prisma Client in your application + +Note that as you evolve the application, [this process can be repeated for an indefinite number of times](#introspection-with-an-existing-schema). + +![Introspect workflow](/img/prisma-evolve-app-workflow.png) + +## Rules and conventions + +Prisma employs a number of conventions for translating a database schema into a Prisma data model: + +### Model, field and enum names + +Field, model and enum names (identifiers) must start with a letter and generally must only contain underscores, letters and digits. You can find the naming rules and conventions for each of these identifiers on the respective docs page: + +- [Naming models](/orm/reference/prisma-schema-reference#naming-conventions) +- [Naming fields](/orm/reference/prisma-schema-reference#naming-conventions-1) +- [Naming enums](/orm/reference/prisma-schema-reference#naming-conventions-2) + +The general rule for identifiers is that they need to adhere to this regular expression: + +``` +[A-Za-z][A-Za-z0-9_]* +``` + +#### Sanitization of invalid characters + +**Invalid characters** are being sanitized during introspection: + +- If they appear _before_ a letter in an identifier, they get dropped. +- If they appear _after_ the first letter, they get replaced by an underscore. + +Additionally, the transformed name is mapped to the database using `@map` or `@@map` to retain the original name. + +Consider the following table as an example: + +```sql +CREATE TABLE "42User" ( + _id SERIAL PRIMARY KEY, + _name VARCHAR(255), + two$two INTEGER +); +``` + +Because the leading `42` in the table name as well as the leading underscores and the `$` on the columns are forbidden in Prisma, introspection adds the `@map` and `@@map` attributes so that these names adhere to Prisma's naming conventions: + +```prisma +model User { + id Int @id @default(autoincrement()) @map("_id") + name String? @map("_name") + two_two Int? @map("two$two") + + @@map("42User") +} +``` + +#### Duplicate Identifiers after Sanitization + +If sanitization results in duplicate identifiers, no immediate error handling is in place. You get the error later and can manually fix it. + +Consider the case of the following two tables: + +```sql +CREATE TABLE "42User" ( + _id SERIAL PRIMARY KEY +); + +CREATE TABLE "24User" ( + _id SERIAL PRIMARY KEY +); +``` + +This would result in the following introspection result: + +```prisma +model User { + id Int @id @default(autoincrement()) @map("_id") + + @@map("42User") +} + +model User { + id Int @id @default(autoincrement()) @map("_id") + + @@map("24User") +} +``` + +Trying to generate your Prisma Client with `prisma generate` you would get the following error: + + + + + +``` +npx prisma generate +``` + + + + + +```code no-copy +$ npx prisma generate +Error: Schema parsing +error: The model "User" cannot be defined because a model with that name already exists. + --> schema.prisma:17 + | +16 | } +17 | model User { + | + +Validation Error Count: 1 +``` + + + + + +In this case, you must manually change the name of one of the two generated `User` models because duplicate model names are not allowed in the Prisma schema. + +### Order of fields + +Introspection lists model fields in the same order as the corresponding table columns in the database. + +### Order of attributes + +Introspection adds attributes in the following order (this order is mirrored by `prisma format`): + +- Block level: `@@id`, `@@unique`, `@@index`, `@@map` +- Field level : `@id`, `@unique`, `@default`, `@updatedAt`, `@map`, `@relation` + +### Relations + +Prisma translates foreign keys that are defined on your database tables into [relations](/orm/prisma-schema/data-model/relations). + +#### One-to-one relations + +Prisma adds a [one-to-one](/orm/prisma-schema/data-model/relations/one-to-one-relations) relation to your data model when the foreign key on a table has a `UNIQUE` constraint, e.g.: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Profile" ( + id SERIAL PRIMARY KEY, + "user" integer NOT NULL UNIQUE, + FOREIGN KEY ("user") REFERENCES "User"(id) +); +``` + +Prisma translates this into the following data model: + +```prisma +model User { + id Int @id @default(autoincrement()) + Profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + user Int @unique + User User @relation(fields: [user], references: [id]) +} +``` + +#### One-to-many relations + +By default, Prisma adds a [one-to-many](/orm/prisma-schema/data-model/relations/one-to-many-relations) relation to your data model for a foreign key it finds in your database schema: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + "author" integer NOT NULL, + FOREIGN KEY ("author") REFERENCES "User"(id) +); +``` + +These tables are transformed into the following models: + +```prisma +model User { + id Int @id @default(autoincrement()) + Post Post[] +} + +model Post { + id Int @id @default(autoincrement()) + author Int + User User @relation(fields: [author], references: [id]) +} +``` + +#### Many-to-many relations + +[Many-to-many](/orm/prisma-schema/data-model/relations/many-to-many-relations) relations are commonly represented as [relation tables](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) in relational databases. + +Prisma supports two ways for defining many-to-many relations in the Prisma schema: + +- [Implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) (Prisma manages the relation table under the hood) +- [Explicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations) (the relation table is present as a [model](/orm/prisma-schema/data-model/models#defining-models)) + +_Implicit_ many-to-many relations are recognized if they adhere to Prisma's [conventions for relation tables](/orm/prisma-schema/data-model/relations/many-to-many-relations#conventions-for-relation-tables-in-implicit-m-n-relations). Otherwise the relation table is rendered in the Prisma schema as a model (therefore making it an _explicit_ many-to-many relation). + +This topic is covered extensively on the docs page about [Relations](/orm/prisma-schema/data-model/relations). + +#### Disambiguating relations + +Prisma generally omits the `name` argument on the [`@relation`](/orm/prisma-schema/data-model/relations#the-relation-attribute) attribute if it's not needed. Consider the `User` ↔ `Post` example from the previous section. The `@relation` attribute only has the `references` argument, `name` is omitted because it's not needed in this case: + +```prisma +model Post { + id Int @id @default(autoincrement()) + author Int + User User @relation(fields: [author], references: [id]) +} +``` + +It would be needed if there were _two_ foreign keys defined on the `Post` table: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + "author" integer NOT NULL, + "favoritedBy" INTEGER, + FOREIGN KEY ("author") REFERENCES "User"(id), + FOREIGN KEY ("favoritedBy") REFERENCES "User"(id) +); +``` + +In this case, Prisma needs to [disambiguate the relation](/orm/prisma-schema/data-model/relations#disambiguating-relations) using a dedicated relation name: + +```prisma +model Post { + id Int @id @default(autoincrement()) + author Int + favoritedBy Int? + User_Post_authorToUser User @relation("Post_authorToUser", fields: [author], references: [id]) + User_Post_favoritedByToUser User? @relation("Post_favoritedByToUser", fields: [favoritedBy], references: [id]) +} + +model User { + id Int @id @default(autoincrement()) + Post_Post_authorToUser Post[] @relation("Post_authorToUser") + Post_Post_favoritedByToUser Post[] @relation("Post_favoritedByToUser") +} +``` + +Note that you can rename the [Prisma-level](/orm/prisma-schema/data-model/relations#relation-fields) relation field to anything you like so that it looks friendlier in the generated Prisma Client API. + +## Introspection with an existing schema + +Running `prisma db pull` for relational databases with an existing schema file merges manual changes made to the schema, with changes made in the database. (This functionality has been added for the first time with version 2.6.0.) For MongoDB, Introspection for now is meant to be done only once for the initial data model. Running it repeatedly will lead to loss of custom changes, as the ones listed below. + +Introspection for relational databases maintains the following manual changes: + +- Order of `model` blocks +- Order of `enum` blocks +- Comments +- `@map` and `@@map` attributes +- `@updatedAt` +- `@default(cuid())` (`cuid()` is a Prisma-level function) +- `@default(uuid())` (`uuid()` is a Prisma-level function) +- Custom `@relation` names + +> **Note**: Only relations between models on the database level will be picked up. This means that there **must be a foreign key set**. + +The following properties of the schema are determined by the database: + +- Order of fields within `model` blocks +- Order of values within `enum` blocks + +> **Note**: All `enum` blocks are listed below `model` blocks. + +### Force overwrite + +To overwrite manual changes, and generate a schema based solely on the introspected database and ignore any existing schema file, add the `--force` flag to the `db pull` command: + +```terminal +npx prisma db pull --force +``` + +Use cases include: + +- You want to start from scratch with a schema generated from the underlying database +- You have an invalid schema and must use `--force` to make introspection succeed + +## Introspecting only a subset of your database schema + +Introspecting only a subset of your database schema is [not yet officially supported](https://github.com/prisma/prisma/issues/807) by Prisma. + +However, you can achieve this by creating a new database user that only has access to the tables which you'd like to see represented in your Prisma schema, and then perform the introspection using that user. The introspection will then only include the tables the new user has access to. + +If your goal is to exclude certain models from the [Prisma Client generation](/orm/prisma-client/setup-and-configuration/generating-prisma-client), you can add the [`@@ignore` attribute](/orm/reference/prisma-schema-reference#ignore-1) to the model definition in your Prisma schema. Ignored models are excluded from the generated Prisma Client. + +## Introspection warnings for unsupported features + +The Prisma Schema Language (PSL) can express a majority of the database features of the [target databases](/orm/reference/supported-databases) Prisma supports. However, there are features and functionality the Prisma Schema Language still needs to express. + +For these features, the Prisma CLI will surface detect usage of the feature in your database and return a warning. The Prisma CLI will also add a comment in the models and fields the features are in use in the Prisma schema. The warnings will also contain a workaround suggestion. + +The `prisma db pull` command will surface the following unsupported features: + +- From version [4.13.0](https://github.com/prisma/prisma/releases/tag/4.13.0): + - [Partitioned tables](https://github.com/prisma/prisma/issues/1708) + - [PostgreSQL Row Level Security](https://github.com/prisma/prisma/issues/12735) + - [Index sort order, `NULLS FIRST` / `NULLS LAST`](https://github.com/prisma/prisma/issues/15466) + - [CockroachDB row-level TTL](https://github.com/prisma/prisma/issues/13982) + - [Comments](https://github.com/prisma/prisma/issues/8703) + - [PostgreSQL deferred constraints](https://github.com/prisma/prisma/issues/8807) +- From version [4.14.0](https://github.com/prisma/prisma/releases/tag/4.14.0): + - [Check Constraints](https://github.com/prisma/prisma/issues/3388) (MySQL + PostgreSQL) + - [Exclusion Constraints](https://github.com/prisma/prisma/issues/17514) + - [MongoDB $jsonSchema](https://github.com/prisma/prisma/issues/8135) +- From version [4.16.0](https://github.com/prisma/prisma/releases/tag/4.16.0): + - [Expression indexes](https://github.com/prisma/prisma/issues/2504) + +You can find the list of features we intend to support on [GitHub (labeled with `topic:database-functionality`)](https://github.com/prisma/prisma/issues?q=is%3Aopen+label%3A%22topic%3A+database-functionality%22+label%3Ateam%2Fschema+sort%3Aupdated-desc+). + +### Workaround for introspection warnings for unsupported features + +If you are using a relational database and either one of the above features listed in the previous section: + +1. Create a draft migration: + ```terminal + npx prisma migrate dev --create-only + ``` +2. Add the SQL that adds the feature surfaced in the warnings. +3. Apply the draft migration to your database: + ```terminal + npx prisma migrate dev + ``` diff --git a/docs/200-orm/100-prisma-schema/80-postgresql-extensions.mdx b/docs/200-orm/100-prisma-schema/80-postgresql-extensions.mdx new file mode 100644 index 0000000000..bb035178bf --- /dev/null +++ b/docs/200-orm/100-prisma-schema/80-postgresql-extensions.mdx @@ -0,0 +1,116 @@ +--- +title: 'PostgreSQL extensions' +metaTitle: 'How to represent PostgreSQL extensions in your Prisma schema' +metaDescription: 'How to represent PostgreSQL extensions in your Prisma scheme, introspect extensions in your database, and apply changes to extensions with Prisma Migrate' +preview: true +tocDepth: 3 +--- + + + +This page introduces PostgreSQL extensions and describes how to represent extensions in your Prisma schema, how to introspect existing extensions in your database, and how to apply changes to your extensions to your database with Prisma Migrate. + + + +Support for declaring PostgreSQL extensions in your schema is available in preview for the PostgreSQL connector only in Prisma versions 4.5.0 and later. + + + + + +## What are PostgreSQL extensions? + +PostgreSQL allows you to extend your database functionality by installing and activating packages known as _extensions_. For example, the `citext` extension adds a case-insensitive string data type. Some extensions, such as `citext`, are supplied directly by PostgreSQL, while other extensions are developed externally. For more information on extensions, see [the PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-createextension.html). + +To use an extension, it must first be _installed_ on the local file system of your database server. You then need to _activate_ the extension, which runs a script file that adds the new functionality. + + + +Note that PostgreSQL's documentation uses the term 'install' to refer to what we call activating an extension. We have used separate terms here to make it clear that these are two different steps. + + + +Prisma's `postgresqlExtensions` preview feature allows you to represent PostgreSQL extensions in your Prisma schema. Note that specific extensions may add functionality that is not currently supported by Prisma. For example, an extension may add a type or index that is not supported by Prisma. This functionality must be implemented on a case-by-case basis and is not provided by this preview feature. + +## How to enable the `postgresqlExtensions` preview feature + +Representing PostgreSQL extensions in your Prisma schema is currently a preview feature. To enable the `postgresqlExtensions` preview feature, you will need to add the `postgresqlExtensions` feature flag to the `previewFeatures` field of the `generator` block in your Prisma schema file: + +```prisma file=schema.prisma highlight=3;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +## How to represent PostgreSQL extensions in your Prisma schema + +To represent PostgreSQL extensions in your Prisma schema, add the `extensions` field to the `datasource` block of your `schema.prisma` file with an array of the extensions that you require. For example, the following schema lists the `hstore`, `pg_trgm` and `postgis` extensions: + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [hstore(schema: "myHstoreSchema"), pg_trgm, postgis(version: "2.1")] +} +``` + +Each extension name in the Prisma schema can take the following optional arguments: + +- `schema`: the name of the schema in which to activate the extension's objects. If this argument is not specified, the current default object creation schema is used. +- `version`: the version of the extension to activate. If this argument is not specified, the value given in the extension's control file is used. +- `map`: the database name of the extension. If this argument is not specified, the name of the extension in the Prisma schema must match the database name. + +In the example above, the `hstore` extension uses the `myHstoreSchema` schema, and the `postgis` extension is activated with version 2.1 of the extension. + +The `map` argument is useful when the PostgreSQL extension that you want to activate has a name that is not a valid identifier in the Prisma schema. For example, the `uuid-ossp` PostgreSQL extension name is an invalid identifier because it contains a hyphen. In the following example, the extension is mapped to the valid name `uuidOssp` in the Prisma schema: + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [uuidOssp(map: "uuid-ossp")] +} +``` + +## How to introspect PostgreSQL extensions + +To [introspect](/orm/prisma-schema/introspection) PostgreSQL extensions currently activated in your database and add relevant extensions to your Prisma schema, run `npx prisma db pull`. + +Many PostgreSQL extensions are not relevant to the Prisma schema. For example, some extensions are intended for database administration tasks that do not change the schema. If all these extensions were included, the list of extensions would be very long. To avoid this, Prisma maintains an allowlist of known relevant extensions. The current allowlist is the following: + +- [`citext`](https://www.postgresql.org/docs/current/citext.html): provides a case-insensitive character string type, `citext` +- [`pgcrypto`](https://www.postgresql.org/docs/current/pgcrypto.html): provides cryptographic functions, like `gen_random_uuid()`, to generate universally unique identifiers (UUIDs v4) +- [`uuid-ossp`](https://www.postgresql.org/docs/current/uuid-ossp.html): provides functions, like `uuid_generate_v4()`, to generate universally unique identifiers (UUIDs v4) +- [`postgis`](https://postgis.net/): adds GIS (Geographic Information Systems) support + +**Note**: Since PostgreSQL v13, `gen_random_uuid()` can be used without an extension to generate universally unique identifiers (UUIDs v4). + +Extensions are introspected as follows: + +- The first time you introspect, all database extensions that are on the allowlist are added to your Prisma schema +- When you re-introspect, the behavior depends on whether the extension is on the allowlist or not. + - Extensions on the allowlist: + - are **added** to your Prisma schema if they are in the database but not in the Prisma schema + - are **kept** in your Prisma schema if they are in the Prisma schema and in the database + - are **removed** from your Prisma schema if they are in the Prisma schema but not the database + - Extensions not on the allowlist: + - are **kept** in your Prisma schema if they are in the Prisma schema and in the database + - are **removed** from your Prisma schema if they are in the Prisma schema but not the database + +The `version` argument will not be added to the Prisma schema when you introspect. + +## How to migrate PostgreSQL extensions + +You can update your list of PostgreSQL extensions in your Prisma schema and apply the changes to your database with [Prisma Migrate](/orm/prisma-migrate). + +This works in a similar way to migration of other elements of your Prisma schema, such as models or fields. However, there are the following differences: + +- If you remove an extension from your schema but it is still activated on your database, Prisma Migrate will not deactivate it from the database. +- If you add a new extension to your schema, it will only be activated if it does not already exist in the database, because the extension may already have been created manually. +- If you remove the `version` or `schema` arguments from the extension definition, it has no effect to the extensions in the database in the following migrations. diff --git a/docs/200-orm/100-prisma-schema/index.mdx b/docs/200-orm/100-prisma-schema/index.mdx new file mode 100644 index 0000000000..4ab105016c --- /dev/null +++ b/docs/200-orm/100-prisma-schema/index.mdx @@ -0,0 +1,11 @@ +--- +title: 'Prisma schema' +metaTitle: 'Prisma schema' +metaDescription: 'Learn everything you need to know about the Prisma schema.' +staticLink: true +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/100-prisma-schema/prisma-schema/relations-intro.png b/docs/200-orm/100-prisma-schema/prisma-schema/relations-intro.png new file mode 100644 index 0000000000..47bb895580 Binary files /dev/null and b/docs/200-orm/100-prisma-schema/prisma-schema/relations-intro.png differ diff --git a/docs/200-orm/100-prisma-schema/prisma-schema/sample-database.png b/docs/200-orm/100-prisma-schema/prisma-schema/sample-database.png new file mode 100644 index 0000000000..9aef1eb524 Binary files /dev/null and b/docs/200-orm/100-prisma-schema/prisma-schema/sample-database.png differ diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/005-introduction.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/005-introduction.mdx new file mode 100644 index 0000000000..94af6c6b46 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/005-introduction.mdx @@ -0,0 +1,172 @@ +--- +title: 'Introduction' +metaTitle: 'Introduction to Prisma Client' +metaDescription: 'Learn how to set up Prisma Client.' +--- + + + +Prisma Client is an auto-generated and type-safe query builder that's _tailored_ to your data. The easiest way to get started with Prisma Client is by following the **[Quickstart](/getting-started/quickstart)**. + + + Quickstart (5 min) + + +The setup instructions [below](#set-up) provide a high-level overview of the steps needed to set up Prisma Client. If you want to get started using Prisma Client with your own database, follow one of these guides: + + + Set up a new project from scratch + +
+
+ + Add Prisma to an existing project + + +
+ +## Set up + +### 1. Prerequisites + +In order to set up Prisma Client, you need a [Prisma schema file](/orm/prisma-schema) with your database connection, the Prisma Client generator, and at least one model: + +```prisma file=schema.prisma +datasource db { + url = env("DATABASE_URL") + provider = "postgresql" +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? +} +``` + +Also make sure to [install the Prisma CLI](/orm/tools/prisma-cli#installation): + +``` +npm install prisma --save-dev +npx prisma +``` + +### 2. Installation + +Install Prisma Client in your project with the following command: + +``` +npm install @prisma/client +``` + +This command also runs the `prisma generate` command, which generates Prisma Client into the [`node_modules/.prisma/client`](/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-prismaclient-npm-package) directory. + +### 3. Importing Prisma Client + +There are multiple ways to import Prisma Client in your project depending on your use case: + + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +For edge environments, you can import Prisma Client as follows: + + + + + +```ts +import { PrismaClient } from '@prisma/client/edge' + +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +```js +const { PrismaClient } = require('@prisma/client/edge') + +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +For Deno, you can import Prisma Client as follows: + +```ts file=lib/prisma.ts +import { PrismaClient } from './generated/client/deno/edge.ts' + +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + +The import path will depend on the custom `output` specified in Prisma Client's [`generator`](/orm/reference/prisma-schema-reference#fields-1) block in your Prisma schema. + +### 4. Use Prisma Client to send queries to your database + +Once you have instantiated `PrismaClient`, you can start sending queries in your code: + +```ts +// run inside `async` function +const newUser = await prisma.user.create({ + data: { + name: 'Alice', + email: 'alice@prisma.io', + }, +}) + +const users = await prisma.user.findMany() +``` + + + +All Prisma Client methods return an instance of [`PrismaPromise`](/orm/reference/prisma-client-reference#prismapromise-behavior) which only executes when you call `await` or `.then()` or `.catch()`. + + + +### 5. Evolving your application + +Whenever you make changes to your database that are reflected in the Prisma schema, you need to manually re-generate Prisma Client to update the generated code in the `node_modules/.prisma/client` directory: + +``` +prisma generate +``` diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/010-generating-prisma-client.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/010-generating-prisma-client.mdx new file mode 100644 index 0000000000..4ff9d3e534 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/010-generating-prisma-client.mdx @@ -0,0 +1,162 @@ +--- +title: 'Generating Prisma Client' +metaTitle: 'Generating Prisma Client' +metaDescription: 'This page explains how to generate Prisma Client. It also provides additional context on the generated client, typical workflows and Node.js configuration.' +--- + + + +Prisma Client is an auto-generated database client that's tailored to your database schema. By default, Prisma Client is generated into the `node_modules/.prisma/client` folder, but [you can specify a custom location](#using-a-custom-output-path). + +To generate and instantiate Prisma Client: + +1. Ensure that you have [Prisma CLI installed on your machine](/orm/tools/prisma-cli#installation). + +1. Add the following `generator` definition to your Prisma schema: + + ```prisma + generator client { + provider = "prisma-client-js" + } + ``` + +1. Install the `@prisma/client` npm package: + + ```terminal + npm install @prisma/client + ``` + + + + We recommend that you keep **both** the `prisma` and `@prisma/client` packages in sync to avoid any unexpected errors or behaviors. + + + +1. Generate Prisma Client with the following command: + + ```terminal + prisma generate + ``` + +1. You can now [instantiate Prisma Client](instantiate-prisma-client) in your code: + + + + + +```ts +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +```js +const { PrismaClient } = require('@prisma/client') +const prisma = new PrismaClient() +// use `prisma` in your application to read and write data in your DB +``` + + + + + +> **Important**: You need to re-run the `prisma generate` command after every change that's made to your Prisma schema to update the generated Prisma Client code. + +Here is a graphical illustration of the typical workflow for generation of Prisma Client: + +![Graphical illustration of the typical workflow for generation of Prisma Client](prisma-client-generation-workflow.png) + +Note also that `prisma generate` is _automatically_ invoked when you're installing the `@prisma/client` npm package. So, when you're initially setting up Prisma Client, you can typically save the third step from the list above. + + + +## The `@prisma/client` npm package + +The `@prisma/client` npm package consists of two key parts: + +- The `@prisma/client` module itself, which only changes when you re-install the package +- The `.prisma/client` folder, which is the [default location](#using-a-custom-output-path) for the unique Prisma Client generated from your schema + +`@prisma/client/index.d.ts` exports `.prisma/client`: + +```ts +export * from '.prisma/client' +``` + +This means that you still import `@prisma/client` in your own `.ts` files: + +```ts +import { PrismaClient } from '@prisma/client' +``` + +Prisma Client is generated from your Prisma schema and is unique to your project. Each time you change the schema (for example, by performing a [schema migration](/orm/prisma-migrate)) and run `prisma generate`, Prisma Client's code changes: + +![The .prisma and @prisma folders](prisma-client-node-module.png) + +The `.prisma` folder is unaffected by [pruning](https://docs.npmjs.com/cli/prune.html) in Node.js package managers. + +## The location of Prisma Client + +If you do not specify a custom `output` in the `generator` block, Prisma Client is generated into the `./node_modules/.prisma/client` folder by default. There are [some advantages to maintaining the default location](#why-is-prisma-client-generated-into-node_modulesprismaclient-by-default). + +### Using a custom `output` path + +You can also specify a custom `output` path on the `generator` configuration, for example (assuming your `schema.prisma` file is located at the default `prisma` subfolder): + +```prisma +generator client { + provider = "prisma-client-js" + output = "../src/generated/client" +} +``` + +After running `prisma generate` for that schema file, the Prisma Client package will be located in: + +``` +./src/generated/client +``` + +To import the `PrismaClient` from a custom location (for example, from a file named `./src/script.ts`): + +```ts +import { PrismaClient } from './generated/client' +``` + +### Why is Prisma Client generated into `node_modules/.prisma/client` by default? + +#### Importing Prisma Client + +By generating Prisma Client into `node_modules/.prisma/client` and exporting it from `@prisma/client`, you can import it and instantiate Prisma Client in your code as follows: + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +// use `prisma` in your application to read and write data in your DB +``` + +or + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +// use `prisma` in your application to read and write data in your DB +``` + +#### Keeping the query engine out of version control by default + +Prisma Client uses a [_query engine_](/orm/more/under-the-hood/engines) to run queries against the database. This query engine is downloaded when `prisma generate` is invoked and stored in the `output` path together with the generated Client. + +By generating Prisma Client into `node_modules`, the query engine is usually kept out of version control by default since `node_modules` is typically ignored for version control. +When using a custom `output` path for the generated Prisma Client, it is advised to exclude it from your version control. For Git, this means adding the `output` path to your `.gitignore` file. + +## Generating Prisma Client in the `postinstall` hook of `@prisma/client` + +The `@prisma/client` package defines its own `postinstall` hook that's being executed whenever the package is being installed. This hook invokes the `prisma generate` command which in turn generates the Prisma Client code into the default location `node_modules/.prisma/client`. Notice that this requires the `prisma` CLI to be available, either as local dependency or as a global installation. It is recommended to always install the `prisma` package as a development dependency, using `npm install prisma --save-dev`, to avoid versioning conflicts. diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/015-instantiate-prisma-client.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/015-instantiate-prisma-client.mdx new file mode 100644 index 0000000000..45ef965454 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/015-instantiate-prisma-client.mdx @@ -0,0 +1,63 @@ +--- +title: 'Instantiating Prisma Client' +metaTitle: 'Instantiating Prisma Client' +metaDescription: 'How to create and use an instance of PrismaClient in your app.' +tocDepth: 3 +--- + + + +The following example demonstrates how to import and instantiate your [generated client](generating-prisma-client) from the [default path](generating-prisma-client#using-a-custom-output-path). + + + + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() +``` + + + + + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() +``` + + + + + +:::tip + +You can further customize `PrismaClient` with [constructor parameters](/orm/reference/prisma-client-reference#prismaclient) - for example, set [logging levels](/orm/prisma-client/observability-and-logging/logging) or customize [error formatting](error-formatting). + +::: + +## The number of `PrismaClient` instances matters + +Your application should generally only create **one instance** of `PrismaClient`. How to achieve this depends on whether you are using Prisma in a [long-running application](/orm/prisma-client/setup-and-configuration/databases-connections#prismaclient-in-long-running-applications) or in a [serverless environment](/orm/prisma-client/setup-and-configuration/databases-connections#prismaclient-in-serverless-environments) . + +The reason for this is that each instance of `PrismaClient` manages a connection pool, which means that a large number of clients can **exhaust the database connection limit**. This applies to all database connectors. + +If you use the **MongoDB connector**, connections are managed by the MongoDB driver connection pool. If you use a **relational database connector**, connections are managed by Prisma's [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool). Each instance of `PrismaClient` creates its own pool. + +1. Each client creates its own instance of the [query engine](/orm/more/under-the-hood/engines). +1. Each query engine creates a [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool) with a default pool size of: + + - `num_physical_cpus * 2 + 1` for relational databases + - [`100` for MongoDB](https://docs.mongodb.com/manual/reference/connection-string/#mongodb-urioption-urioption.maxPoolSize) + +1. Too many connections may start to **slow down your database** and eventually lead to errors such as: + + ``` + Error in connector: Error querying the database: db error: FATAL: sorry, too many clients already + at PrismaClientFetcher.request + ``` diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/100-connection-management.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/100-connection-management.mdx new file mode 100644 index 0000000000..c74b5fbf5f --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/100-connection-management.mdx @@ -0,0 +1,108 @@ +--- +title: 'Connection management' +metaTitle: 'Connection management' +metaDescription: 'This page explains how database connections are handled with Prisma Client and how to manually connect and disconnect your database.' +tocDepth: 3 +--- + + + +`PrismaClient` connects and disconnects from your data source using the following two methods: + +- [`$connect()`](/orm/reference/prisma-client-reference#connect-1) +- [`$disconnect()`](/orm/reference/prisma-client-reference#disconnect-1) + +In most cases, you **do not need to explicitly call these methods**. `PrismaClient` automatically connects when you run your first query, creates a [connection pool](connection-pool), and disconnects when the Node.js process ends. + +See the [connection management guide](/orm/prisma-client/setup-and-configuration/databases-connections) for information about managing connections for different deployment paradigms (long-running processes and serverless functions). + + + +## `$connect()` + +It is not necessary to call [`$connect()`](/orm/reference/prisma-client-reference#connect-1) thanks to the _lazy connect_ behavior: The `PrismaClient` instance connects lazily when the first request is made to the API (`$connect()` is called for you under the hood). + +### Calling `$connect()` explicitly + +If you need the first request to respond instantly and cannot wait for a lazy connection to be established, you can explicitly call `prisma.$connect()` to establish a connection to the data source: + +```ts +const prisma = new PrismaClient() + +// run inside `async` function +await prisma.$connect() +``` + +## `$disconnect()` + +When you call [`$disconnect()`](/orm/reference/prisma-client-reference#disconnect-1) , Prisma Client: + +1. Runs the [`beforeExit` hook](#exit-hooks) +2. Ends the Query Engine child process and closes all connections + +In a long-running application such as a GraphQL API, which constantly serves requests, it does not make sense to `$disconnect()` after each request - it takes time to establish a connection, and doing so as part of each request will slow down your application. + +:::tip + +To avoid too _many_ connections in a long-running application, we recommend that you [use a single instance of `PrismaClient` across your application](/orm/prisma-client/setup-and-configuration/instantiate-prisma-client#the-number-of-prismaclient-instances-matters). + +::: + +### Calling `$disconnect()` explicitly + +One scenario where you should call `$disconnect()` explicitly is where a script: + +1. Runs **infrequently** (for example, a scheduled job to send emails each night), which means it does not benefit from a long-running connection to the database _and_ +2. Exists in the context of a **long-running application**, such as a background service. If the application never shuts down, Prisma Client never disconnects. + +The following script creates a new instance of `PrismaClient`, performs a task, and then disconnects - which closes the connection pool: + +```ts highlight=19;normal +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() +const emailService = new EmailService() + +async function main() { + const allUsers = await prisma.user.findMany() + const emails = allUsers.map((x) => x.email) + + await emailService.send(emails, 'Hello!') +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + +If the above script runs multiple times in the context of a long-running application _without_ calling `$disconnect()`, a new connection pool is created with each new instance of `PrismaClient`. + +## Exit hooks + + + +From Prisma 5.0.0, the `beforeExit` hook only applies to the [binary Query Engine](/orm/more/under-the-hood/engines#configuring-the-query-engine). + + + +The `beforeExit` hook runs when Prisma is triggered externally (e.g. via a `SIGINT` signal) to shut down, and allows you to run code _before_ Prisma Client disconnects - for example, to issue queries as part of a graceful shutdown of a service: + +```ts +const prisma = new PrismaClient() + +prisma.$on('beforeExit', async () => { + console.log('beforeExit hook') + // PrismaClient still available + await prisma.message.create({ + data: { + message: 'Shutting down server', + }, + }) +}) +``` diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/115-connection-pool.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/115-connection-pool.mdx new file mode 100644 index 0000000000..54e52841b6 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/115-connection-pool.mdx @@ -0,0 +1,217 @@ +--- +title: Connection pool +metaDescription: Prisma's query engine creates a connection pool to store and manage database connections. +tocDepth: 4 +--- + + + +The query engine manages a **connection pool** of database connections. The pool is created when Prisma Client opens the _first_ connection to the database, which can happen in one of two ways: + +- By [explicitly calling `$connect()`](connection-management#connect) _or_ +- By running the first query, which calls `$connect()` under the hood + +Relational database connectors use Prisma's own connection pool, and the MongoDB connectors uses the [MongoDB driver connection pool](https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst). + + + +## Relational databases + +The relational database connectors use Prisma's connection pool. The connection pool has a **connection limit** and a **pool timeout**, which are controlled by connection URL parameters. + +### How the connection pool works + +The following steps describe how the query engine uses the connection pool: + +1. The query engine instantiates a connection pool with a [configurable pool size](#setting-the-connection-pool-size) and [pool timeout](#setting-the-connection-pool-timeout). +1. The query engine creates one connection and adds it to the connection pool. +1. When a query comes in, the query engine reserves a connection from the pool to process query. +1. If there are no idle connections available in the connection pool, the query engine opens additional database connections and adds them to the connection pool until the number of database connections reaches the limit defined by `connection_limit`. +1. If the query engine cannot reserve a connection from the pool, queries are added to a FIFO (First In First Out) queue in memory. FIFO means that queries are processed in the order they enter the queue. +1. If the query engine cannot process a query in the queue for **before the [time limit](#default-pool-timeout)**, it throws an exception with error code `P2024` for that query and moves on to the next one in the queue. + +If you consistently experience pool timeout errors, you need to [optimize the connection pool](/orm/prisma-client/setup-and-configuration/databases-connections#optimizing-the-connection-pool) . + +### Connection pool size + +#### Default connection pool size + +The default number of connections (pool size) is calculated with the following formula: + +```bash +num_physical_cpus * 2 + 1 +``` + +`num_physical_cpus` represents the number of physical CPUs on the machine your application is running on. If your machine has **four** physical CPUs, your connection pool will contain **nine** connections (`4 * 2 + 1 = 9`). + +Although the formula represents a good starting point, the [recommended connection limit](/orm/prisma-client/setup-and-configuration/databases-connections#recommended-connection-pool-size) also depends on your deployment paradigm - particularly if you are using serverless. + +#### Setting the connection pool size + +You can specify the number of connections by explicitly setting the `connection_limit` parameter in your database connection URL. For example, with the following `datasource` configuration in your [Prisma schema](/orm/prisma-schema) the connection pool will have exactly five connections: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5" +} +``` + +#### Viewing the connection pool size + +The number of connections Prisma Client uses can be viewed using [logging](/orm/prisma-client/observability-and-logging/logging) and [metrics](/orm/prisma-client/observability-and-logging/metrics). + +Using the `info` [logging level](/orm/reference/prisma-client-reference#log-levels), you can log the number of connections in a connection pool that are opened when Prisma Client is instantiated. + +For example, consider the following Prisma Client instance and invocation: + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ + log: ['info'], +}) + +async function main() { + await prisma.user.findMany() +} + +main() +``` + + + + +```text no-copy +prisma:info Starting a postgresql pool with 21 connections. +``` + + + + +When the `PrismaClient` class was instantiated, the logging notified `stdout` that a connection pool with 21 connections was started. + + + +Note that the output generated by `log: ['info']` can change in any release without notice. Be aware of this in case you are relying on the output in your application or a tool that you're building. + + + +If you need even more insights into the size of your connection pool and the amount of in-use and idle connection, you can use the [metrics](/orm/prisma-client/observability-and-logging/metrics) feature (which is currently in Preview). + +Consider the following example: + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + await Promise.all([prisma.user.findMany(), prisma.post.findMany()]) + + const metrics = await prisma.$metrics.json() + console.dir(metrics, { depth: Infinity }) +} + +main() +``` + + + + +```json no-copy +{ + "counters": [ + // ... + { + "key": "prisma_pool_connections_open", + "labels": {}, + "value": 2, + "description": "Number of currently open Pool Connections" + } + ], + "gauges": [ + // ... + { + "key": "prisma_pool_connections_busy", + "labels": {}, + "value": 0, + "description": "Number of currently busy Pool Connections (executing a datasource query)" + }, + { + "key": "prisma_pool_connections_idle", + "labels": {}, + "value": 21, + "description": "Number of currently unused Pool Connections (waiting for the next datasource query to run)" + }, + { + "key": "prisma_pool_connections_opened_total", + "labels": {}, + "value": 2, + "description": "Total number of Pool Connections opened" + } + ], + "histograms": [ + /** ... **/ + ] +} +``` + + + + + + +For more details on what is available in the metrics output, see the [About metrics](/orm/prisma-client/observability-and-logging/metrics#about-metrics) section. + + + +### Connection pool timeout + +#### Default pool timeout + +The default connection pool timeout is 10 seconds. If the Query Engine does not get a connection from the database connection pool within that time, it throws an exception and moves on to the next query in the queue. + +#### Setting the connection pool timeout + +You can specify the pool timeout by explicitly setting the `pool_timeout` parameter in your database connection URL. In the following example, the pool times out after `2` seconds: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=2" +} +``` + +#### Disabling the connection pool timeout + +You disable the connection pool timeout by setting the `pool_timeout` parameter to `0`: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=0" +} +``` + +You can choose to [disable the connection pool timeout if queries **must** remain in the queue](/orm/prisma-client/setup-and-configuration/databases-connections#disabling-the-pool-timeout) - for example, if you are importing a large number of records in parallel and are confident that the queue will not use up all available RAM before the job is complete. + +## MongoDB + +The MongoDB connector does not use the Prisma connection pool. The connection pool is managed internally by the MongoDB driver and [configured via connection string parameters](https://docs.mongodb.com/manual/reference/connection-string/#connection-pool-options). + +## External connection poolers + +You cannot increase the `connection_limit` beyond what the underlying database can support. This is a particular challenge in serverless environments, where each function manages an instance of `PrismaClient` - and its own connection pool. + +Consider introducing [an external connection pooler like PgBouncer](/orm/prisma-client/setup-and-configuration/databases-connections#pgbouncer) to prevent your application or functions from exhausting the database connection limit. + +## Manual database connection handling + +When using Prisma, the database connections are handled on an [engine](https://github.com/prisma/prisma-engines)-level. This means they're not exposed to the developer and it's not possible to manually access them. diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/200-pgbouncer.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/200-pgbouncer.mdx new file mode 100644 index 0000000000..18aa50bfce --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/200-pgbouncer.mdx @@ -0,0 +1,82 @@ +--- +title: Configure Prisma Client with PgBouncer +metaTitle: Configure Prisma Client with PgBouncer +--- + + + +An external connection pooler like PgBouncer holds a connection pool to the database, and proxies incoming client connections by sitting between Prisma Client and the database. This reduces the number of processes a database has to handle at any given time. + +Usually, this works transparently, but some connection poolers only support a limited set of functionality. One common feature that external connection poolers do not support are named prepared statements, which Prisma uses. For these cases, Prisma can be configured to behave differently. + + + +## PgBouncer + +### Set PgBouncer to transaction mode + +For Prisma Client to work reliably, PgBouncer must run in [**Transaction mode**](https://www.pgbouncer.org/features.html). + +Transaction mode offers a connection for every transaction – a requirement for the Prisma Client to work with PgBouncer. + +### Add `pgbouncer=true` to the connection URL + +To use Prisma Client with PgBouncer, add the `?pgbouncer=true` flag to the PostgreSQL connection URL: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE?pgbouncer=true +``` + +> Note: `PORT` specified for PgBouncer pooling is sometimes different from the default `5432` port. Check your database provider docs for the correct port number. + +
+ How `pgbouncer` mode works in Prisma + +- Prisma opens a transaction for every query – even when just reading data, allowing Prisma to use prepared statements. +- Prisma does not try to set the `search_path`, which is not supported by PgBouncer. +- Prisma cleans up already present prepared statements in the connection by running `DEALLOCATE ALL` before preparing and executing Prisma Client queries. +- Prisma also disables any prepared statement or type query caches. + +
+ +### Prisma Migrate and PgBouncer workaround + +Prisma Migrate uses **database transactions** to check out the current state of the database and the migrations table. However, the Schema Engine is designed to use a **single connection to the database**, and does not support connection pooling with PgBouncer. If you attempt to run Prisma Migrate commands in any environment that uses PgBouncer for connection pooling, you might see the following error: + +```bash +Error: undefined: Database error +Error querying the database: db error: ERROR: prepared statement "s0" already exists +``` + +To work around this issue, you must connect directly to the database rather than going through PgBouncer. To achieve this, you can use the [`directUrl`](/orm/reference/prisma-schema-reference#fields) field in your [`datasource`](/orm/reference/prisma-schema-reference#datasource) block. + +For example, consider the following `datasource` block: + +```prisma +datasource db { + provider = "postgresql" + url = "postgres://USER:PASSWORD@HOST:PORT/DATABASE?pgbouncer=true" + directUrl = "postgres://USER:PASSWORD@HOST:PORT/DATABASE" +} +``` + +The block above uses a PgBouncer connection string as the primary URL using `url`, allowing Prisma Client to take advantage of the PgBouncer connection pooler. + +It also provides a connection string directly to the database, without PgBouncer, using the `directUrl` field. This connection string will be used when commands that require a single connection to the database, such as `prisma migrate dev` or `prisma db push`, are invoked. + +### PgBouncer with different database providers + +There are sometimes minor differences in how to connect directly to a Postgres database that depend on the provider hosting the database. + +Below are links to information on how to set up these connections with providers who have setup steps not covered here in our documentation: + +- [Connecting directly to a PostgreSQL database hosted on Digital Ocean](https://github.com/prisma/prisma/issues/6157) +- [Connecting directly to a PostgreSQL database hosted on ScaleGrid](https://github.com/prisma/prisma/issues/6701#issuecomment-824387959) + +## Supabase Supavisor + +Supabase's Supavisor behaves similarly to [PgBouncer](#pgbouncer). You can add `?pgbouncer=true` to your connection pooled connection string available via your [Supabase database settings](https://supabase.com/dashboard/project/_/settings/database). + +## Other external connection poolers + +Although Prisma does not have explicit support for other connection poolers, if the limitations are similar to the ones of [PgBouncer](#pgbouncer) you can usually also use `pgbouncer=true` in your connection string to put Prisma in a mode that works with them as well. diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/index.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/index.mdx new file mode 100644 index 0000000000..856fe1e92e --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/index.mdx @@ -0,0 +1,337 @@ +--- +title: Database connections +metaTitle: Database connections +metaDescription: 'Databases connections' +tocDepth: 3 +--- + + + +Databases can handle a limited number of concurrent connections. Each connection requires RAM, which means that simply increasing the database connection limit without scaling available resources: + +- ✔ might allow more processes to connect _but_ +- ✘ significantly affects **database performance**, and can result in the database being **shut down** due to an out of memory error + +The way your application **manages connections** also impacts performance. This guide describes how to approach connection management in [serverless environments](#serverless-environments-faas) and [long-running processes](#long-running-processes). + + + +This guide focuses on **relational databases** and how to configure and tune the Prisma connection pool (MongoDB uses the MongoDB driver connection pool). + + + + + +## Long-running processes + +Examples of long-running processes include Node.js applications hosted on a service like Heroku or a virtual machine. Use the following checklist as a guide to connection management in long-running environments: + +- Start with the [recommended pool size (`connection_limit`)](#recommended-connection-pool-size) and [tune it](#optimizing-the-connection-pool) +- Make sure you have [**one** global instance of `PrismaClient`](#prismaclient-in-long-running-applications) + +### Recommended connection pool size + +The recommended connection pool size (`connection_limit`) to [start with](#optimizing-the-connection-pool) for long-running processes is the **default pool size** (`num_physical_cpus * 2 + 1`) ÷ **number of application instances**. + + + +`num_physical_cpus` refers to the the number of CPUs of the machine your application is running on. + + + +If you have **one** application instances: + +- The default pool size applies by default (`num_physical_cpus * 2 + 1`) - you do not need to set the `connection_limit` parameter. +- You can optionally [tune the pool size](#optimizing-the-connection-pool). + +If you have **multiple** application instances: + +- You must **manually** [set the `connection_limit` parameter](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool#setting-the-connection-pool-size) . For example, if your calculated pool size is _10_ and you have _2_ instances of your app, the `connection_limit` parameter should be **no more than _5_**. +- You can optionally [tune the pool size](#optimizing-the-connection-pool). + +### `PrismaClient` in long-running applications + +In **long-running** applications, we recommend that you: + +- ✔ Create **one** instance of `PrismaClient` and re-use it across your application +- ✔ Assign `PrismaClient` to a global variable _in dev environments only_ to [prevent hot reloading from creating new instances](#prevent-hot-reloading-from-creating-new-instances-of-prismaclient) + +#### Re-using a single `PrismaClient` instance + +To re-use a single instance, create a module that exports a `PrismaClient` object: + +```ts file=client.ts +import { PrismaClient } from '@prisma/client' + +let prisma = new PrismaClient() + +export default prisma +``` + +The object is [cached](https://nodejs.org/api/modules.html#modules_caching) the first time the module is imported. Subsequent requests return the cached object rather than creating a new `PrismaClient`: + +```ts file=app.ts +import prisma from './client' + +async function main() { + const allUsers = await prisma.user.findMany() +} + +main() +``` + +You do not have to replicate the example above exactly - the goal is to make sure `PrismaClient` is cached. For example, you can [instantiate `PrismaClient` in the `context` object](https://github.com/prisma/prisma-examples/blob/9f1a6b9e7c25b9e1851bd59b273046158d748995/typescript/graphql-express/src/context.ts#L9) that you [pass into an Express app](https://github.com/prisma/prisma-examples/blob/9f1a6b9e7c25b9e1851bd59b273046158d748995/typescript/graphql-express/src/server.ts#L12). + +#### Do not explicitly `$disconnect()` + +You [do not need to explicitly `$disconnect()`](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management#calling-disconnect-explicitly) in the context of a long-running application that is continuously serving requests. Opening a new connection takes time and can slow down your application if you disconnect after each query. + +#### Prevent hot reloading from creating new instances of `PrismaClient` + +Frameworks like [Next.js](https://nextjs.org/) support hot reloading of changed files, which enables you to see changes to your application without restarting. However, if the framework refreshes the module responsible for exporting `PrismaClient`, this can result in **additional, unwanted instances of `PrismaClient` in a development environment**. + +As a workaround, you can store `PrismaClient` as a global variable in development environments only, as global variables are not reloaded: + + +```ts file=client.ts +import { PrismaClient } from '@prisma/client' + +const globalForPrisma = globalThis as unknown as { prisma: PrismaClient } + +export const prisma = + globalForPrisma.prisma || new PrismaClient() + +if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma +``` + +The way that you import and use Prisma Client does not change: + +```ts file=app.ts +import { prisma } from './client' + +async function main() { + const allUsers = await prisma.user.findMany() +} + +main() +``` + +## Serverless environments (FaaS) + +Examples of serverless environments include Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions. Use the following checklist as a guide to connection management in serverless environments: + +- Familiarize yourself with the [serverless connection management challenge](#the-serverless-challenge) +- [Set pool size (`connection_limit`)](#recommended-connection-pool-size-1) based on whether you have an external connection pooler, and optionally [tune the pool size](#optimizing-the-connection-pool) +- [Instantiate `PrismaClient` outside the handler](#instantiate-prismaclient-outside-the-handler) and do not explicitly `$disconnect()` +- Configure [function concurrency](#concurrency-limits) and handle [idle connections](#zombie-connections) + +### The serverless challenge + +In a serverless environment, each function creates **its own instance** of `PrismaClient`, and each client instance has its own connection pool. + +Consider the following example, where a single AWS Lambda function uses `PrismaClient` to connect to a database. The `connection_limit` is **3**: + +![An AWS Lambda function connecting to a database.](./serverless-connections.png) + +A traffic spike causes AWS Lambda to spawn two additional lambdas to handle the increased load. Each lambda creates an instance of `PrismaClient`, each with a `connection_limit` of **3**, which results in a maximum of **9** connections to the database: + +![Three AWS Lambda function connecting to a database.](./serverless-connections-2.png) + +200 _concurrent functions_ (and therefore 600 possible connections) responding to a traffic spike 📈 can exhaust the database connection limit very quickly. Furthermore, any functions that are **paused** keep their connections open by default and block them from being used by another function. + +1. Start by [setting the `connection_limit` to `1`](#recommended-connection-pool-size-1) +2. If a smaller pool size is not enough, consider using an [external connection pooler like PgBouncer](#external-connection-poolers) + +### Recommended connection pool size + +The recommended pool size (`connection_limit`) in serverless environments depends on: + +- Whether you are using an [external connection pooler](#external-connection-poolers) +- Whether your functions are [designed to send queries in parallel](#optimizing-for-parallel-requests) + +#### Without an external connection pooler + +If you are **not** using an external connection pooler, _start_ by setting the pool size (`connection_limit`) to **1**, then [optimize](#optimizing-for-parallel-requests). Each incoming request starts a short-lived Node.js process, and many concurrent functions with a high `connection_limit` can quickly **exhaust the _database_ connection limit** during a traffic spike. + +The following example demonstrates how to set the `connection_limit` to 1 in your connection URL: + + + + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public&connection_limit=1 +``` + + + + +``` +mysql://USER:PASSWORD@HOST:PORT/DATABASE?connection_limit=1 +``` + + + + +:::tip + +If you are using AWS Lambda and _not_ configuring a `connection_limit`, refer to the following GitHub issue for information about the expected default pool size: https://github.com/prisma/docs/issues/667 + +::: + +#### With an external connection pooler + +If you are using an external connection pooler, use the default pool size (`num_physical_cpus * 2 + 1`) as a starting point and then [tune the pool size](#optimizing-the-connection-pool). The external connection pooler should prevent a traffic spike from overwhelming the database. + +#### Optimizing for parallel requests + +If you rarely or never exceed the database connection limit with the pool size set to 1, you can further optimize the connection pool size. Consider a function that sends queries in parallel: + +```ts +Promise.all() { + query1, + query2, + query3 + query4, + ... +} +``` + +If the `connection_limit` is 1, this function is forced to send queries **serially** (one after the other) rather than **in parallel**. This slows down the function's ability to process requests, and may result in pool timeout errors. Tune the `connection_limit` parameter until a traffic spike: + +- Does not exhaust the database connection limit +- Does not result in pool timeout errors + +### `PrismaClient` in serverless environments + +#### Instantiate `PrismaClient` outside the handler + +Instantiate `PrismaClient` [outside the scope of the function handler](https://github.com/prisma/e2e-tests/blob/5d1041d3f19245d3d237d959eca94d1d796e3a52/platforms/serverless-lambda/index.ts#L3) to increase the chances of reuse. As long as the handler remains 'warm' (in use), the connection is potentially reusable: + +```ts highlight=3;normal +import { PrismaClient } from '@prisma/client' + +const client = new PrismaClient() + +export async function handler() { + /* ... */ +} +``` + +#### Do not explicitly `$disconnect()` + +You [do not need to explicitly `$disconnect()`](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management#calling-disconnect-explicitly) at the end of a function, as there is a possibility that the container might be reused. Opening a new connection takes time and slows down your function's ability to process requests. + +### Other serverless considerations + +#### Container reuse + +There is no guarantee that subsequent nearby invocations of a function will hit the same container - for example, AWS can choose to create a new container at any time. + +Code should assume the container to be stateless and create a connection only if it does not exist - Prisma Client JS already implements this logic. + +#### Zombie connections + +Containers that are marked "to be removed" and are not being reused still **keep a connection open** and can stay in that state for some time (unknown and not documented from AWS). This can lead to sub-optimal utilization of the database connections. + +A potential solution is to **clean up idle connections** ([`serverless-mysql`](https://github.com/jeremydaly/serverless-mysql) implements this idea, but cannot be used with Prisma). + +#### Concurrency limits + +Depending on your serverless concurrency limit (the number of serverless functions running in parallel), you might still exhaust your database's connection limit. This can happen when too many functions are invoked concurrently, each with its own connection pool, which eventually exhausts the database connection limit. To prevent this, you can [set your serverless concurrency limit](https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html) to a number lower than the maximum connection limit of your database divided by the number of connections used by each function invocation (as you might want to be able to connect from another client for other purposes). + +## Optimizing the connection pool + +If the query engine cannot [process a query in the queue before the time limit](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool#how-the-connection-pool-works) , you will see connection pool timeout exceptions in your log. A connection pool timeout can occur if: + +- Many users are accessing your app simultaneously +- You send a large number of queries in parallel (for example, using `await Promise.all()`) + +If you consistently experience connection pool timeouts after configuring the recommended pool size, you can further tune the `connection_limit` and `pool_timeout` parameters. + +### Increasing the pool size + +Increasing the pool size allows the query engine to process a larger number of queries in parallel. Be aware that your database must be able to support the increased number of concurrent connections, otherwise you will **exhaust the database connection limit**. + +To increase the pool size, manually set the `connection_limit` to a higher number: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public&connection_limit=40" +} +``` + +> **Note**: Setting the `connection_limit` to 1 in serverless environments is a recommended starting point, but [this value can also be tuned](#optimizing-for-parallel-requests). + +### Increasing the pool timeout + +Increasing the pool timeout gives the query engine more time to process queries in the queue. You might consider this approach in the following scenario: + +- You have already increased the `connection_limit`. +- You are confident that the queue will not grow beyond a certain size, otherwise **you will eventually run out of RAM**. + +To increase the pool timeout, set the `pool_timeout` parameter to a value larger than the default (10 seconds): + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=20" +} +``` + +### Disabling the pool timeout + +Disabling the pool timeout prevents the query engine from throwing an exception after x seconds of waiting for a connection and allows the queue to build up. You might consider this approach in the following scenario: + +- You are submitting a large number of queries for a limited time - for example, as part of a job to import or update every customer in your database. +- You have already increased the `connection_limit`. +- You are confident that the queue will not grow beyond a certain size, otherwise **you will eventually run out of RAM**. + +To disable the pool timeout, set the `pool_timeout` parameter to `0`: + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=0" +} +``` + +## External connection poolers + +Connection poolers like [Prisma Accelerate](/accelerate) and PgBouncer prevent your application from exhausting the database's connection limit. + +If you would like to use the Prisma CLI in order to perform other actions on your database ,e.g. migrations and introspection, you will need to add an environment variable that provides a direct connection to your database in the `datasource.directUrl` property in your Prisma schema: + +```env file=.env highlight=4,5;add +# Connection URL to your database using PgBouncer. +DATABASE_URL="postgres://root:password@127.0.0.1:54321/postgres?pgbouncer=true" + +# Direct connection URL to the database used for migrations +DIRECT_URL="postgres://root:password@127.0.0.1:5432/postgres" +``` + +You can then update your `schema.prisma` to use the new direct URL: + +```prisma file=schema.prisma highlight=4;add +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_URL") +} +``` + +More information about the `directUrl` field can be found [here](/orm/reference/prisma-schema-reference#fields). + +### Prisma Accelerate + +[Prisma Accelerate](/accelerate) is a managed external connection pooler built by Prisma that is integrated in the [Prisma Data Platform](/platform) and handles connection pooling for you. + +### PgBouncer + +PostgreSQL only supports a certain amount of concurrent connections, and this limit can be reached quite fast when the service usage goes up – especially in [serverless environments](#serverless-environments-faas). + +[PgBouncer](https://www.pgbouncer.org/) holds a connection pool to the database and proxies incoming client connections by sitting between Prisma Client and the database. This reduces the number of processes a database has to handle at any given time. PgBouncer passes on a limited number of connections to the database and queues additional connections for delivery when connections becomes available. To use PgBouncer, see [Configure Prisma Client with PgBouncer](/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer). + +### AWS RDS Proxy + +Due to the way AWS RDS Proxy pins connections, [it does not provide any connection pooling benefits](/orm/prisma-client/deployment/caveats-when-deploying-to-aws-platforms#aws-rds-proxy) when used together with Prisma Client. diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections-2.png b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections-2.png new file mode 100644 index 0000000000..a933db1341 Binary files /dev/null and b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections-2.png differ diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections.png b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections.png new file mode 100644 index 0000000000..6c4b56ea9b Binary files /dev/null and b/docs/200-orm/200-prisma-client/000-setup-and-configuration/050-databases-connections/serverless-connections.png differ diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/100-custom-model-and-field-names.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/100-custom-model-and-field-names.mdx new file mode 100644 index 0000000000..a60eb98ed9 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/100-custom-model-and-field-names.mdx @@ -0,0 +1,310 @@ +--- +title: 'Custom model and field names' +metaTitle: 'Custom model and field names' +metaDescription: 'Learn how you can decouple the naming of Prisma models from database tables to improve the ergonomics of the generated Prisma Client API.' +--- + + + +The Prisma Client API is generated based on the models in your [Prisma schema](/orm/prisma-schema). Models are _typically_ 1:1 mappings of your database tables. + +In some cases, especially when using [introspection](/orm/prisma-schema/introspection), it might be useful to _decouple_ the naming of database tables and columns from the names that are used in your Prisma Client API. This can be done via the [`@map` and `@@map`](/orm/prisma-schema/data-model/models#mapping-model-names-to-tables-or-collections) attributes in your Prisma schema. + +You can use `@map` and `@@map` to rename MongoDB fields and collections respectively. This page uses a relational database example. + + + +## Example: Relational database + +Assume you have a PostgreSQL relational database schema looking similar to this: + +```sql +CREATE TABLE users ( + user_id SERIAL PRIMARY KEY NOT NULL, + name VARCHAR(256), + email VARCHAR(256) UNIQUE NOT NULL +); +CREATE TABLE posts ( + post_id SERIAL PRIMARY KEY NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + title VARCHAR(256) NOT NULL, + content TEXT, + author_id INTEGER REFERENCES users(user_id) +); +CREATE TABLE profiles ( + profile_id SERIAL PRIMARY KEY NOT NULL, + bio TEXT, + user_id INTEGER NOT NULL UNIQUE REFERENCES users(user_id) +); +CREATE TABLE categories ( + category_id SERIAL PRIMARY KEY NOT NULL, + name VARCHAR(256) +); +CREATE TABLE post_in_categories ( + post_id INTEGER NOT NULL REFERENCES posts(post_id), + category_id INTEGER NOT NULL REFERENCES categories(category_id) +); +CREATE UNIQUE INDEX post_id_category_id_unique ON post_in_categories(post_id int4_ops,category_id int4_ops); +``` + +When introspecting a database with that schema, you'll get a Prisma schema looking similar to this: + +```prisma +model categories { + category_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + post_in_categories post_in_categories[] +} + +model post_in_categories { + post_id Int + category_id Int + categories categories @relation(fields: [category_id], references: [category_id], onDelete: NoAction, onUpdate: NoAction) + posts posts @relation(fields: [post_id], references: [post_id], onDelete: NoAction, onUpdate: NoAction) + + @@unique([post_id, category_id], map: "post_id_category_id_unique") +} + +model posts { + post_id Int @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + title String @db.VarChar(256) + content String? + author_id Int? + users users? @relation(fields: [author_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction) + post_in_categories post_in_categories[] +} + +model profiles { + profile_id Int @id @default(autoincrement()) + bio String? + user_id Int @unique + users users @relation(fields: [user_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction) +} + +model users { + user_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + email String @unique @db.VarChar(256) + posts posts[] + profiles profiles? +} +``` + +There are a few "issues" with this Prisma schema when the Prisma Client API is generated: + +**Adhering to Prisma's naming conventions** + +Prisma has a [naming convention](/orm/reference/prisma-schema-reference#naming-conventions) of **camelCasing** and using the **singular form** for Prisma models. If these naming conventions are not met, the Prisma schema can become harder to interpret and the generated Prisma Client API will feel less natural. Consider the following, generated model: + +```prisma +model users { + user_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + email String @unique @db.VarChar(256) + posts posts[] + profiles profiles? +} +``` + +Although `profiles` refers to a 1:1 relation, its type is currently called `profiles` in plural, suggesting that there might be many `profiles` in this relation. With Prisma conventions, the models and fields were _ideally_ named as follows: + +```prisma +model User { + user_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + email String @unique @db.VarChar(256) + posts Post[] + profile Profile? +} +``` + +Because these fields are "Prisma-level" [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) that do not manifest you can manually rename them in your Prisma schema. + +**Naming of annotated relation fields** + +Foreign keys are represented as a combination of a [annotated relation fields](/orm/prisma-schema/data-model/relations#relation-fields) and its corresponding relation scalar field in the Prisma schema. Here's how all the relations from the SQL schema are currently represented: + +```prisma +model categories { + category_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + post_in_categories post_in_categories[] // virtual relation field +} + +model post_in_categories { + post_id Int // relation scalar field + category_id Int // relation scalar field + categories categories @relation(fields: [category_id], references: [category_id], onDelete: NoAction, onUpdate: NoAction) // virtual relation field + posts posts @relation(fields: [post_id], references: [post_id], onDelete: NoAction, onUpdate: NoAction) + + @@unique([post_id, category_id], map: "post_id_category_id_unique") +} + +model posts { + post_id Int @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + title String @db.VarChar(256) + content String? + author_id Int? + users users? @relation(fields: [author_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction) + post_in_categories post_in_categories[] +} + +model profiles { + profile_id Int @id @default(autoincrement()) + bio String? + user_id Int @unique + users users @relation(fields: [user_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction) +} + +model users { + user_id Int @id @default(autoincrement()) + name String? @db.VarChar(256) + email String @unique @db.VarChar(256) + posts posts[] + profiles profiles? +} +``` + +## Using `@map` and `@@map` to rename fields and models in the Prisma Client API + +You can "rename" fields and models that are used in Prisma Client by mapping them to the "original" names in the database using the `@map` and `@@map` attributes. For the example above, you could e.g. annotate your models as follows. + +_After_ you introspected your database with `prisma db pull`, you can manually adjust the resulting Prisma schema as follows: + +```prisma +model Category { + id Int @id @default(autoincrement()) @map("category_id") + name String? @db.VarChar(256) + post_in_categories PostInCategories[] + + @@map("categories") +} + +model PostInCategories { + post_id Int + category_id Int + categories Category @relation(fields: [category_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + posts Post @relation(fields: [post_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + + @@unique([post_id, category_id], map: "post_id_category_id_unique") + @@map("post_in_categories") +} + +model Post { + id Int @id @default(autoincrement()) @map("post_id") + created_at DateTime? @default(now()) @db.Timestamptz(6) + title String @db.VarChar(256) + content String? + author_id Int? + users User? @relation(fields: [author_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + post_in_categories PostInCategories[] + + @@map("posts") +} + +model Profile { + id Int @id @default(autoincrement()) @map("profile_id") + bio String? + user_id Int @unique + users User @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + + @@map("profiles") +} + +model User { + id Int @id @default(autoincrement()) @map("user_id") + name String? @db.VarChar(256) + email String @unique @db.VarChar(256) + posts Post[] + profiles Profile? + + @@map("users") +} +``` + +With these changes, you're now adhering to Prisma's naming conventions and the generated Prisma Client API feels more "natural": + +```ts +// Nested writes +const profile = await prisma.profile.create({ + data: { + bio: 'Hello World', + users: { + create: { + name: 'Alice', + email: 'alice@prisma.io', + }, + }, + }, +}) + +// Fluent API +const userByProfile = await prisma.profile + .findUnique({ + where: { id: 1 }, + }) + .users() +``` + +## Renaming relation fields + +Prisma-level [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) (sometimes referred to as "virtual relation fields") only exist in the Prisma schema, but do not actually manifest in the underlying database. You can therefore name these fields whatever you want. + +Consider the following example of an ambiguous relation in a SQL database: + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + "author" integer NOT NULL, + "favoritedBy" INTEGER, + FOREIGN KEY ("author") REFERENCES "User"(id), + FOREIGN KEY ("favoritedBy") REFERENCES "User"(id) +); +``` + +Prisma's introspection will output the following Prisma schema: + +```prisma +model Post { + id Int @id @default(autoincrement()) + author Int + favoritedBy Int? + User_Post_authorToUser User @relation("Post_authorToUser", fields: [author], references: [id], onDelete: NoAction, onUpdate: NoAction) + User_Post_favoritedByToUser User? @relation("Post_favoritedByToUser", fields: [favoritedBy], references: [id], onDelete: NoAction, onUpdate: NoAction) +} + +model User { + id Int @id @default(autoincrement()) + Post_Post_authorToUser Post[] @relation("Post_authorToUser") + Post_Post_favoritedByToUser Post[] @relation("Post_favoritedByToUser") +} +``` + +Because the names of the virtual relation fields `Post_Post_authorToUser` and `Post_Post_favoritedByToUser` are based on the generated relation names, they don't look very friendly in the Prisma Client API. In that case, you can rename the relation fields. For example: + +```prisma highlight=11-12;edit +model Post { + id Int @id @default(autoincrement()) + author Int + favoritedBy Int? + User_Post_authorToUser User @relation("Post_authorToUser", fields: [author], references: [id], onDelete: NoAction, onUpdate: NoAction) + User_Post_favoritedByToUser User? @relation("Post_favoritedByToUser", fields: [favoritedBy], references: [id], onDelete: NoAction, onUpdate: NoAction) +} + +model User { + id Int @id @default(autoincrement()) + writtenPosts Post[] @relation("Post_authorToUser") + favoritedPosts Post[] @relation("Post_favoritedByToUser") +} +``` + + + +`prisma db pull` preserves custom relation fields defined in your Prisma schema on re-introspecting your database. + + diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/150-error-formatting.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/150-error-formatting.mdx new file mode 100644 index 0000000000..c880444b25 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/150-error-formatting.mdx @@ -0,0 +1,41 @@ +--- +title: 'Configuring error formatting' +metaTitle: 'Configuring error formatting (Concepts)' +metaDescription: 'This page explains how to configure the formatting of errors when using Prisma Client.' +--- + + + +By default, Prisma Client uses [ANSI escape characters](https://en.wikipedia.org/wiki/ANSI_escape_code) to pretty print the error stack and give recommendations on how to fix a problem. While this is very useful when using Prisma Client from the terminal, in contexts like a GraphQL API, you only want the minimal error without any additional formatting. + +This page explains how error formatting can be configured with Prisma Client. + + + +## Formatting levels + +There are 3 error formatting levels: + +1. **Pretty Error** (default): Includes a full stack trace with colors, syntax highlighting of the code and extended error message with a possible solution for the problem. +2. **Colorless Error**: Same as pretty errors, just without colors. +3. **Minimal Error**: The raw error message. + +In order to configure these different error formatting levels, there are two options: + +- Setting the config options via environment variables +- Providing the config options to the `PrismaClient` constructor + +## Formatting via environment variables + +- [`NO_COLOR`](/orm/reference/environment-variables-reference#no_color): If this env var is provided, colors are stripped from the error messages. Therefore you end up with a **colorless error**. The `NO_COLOR` environment variable is a standard described [here](https://no-color.org/). +- `NODE_ENV=production`: If the env var `NODE_ENV` is set to `production`, only the **minimal error** will be printed. This allows for easier digestion of logs in production environments. + +### Formatting via the `PrismaClient` constructor + +Alternatively, use the `PrismaClient` [`errorFormat`](/orm/reference/prisma-client-reference#errorformat) parameter to set the error format: + +```ts +const prisma = new PrismaClient({ + errorFormat: 'pretty', +}) +``` diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/200-read-replicas.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/200-read-replicas.mdx new file mode 100644 index 0000000000..0d73fbb82b --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/200-read-replicas.mdx @@ -0,0 +1,83 @@ +--- +title: 'Read replicas' +metaTitle: 'Read replicas' +metaDescription: 'Learn how to set up and use read replicas with Prisma Client' +tocDepth: 3 +--- + + + +Read replicas enable you to distribute workloads across database replicas for high-traffic workloads. The [read replicas extension](https://github.com/prisma/extension-read-replicas), `@prisma/extension-read-replicas`, adds support for read-only database replicas to Prisma Client. + +The read replicas extension supports Prisma versions [5.2.0](https://github.com/prisma/prisma/releases/tag/5.2.0) and higher. If you run into a bug or have feedback, create a GitHub issue [here](https://github.com/prisma/extension-read-replicas/issues/new). + + + +## Setup the read replicas extension + +Install the extension: + +```terminal +npm install @prisma/extension-read-replicas +``` + +Initialize the extension by extending your Prisma Client instance and provide the extension a connection string that points to your read replica in the `url` option of the extension. + + + +```ts +import { PrismaClient } from '@prisma/client' +import { readReplicas } from '@prisma/extension-read-replicas' + +const prisma = new PrismaClient().$extends( + readReplicas({ + url: process.env.DATABASE_URL_REPLICA, + }) +) + +// Query is run against the database replica +await prisma.post.findMany() + +// Query is run against the primary database +await prisma.post.create({ + data: {/** */}, +}) +``` + + +All read operations, e.g. `findMany`, will be executed against the database replica with the above setup. All write operations — e.g. `create`, `update` — and `$transaction` queries, will be executed against your primary database. + +If you run into a bug or have feedback, create a GitHub issue [here](https://github.com/prisma/extension-read-replicas/issues/new). + +## Configure multiple database replicas + +The `url` property also accepts an array of values, i.e. an array of all your database replicas you would like to configure: + +```ts +const prisma = new PrismaClient().$extends( + readReplicas({ + url: [ + process.env.DATABASE_URL_REPLICA_1, + process.env.DATABASE_URL_REPLICA_2, + ], + }) +) +``` + +If you have more than one read replica configured, a database replica will be randomly selected to execute your query. + +## Executing read operations against your primary database + +You can use the `$primary()` method to explicitly execute a read operation against your primary database: + +```ts +const posts = await prisma.$primary().post.findMany() +``` + +## Executing operations against a database replica + +You can use the `$replica()` method to explicitly execute your query against a replica instead of your primary database: + +```ts +const result = await prisma.$replica().$queryRaw`SELECT ...` +``` diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/220-database-polyfills.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/220-database-polyfills.mdx new file mode 100644 index 0000000000..9cfe6e692e --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/220-database-polyfills.mdx @@ -0,0 +1,22 @@ +--- +title: 'Database polyfills' +metaTitle: 'Database polyfills (Concepts)' +metaDescription: 'Prisma Client provides features that are not achievable with relational databases. These features are referred to as "polyfills" and explained on this page.' +--- + + + +Prisma Client provides features that are typically either not achievable with particular databases or require extensions. These features are referred to as _polyfills_. For all databases, this includes: + +- Initializing [ID](/orm/prisma-schema/data-model/models#defining-an-id-field) values with `cuid` and `uuid` values +- Using [`@updatedAt`](/orm/prisma-schema/data-model/models#defining-attributes) to store the time when a record was last updated + +For relational databases, this includes: + +- [Implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) + +For MongoDB, this includes: + +- [Relations in general](/orm/prisma-schema/data-model/relations) - foreign key relations between documents are not enforced in MongoDB + + diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/index.mdx b/docs/200-orm/200-prisma-client/000-setup-and-configuration/index.mdx new file mode 100644 index 0000000000..c1c3f53673 --- /dev/null +++ b/docs/200-orm/200-prisma-client/000-setup-and-configuration/index.mdx @@ -0,0 +1,15 @@ +--- +title: 'Setup & configuration' +metaTitle: 'Setup & configuration' +metaDescription: 'This section explains how to generate, configure, and instantiate Prisma Client, as well as when and how to manage database connections.' +--- + + + +This section describes how to set up, generate, configure, and instantiate `PrismaClient` , as well as when and how to actively [manage connections](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management). + + + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-generation-workflow.png b/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-generation-workflow.png new file mode 100644 index 0000000000..1faf99964c Binary files /dev/null and b/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-generation-workflow.png differ diff --git a/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-node-module.png b/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-node-module.png new file mode 100644 index 0000000000..890f494d68 Binary files /dev/null and b/docs/200-orm/200-prisma-client/000-setup-and-configuration/prisma-client-node-module.png differ diff --git a/docs/200-orm/200-prisma-client/100-queries/030-crud.mdx b/docs/200-orm/200-prisma-client/100-queries/030-crud.mdx new file mode 100644 index 0000000000..e89a2c2ba6 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/030-crud.mdx @@ -0,0 +1,996 @@ +--- +title: 'CRUD' +metaTitle: 'CRUD (Reference)' +metaDescription: 'How to perform CRUD with Prisma Client.' +tocDepth: 4 +--- + + + +This page describes how to perform CRUD operations with your generated Prisma Client API. CRUD is an acronym that stands for: + +- [Create](#create) +- [Read](#read) +- [Update](#update) +- [Delete](#delete) + +Refer to the [Prisma Client API reference documentation](/orm/reference/prisma-client-reference) for detailed explanations of each method. + + + +## Example schema + +All examples are based on the following schema: + +
+ +Expand for sample schema + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model ExtendedProfile { + id Int @id @default(autoincrement()) + biography String + user User @relation(fields: [userId], references: [id]) + userId Int @unique +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + profileViews Int @default(0) + role Role @default(USER) + coinflips Boolean[] + posts Post[] + profile ExtendedProfile? +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId Int + comments Json? + views Int @default(0) + likes Int @default(0) + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + name String @unique + posts Post[] +} + +enum Role { + USER + ADMIN +} +``` + + + + +```prisma +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model ExtendedProfile { + id String @id @default(auto()) @map("_id") @db.ObjectId + biography String + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + email String @unique + profileViews Int @default(0) + role Role @default(USER) + coinflips Boolean[] + posts Post[] + profile ExtendedProfile? +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + comments Json? + views Int @default(0) + likes Int @default(0) + categories Category[] +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String @unique + posts Post[] +} + +enum Role { + USER + ADMIN +} +``` + + + + +
+ +For **relational databases**, use `db push` command to push the example schema to your own database + +```terminal +npx prisma db push +``` + +For **MongoDB**, ensure your data is in a uniform shape and matches the model defined in the Prisma schema. + +## Create + +### Create a single record + +The following query creates ([`create`](/orm/reference/prisma-client-reference#create) ) a single user with two fields: + + + + +```ts +const user = await prisma.user.create({ + data: { + email: 'elsa@prisma.io', + name: 'Elsa Prisma', + }, +}) +``` + + + + +```js no-copy +{ + id: 22, + name: 'Elsa Prisma', + email: 'elsa@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [] +} +``` + + + + +The user's `id` is auto-generated, and your schema determines [which fields are mandatory](/orm/prisma-schema/data-model/models#optional-and-mandatory-fields). + +#### Create a single record using generated types + +The following example produces an identical result, but creates a `UserCreateInput` variable named `user` _outside_ the context of the `create` query. After completing a simple check (should posts be included in this `create` query?), the `user` variable is passed into the query: + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + let includePosts: boolean = false + let user: Prisma.UserCreateInput + + // Check if posts should be included in the query + if (includePosts) { + user = { + email: 'elsa@prisma.io', + name: 'Elsa Prisma', + posts: { + create: { + title: 'Include this post!', + }, + }, + } + } else { + user = { + email: 'elsa@prisma.io', + name: 'Elsa Prisma', + } + } + + // Pass 'user' object into query + const createUser = await prisma.user.create({ data: user }) +} + +main() +``` + +For more information about working with generated types, see: [Generated types](/orm/prisma-client/type-safety). + +### Create multiple records + +Prisma Client supports bulk inserts as a GA feature in [2.20.0](https://github.com/prisma/prisma/releases/2.20.0) and later. + +The following [`createMany`](/orm/reference/prisma-client-reference#createmany) query creates multiple users and skips any duplicates (`email` must be unique): + + + + +```ts +const createMany = await prisma.user.createMany({ + data: [ + { name: 'Bob', email: 'bob@prisma.io' }, + { name: 'Bobo', email: 'bob@prisma.io' }, // Duplicate unique key! + { name: 'Yewande', email: 'yewande@prisma.io' }, + { name: 'Angelique', email: 'angelique@prisma.io' }, + ], + skipDuplicates: true, // Skip 'Bobo' +}) +``` + + + + +```js no-copy +{ + count: 3 +} +``` + + + + + + + +Note `skipDuplicates` is not supported when using MongoDB or SQLServer. + + + +`createMany` uses a single `INSERT INTO` statement with multiple values, which is generally more efficient than a separate `INSERT` per row: + +```sql +BEGIN +INSERT INTO "public"."User" ("id","name","email","profileViews","role","coinflips","testing","city","country") VALUES (DEFAULT,$1,$2,$3,$4,DEFAULT,DEFAULT,DEFAULT,$5), (DEFAULT,$6,$7,$8,$9,DEFAULT,DEFAULT,DEFAULT,$10), (DEFAULT,$11,$12,$13,$14,DEFAULT,DEFAULT,DEFAULT,$15), (DEFAULT,$16,$17,$18,$19,DEFAULT,DEFAULT,DEFAULT,$20) ON CONFLICT DO NOTHING +COMMIT +SELECT "public"."User"."country", "public"."User"."city", "public"."User"."email", SUM("public"."User"."profileViews"), COUNT(*) FROM "public"."User" WHERE 1=1 GROUP BY "public"."User"."country", "public"."User"."city", "public"."User"."email" HAVING AVG("public"."User"."profileViews") >= $1 ORDER BY "public"."User"."country" ASC OFFSET $2 +``` + +> **Note**: Multiple `create` statements inside a `$transaction` results in multiple `INSERT` statements. + +The following video demonstrates how to use `createMany` and [faker.js](https://github.com/faker-js/faker/) to seed a database with sample data: + +
+ +
+ +### Create records and connect or create related records + +See [Working with relations > Nested writes](relation-queries#nested-writes) for information about creating a record and one or more related records at the same time. + +## Read + +### Get record by ID or unique identifier + +The following queries return a single record ([`findUnique`](/orm/reference/prisma-client-reference#findunique) ) by unique identifier or ID: + +```ts +// By unique identifier +const user = await prisma.user.findUnique({ + where: { + email: 'elsa@prisma.io', + }, +}) + +// By ID +const user = await prisma.user.findUnique({ + where: { + id: 99, + }, +}) +``` + +If you are using the MongoDB connector and your underlying ID type is `ObjectId`, you can use the string representation of that `ObjectId`: + +```ts +// By ID +const user = await prisma.user.findUnique({ + where: { + id: '60d5922d00581b8f0062e3a8', + }, +}) +``` + +### Get all records + +The following [`findMany`](/orm/reference/prisma-client-reference#findmany) query returns _all_ `User` records: + +```ts +const users = await prisma.user.findMany() +``` + +You can also [paginate your results](pagination). + +### Get the first record that matches a specific criteria + +The following [`findFirst`](/orm/reference/prisma-client-reference#findfirst) query returns the _most recently created user_ with at least one post that has more than 100 likes: + +1. Order users by descending ID (largest first) - the largest ID is the most recent +2. Return the first user in descending order with at least one post that has more than 100 likes + +```ts +const findUser = await prisma.user.findFirst({ + where: { + posts: { + some: { + likes: { + gt: 100, + }, + }, + }, + }, + orderBy: { + id: 'desc', + }, +}) +``` + +### Get a filtered list of records + +Prisma Client supports [filtering](filtering-and-sorting) on record fields and related record fields. + +#### Filter by a single field value + +The following query returns all `User` records with an email that ends in `"prisma.io"`: + +```ts +const users = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + }, + }, +}) +``` + +#### Filter by multiple field values + +The following query uses a combination of [operators](/orm/reference/prisma-client-reference#filter-conditions-and-operators) to return users whose name start with `E` _or_ administrators with at least 1 profile view: + +```ts +const users = await prisma.user.findMany({ + where: { + OR: [ + { + name: { + startsWith: 'E', + }, + }, + { + AND: { + profileViews: { + gt: 0, + }, + role: { + equals: 'ADMIN', + }, + }, + }, + ], + }, +}) +``` + +#### Filter by related record field values + +The following query returns users with an email that ends with `prisma.io` _and_ have at least _one_ post (`some`) that is not published: + +```ts +const users = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + }, + posts: { + some: { + published: false, + }, + }, + }, +}) +``` + +See [Working with relations](relation-queries) for more examples of filtering on related field values. + +### Select a subset of fields + +The following `findUnique` query uses `select` to return the `email` and `name` fields of a specific `User` record: + + + + +```ts +const user = await prisma.user.findUnique({ + where: { + email: 'emma@prisma.io', + }, + select: { + email: true, + name: true, + }, +}) +``` + + + + +```js no-copy +{ email: 'emma@prisma.io', name: "Emma" } +``` + + + + +For more information about including relations, refer to: + +- [Select fields](select-fields) +- [Relation queries](relation-queries) + +#### Select a subset of related record fields + +The following query uses a nested `select` to return: + +- The user's `email` +- The `likes` field of each post + + + + +```ts +const user = await prisma.user.findUnique({ + where: { + email: 'emma@prisma.io', + }, + select: { + email: true, + posts: { + select: { + likes: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ email: 'emma@prisma.io', posts: [ { likes: 0 }, { likes: 0 } ] } +``` + + + + +For more information about including relations, see [Select fields and include relations](select-fields). + +### Select distinct field values + +See [Select `distinct`](aggregation-grouping-summarizing#select-distinct) for information about selecting distinct field values. + +### Include related records + +The following query returns all `ADMIN` users and includes each user's posts in the result: + + + + +```ts +const users = await prisma.user.findMany({ + where: { + role: 'ADMIN', + }, + include: { + posts: true, + }, +}) +``` + + + + +```js no-copy +{ + "id": 38, + "name": "Maria", + "email": "maria@prisma.io", + "profileViews": 20, + "role": "ADMIN", + "coinflips": [ + true, + false, + false + ], + "posts": [] +}, +{ + "id": 39, + "name": "Oni", + "email": "oni2@prisma.io", + "profileViews": 20, + "role": "ADMIN", + "coinflips": [ + true, + false, + false + ], + "posts": [ + { + "id": 25, + "authorId": 39, + "title": "My awesome post", + "published": true, + "comments": null, + "views": 0, + "likes": 0 + } + ] +} +``` + + + + +For more information about including relations, see [Select fields and include relations](select-fields). + +#### Include a filtered list of relations + +See [Working with relations](relation-queries#filter-a-list-of-relations) to find out how to combine [`include`](/orm/reference/prisma-client-reference#include) and `where` for a filtered list of relations - for example, only include a user's published posts. + +## Update + +### Update a single record + +The following query uses [`update`](/orm/reference/prisma-client-reference#update) to find and update a single `User` record by `email`: + + + + +```ts +const updateUser = await prisma.user.update({ + where: { + email: 'viola@prisma.io', + }, + data: { + name: 'Viola the Magnificent', + }, +}) +``` + + + + +```js no-copy +{ + "id": 43, + "name": "Viola the Magnificent", + "email": "viola@prisma.io", + "profileViews": 0, + "role": "USER", + "coinflips": [], +} +``` + + + + +### Update multiple records + +The following query uses [`updateMany`](/orm/reference/prisma-client-reference#updatemany) to update all `User` records that contain `prisma.io`: + + + + +```ts +const updateUsers = await prisma.user.updateMany({ + where: { + email: { + contains: 'prisma.io', + }, + }, + data: { + role: 'ADMIN', + }, +}) +``` + + + + +```js no-copy +{ + "count": 19 +} +``` + + + + +### Update _or_ create records + +The following query uses [`upsert`](/orm/reference/prisma-client-reference#upsert) to update a `User` record with a specific email address, or create that `User` record if it does not exist: + + + + +```ts +const upsertUser = await prisma.user.upsert({ + where: { + email: 'viola@prisma.io', + }, + update: { + name: 'Viola the Magnificent', + }, + create: { + email: 'viola@prisma.io', + name: 'Viola the Magnificent', + }, +}) +``` + + + + +```js no-copy +{ + "id": 43, + "name": "Viola the Magnificent", + "email": "viola@prisma.io", + "profileViews": 0, + "role": "ADMIN", + "coinflips": [], +} +``` + + + + + + +From version 4.6.0, Prisma carries out upserts with database native SQL commands where possible. [Learn more](/orm/reference/prisma-client-reference#database-upserts). + + + +Prisma does not have a `findOrCreate` query. You can use `upsert` as a workaround. To make `upsert` behave like a `findOrCreate` method, provide an empty `update` parameter to `upsert`. + + + +A limitation to using `upsert` as a workaround for `findOrCreate` is that `upsert` will only accept unique model fields in the `where` condition. So it's not possible to use `upsert` to emulate `findOrCreate` if the `where` condition contains non-unique fields. + + + +### Update a number field + +Use [atomic number operations](/orm/reference/prisma-client-reference#atomic-number-operations) to update a number field **based on its current value** - for example, increment or multiply. The following query increments the `views` and `likes` fields by `1`: + +```ts +const updatePosts = await prisma.post.updateMany({ + data: { + views: { + increment: 1, + }, + likes: { + increment: 1, + }, + }, +}) +``` + +### Connect and disconnect related records + +Refer to [Working with relations](relation-queries) for information about disconnecting ([`disconnect`](/orm/reference/prisma-client-reference#disconnect) ) and connecting ([`connect`](/orm/reference/prisma-client-reference#connect) ) related records. + +## Delete + +### Delete a single record + +The following query uses [`delete`](/orm/reference/prisma-client-reference#delete) to delete a single `User` record: + +```ts +const deleteUser = await prisma.user.delete({ + where: { + email: 'bert@prisma.io', + }, +}) +``` + +Attempting to delete a user with one or more posts result in an error, as every `Post` requires an author - see [cascading deletes](#cascading-deletes-deleting-related-records). + +### Delete multiple records + +The following query uses `deleteMany` to delete all `User` records where `email` contains `prisma.io`: + +```ts +const deleteUsers = await prisma.user.deleteMany({ + where: { + email: { + contains: 'prisma.io', + }, + }, +}) +``` + +Attempting to delete a user with one or more posts result in an error, as every `Post` requires an author - see [cascading deletes](#cascading-deletes-deleting-related-records). + +### Delete all records + +The following query uses `deleteMany` to delete all `User` records: + +```ts +const deleteUsers = await prisma.user.deleteMany({}) +``` + +Be aware that this query will fail if the user has any related records (such as posts). In this case, you need to [delete the related records first](#cascading-deletes-deleting-related-records). + +### Cascading deletes (deleting related records) + + + +In [2.26.0](https://github.com/prisma/prisma/releases/tag/2.26.0) and later it is possible to do cascading deletes using the **preview feature** [referential actions](/orm/prisma-schema/data-model/relations/referential-actions). + + + +The following query uses [`delete`](/orm/reference/prisma-client-reference#delete) to delete a single `User` record: + +```ts +const deleteUser = await prisma.user.delete({ + where: { + email: 'bert@prisma.io', + }, +}) +``` + +However, the example schema includes a **required relation** between `Post` and `User`, which means that you cannot delete a user with posts: + +``` +The change you are trying to make would violate the required relation 'PostToUser' between the `Post` and `User` models. +``` + +To resolve this error, you can: + +- Make the relation optional: + + ```prisma highlight=3,4;add|5,6;delete + model Post { + id Int @id @default(autoincrement()) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? + author User @relation(fields: [authorId], references: [id]) + authorId Int + } + ``` + +- Change the author of the posts to another user before deleting the user. + +- Delete a user and all their posts with two separate queries in a transaction (all queries must succeed): + + ```ts + const deletePosts = prisma.post.deleteMany({ + where: { + authorId: 7, + }, + }) + + const deleteUser = prisma.user.delete({ + where: { + id: 7, + }, + }) + + const transaction = await prisma.$transaction([deletePosts, deleteUser]) + ``` + +### Delete all records from all tables + +Sometimes you want to remove all data from all tables but keep the actual tables. This can be particularly useful in a development environment and whilst testing. + +The following shows how to delete all records from all tables with Prisma Client and with Prisma Migrate. + +#### Deleting all data with `deleteMany` + +When you know the order in which your tables should be deleted, you can use the [`deleteMany`](/orm/reference/prisma-client-reference#deletemany) function. This is executed synchronously in a [`$transaction`](/orm/prisma-client/queries/transactions) and can be used with all types of databases. + +```ts +const deletePosts = prisma.post.deleteMany() +const deleteProfile = prisma.profile.deleteMany() +const deleteUsers = prisma.user.deleteMany() + +// The transaction runs synchronously so deleteUsers must run last. +await prisma.$transaction([deleteProfile, deletePosts, deleteUsers]) +``` + +✅ **Pros**: + +- Works well when you know the structure of your schema ahead of time +- Synchronously deletes each tables data + +❌ **Cons**: + +- When working with relational databases, this function doesn't scale as well as having a more generic solution which looks up and `TRUNCATE`s your tables regardless of their relational constraints. Note that this scaling issue does not apply when using the MongoDB connector. + +> **Note**: The `$transaction` performs a cascading delete on each models table so they have to be called in order. + +#### Deleting all data with raw SQL / `TRUNCATE` + +If you are comfortable working with raw SQL you can perform a `TRUNCATE` on a table by utilizing [`$executeRawUnsafe`](/orm/prisma-client/queries/raw-database-access/raw-queries#executerawunsafe). + +In the following examples, the first tab shows how to perform a `TRUNCATE` on a Postgres database by using a `$queryRaw` look up that maps over the table and `TRUNCATES` all tables in a single query. + +The second tab shows performing the same function but with a MySQL database. In this instance the constraints must be removed before the `TRUNCATE` can be executed, before being reinstated once finished. The whole process is run as a `$transaction` + + + + + +```ts +const tablenames = await prisma.$queryRaw< + Array<{ tablename: string }> +>`SELECT tablename FROM pg_tables WHERE schemaname='public'` + +const tables = tablenames + .map(({ tablename }) => tablename) + .filter((name) => name !== '_prisma_migrations') + .map((name) => `"public"."${name}"`) + .join(', ') + +try { + await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${tables} CASCADE;`) +} catch (error) { + console.log({ error }) +} +``` + + + + + +```ts +const transactions: PrismaPromise[] = [] +transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 0;`) + +const tablenames = await prisma.$queryRaw< + Array<{ TABLE_NAME: string }> +>`SELECT TABLE_NAME from information_schema.TABLES WHERE TABLE_SCHEMA = 'tests';` + +for (const { TABLE_NAME } of tablenames) { + if (TABLE_NAME !== '_prisma_migrations') { + try { + transactions.push(prisma.$executeRawUnsafe(`TRUNCATE ${TABLE_NAME};`)) + } catch (error) { + console.log({ error }) + } + } +} + +transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 1;`) + +try { + await prisma.$transaction(transactions) +} catch (error) { + console.log({ error }) +} +``` + + + + + +✅ **Pros**: + +- Scalable +- Very fast + +❌ **Cons**: + +- Can't undo the operation +- Using reserved SQL key words as tables names can cause issues when trying to run a raw query + +#### Deleting all records with Prisma Migrate + +If you use Prisma Migrate, you can use `migrate reset`, this will: + +1. Drop the database +2. Create a new database +3. Apply migrations +4. Seed the database with data + +## Advanced query examples + +### Create a deeply nested tree of records + +- A single `User` +- Two new, related `Post` records +- Connect or create `Category` per post + +```ts +const u = await prisma.user.create({ + include: { + posts: { + include: { + categories: true, + }, + }, + }, + data: { + email: 'emma@prisma.io', + posts: { + create: [ + { + title: 'My first post', + categories: { + connectOrCreate: [ + { + create: { name: 'Introductions' }, + where: { + name: 'Introductions', + }, + }, + { + create: { name: 'Social' }, + where: { + name: 'Social', + }, + }, + ], + }, + }, + { + title: 'How to make cookies', + categories: { + connectOrCreate: [ + { + create: { name: 'Social' }, + where: { + name: 'Social', + }, + }, + { + create: { name: 'Cooking' }, + where: { + name: 'Cooking', + }, + }, + ], + }, + }, + ], + }, + }, +}) +``` diff --git a/docs/200-orm/200-prisma-client/100-queries/035-select-fields.mdx b/docs/200-orm/200-prisma-client/100-queries/035-select-fields.mdx new file mode 100644 index 0000000000..19297b3bc6 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/035-select-fields.mdx @@ -0,0 +1,321 @@ +--- +title: 'Select fields' +metaTitle: 'Select fields (Concepts)' +metaDescription: 'This page explains how to select only a subset of a model''s fields and/or include relations ("eager loading") in a Prisma Client query.' +tocDepth: 3 +--- + + + +By default, when a query returns records (as opposed to a count), the result includes the **default selection set**: + +- **All** scalar fields defined in the Prisma schema (including enums) +- **None** of the relations + +To customize the result: + +- Use [`select`](/orm/reference/prisma-client-reference#select) to return specific fields - [you can also use a nested `select` to include relation fields](relation-queries#select-specific-relation-fields) +- Use [`include`](/orm/reference/prisma-client-reference#include) to explicitly [include relations](relation-queries#nested-reads) + +Selecting only the fields and relations that you require rather than relying on the default selection set can ✔ reduce the size of the response and ✔ improve query speed. + +Since version [5.9.0](https://github.com/prisma/prisma/releases/tag/5.9.0), when doing a relation query with `include` or by using `select` on a relation field, you can also specify the `relationLoadStrategy` to decide whether you want to use a database-level JOIN or perform multiple queries and merge the data on the application level. This feature is currently in [Preview](/orm/more/releases#preview), you can learn more about it [here](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview). + + + +## Example schema + +All examples are based on the following schema: + +
+ +Expand for sample schema + + + + +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model ExtendedProfile { + id Int @id @default(autoincrement()) + biography String + user User @relation(fields: [userId], references: [id]) + userId Int @unique +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + profileViews Int @default(0) + role Role @default(USER) + coinflips Boolean[] + posts Post[] + profile ExtendedProfile? +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId Int + comments Json? + views Int @default(0) + likes Int @default(0) + categories Category[] +} + +model Category { + id Int @id @default(autoincrement()) + name String @unique + posts Post[] +} + +enum Role { + USER + ADMIN +} +``` + + + + +```prisma +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model ExtendedProfile { + id String @id @default(auto()) @map("_id") @db.ObjectId + biography String + user User @relation(fields: [userId], references: [id]) + userId String @unique @db.ObjectId +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + email String @unique + profileViews Int @default(0) + role Role @default(USER) + coinflips Boolean[] + posts Post[] + profile ExtendedProfile? +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + comments Json? + views Int @default(0) + likes Int @default(0) + categories Category[] +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String @unique + posts Post[] +} + +enum Role { + USER + ADMIN +} +``` + + + + +
+ +For **relational databases**, use `db push` command to push the example schema to your own database + +```terminal +npx prisma db push +``` + +For **MongoDB**, ensure your data is in a uniform shape and matches the model defined in the Prisma schema. + +## Return the default selection set + +The following query returns the default selection set (all scalar fields, no relations): + + + + +```ts +// Query returns User or null +const getUser: User | null = await prisma.user.findUnique({ + where: { + id: 22, + }, +}) +``` + + + + +```js no-copy +{ + id: 22, + name: "Alice", + email: "alice@prisma.io", + profileViews: 0, + role: "ADMIN", + coinflips: [true, false], +} +``` + + + + +## Select specific fields + +Use `select` to return a limited subset of fields instead of all fields. The following example returns the `email` and `name` fields only: + + + + +```ts +// Returns an object or null +const getUser: object | null = await prisma.user.findUnique({ + where: { + id: 22, + }, + select: { + email: true, + name: true, + }, +}) +``` + + + + +```js no-copy +{ + name: "Alice", + email: "alice@prisma.io", +} +``` + + + + +## Include relations and select relation fields + +To return **specific relation fields**, you can: + +- Use a nested `select` +- Use a `select` within an `include` + +> To return _all_ relation fields, use `include` only - for example, `{ include: { posts: true } }`. + +The following query uses a nested `select` to select each user's `name` and the `title` of each related post: + + + + +```ts highlight=normal;2,5 +const users = await prisma.user.findMany({ + select: { + name: true, + posts: { + select: { + title: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + "name":"Sabelle", + "posts":[ + { + "title":"Getting started with Azure Functions" + }, + { + "title":"All about databases" + } + ] +} +``` + + + + +The following query uses `select` within an `include`, and returns _all_ user fields and each post's `title` field: + + + + +```ts highlight=normal;2,5 +const users = await prisma.user.findMany({ + // Returns all user fields + include: { + posts: { + select: { + title: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + "id": 9 + "name": "Sabelle", + "email": "sabelle@prisma.io", + "profileViews": 90, + "role": "USER", + "profile": null, + "coinflips": [], + "posts":[ + { + "title":"Getting started with Azure Functions" + }, + { + "title":"All about databases" + } + ] +} +``` + + + + +For more information about querying relations, refer to the following documentation: + +- [Include a relation (including all fields)](relation-queries#include-all-fields-for-a-specific-relation) +- [Select specific relation fields](relation-queries#select-specific-relation-fields) + +## Relation count + +In [3.0.1](https://github.com/prisma/prisma/releases/3.0.1) and later, you can [`include` or `select` a count of relations](aggregation-grouping-summarizing#count-relations) alongside fields - for example, a user's post count. diff --git a/docs/200-orm/200-prisma-client/100-queries/037-relation-queries.mdx b/docs/200-orm/200-prisma-client/100-queries/037-relation-queries.mdx new file mode 100644 index 0000000000..4bddafa2c5 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/037-relation-queries.mdx @@ -0,0 +1,1315 @@ +--- +title: 'Relation queries' +metaTitle: 'Relation queries (Concepts)' +metaDescription: 'Prisma Client provides convenient queries for working with relations, such as a fluent API, nested writes (transactions), nested reads and relation filters.' +tocDepth: 4 +--- + + + +A key feature of Prisma Client is the ability to query [relations](/orm/prisma-schema/data-model/relations) between two or more models. Relation queries include: + +- [Nested reads](#nested-reads) (sometimes referred to as _eager loading_) via [`select`](/orm/reference/prisma-client-reference#select) and [`include`](/orm/reference/prisma-client-reference#include) +- [Nested writes](#nested-writes) with [transactional](transactions) guarantees +- [Filtering on related records](#relation-filters) + +Prisma Client also has a [fluent API for traversing relations](#fluent-api). + + + +## Nested reads + +Nested reads allow you to read related data from multiple tables in your database - such as a user and that user's posts. You can: + +- Use [`include`](/orm/reference/prisma-client-reference#include) to include related records, such as a user's posts or profile, in the query response. +- Use a nested [`select`](/orm/reference/prisma-client-reference#select) to include specific fields from a related record. You can also nest `select` inside an `include`. + +### Relation load strategies (Preview) + +Since version [5.9.0](https://github.com/prisma/prisma/releases/tag/5.9.0), you can decide on a per-query-level _how_ you want Prisma Client to execute a relation query (i.e. what _load strategy_ should be applied) via the `relationLoadStrategy` option. + +Because the `relationLoadStrategy` option is currently in Preview, you need to enable it via the `relationJoins` preview feature flag in your Prisma schema file: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["relationJoins"] +} +``` + +After adding this flag, you need to run `prisma generate` again to re-generate Prisma Client. Also note that this feature is currently only available on PostgreSQL and CockroachDB for now. + +Prisma Client supports two load strategies for relations: + +- `join` (default): Uses a database-level `LATERAL JOIN` and fetches all data with a single query to the database. +- `query`: Sends multiple queries to the database (one per table) and joins them on the application level. + +Another important difference between these two options is that the `join` strategy uses JSON aggregation on the database level. That means that it creates the JSON structures returned by Prisma Client already in the database which saves computation resources on the application level. + +> **Note**: Once `relationLoadStrategy` moves from [Preview](/orm/more/releases#preview) into [General Availability](/orm/more/releases/#generally-available-ga), `join` will universally become the default for all relation queries. + +#### Examples + +You can use the `relationLoadStrategy` option on the top-level in any query that supports `include` or `select`. + +Here is an example with `include`: + +```ts +const users = await prisma.user.findMany({ + relationLoadStrategy: 'join', // or 'query' + include: { + posts: true, + }, +}) +``` + +And here is another example with `select`: + +```ts +const users = await prisma.user.findMany({ + relationLoadStrategy: 'join', // or 'query' + select: { + posts: true, + }, +}) +``` + +#### When to use which load strategy? + +- The `join` strategy will be more effective in most scenarios. It uses a combination of `LATERAL JOINs` and JSON aggregation to reduce redundancy in result sets and delegate the work of transforming the query results into the expected JSON structures on the database server. +- There may be edge cases where `query` could be more performant depending on the characteristics of the dataset and query. We recommend that you profile your database queries to identify these situations. +- Use `query` if you want to save resources on the database server and do heavy-lifting of merging and transforming data in the application server which might be easier to scale. +- Older database versions that don’t implement the `LATERAL` keyword may struggle require query complexity and data redundancy so that sending individual queries could be more performant. + +### Include a relation + +The following example returns a single user and that user's posts: + + + + +```ts +const user = await prisma.user.findFirst({ + include: { + posts: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 19, + name: null, + email: 'emma@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + posts: [ + { + id: 20, + title: 'My first post', + published: true, + authorId: 19, + comments: null, + views: 0, + likes: 0 + }, + { + id: 21, + title: 'How to make cookies', + published: true, + authorId: 19, + comments: null, + views: 0, + likes: 0 + } + ] +} +``` + + + + +### Include all fields for a specific relation + +The following example returns a post and its author: + + + + +```ts +const post = await prisma.post.findFirst({ + include: { + author: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 17, + title: 'How to make cookies', + published: true, + authorId: 16, + comments: null, + views: 0, + likes: 0, + author: { + id: 16, + name: null, + email: 'orla@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + }, +} +``` + + + + +### Include deeply nested relations + +You can nest `include` options to include relations of relations. The following example returns a user's posts, and each post's categories: + + + + +```ts +const user = await prisma.user.findFirst({ + include: { + posts: { + include: { + categories: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + "id": 40, + "name": "Yvette", + "email": "yvette@prisma.io", + "profileViews": 0, + "role": "USER", + "coinflips": [], + "testing": [], + "city": null, + "country": "Sweden", + "posts": [ + { + "id": 66, + "title": "How to make an omelette", + "published": true, + "authorId": 40, + "comments": null, + "views": 0, + "likes": 0, + "categories": [ + { + "id": 3, + "name": "Easy cooking" + } + ] + }, + { + "id": 67, + "title": "How to eat an omelette", + "published": true, + "authorId": 40, + "comments": null, + "views": 0, + "likes": 0, + "categories": [] + } + ] +} +``` + + + + +### Select specific relation fields + +You can use a nested `select` to choose a subset of relation fields to return. For example, the following query returns the user's `name` and the `title` of each related post: + + + + +```ts +const user = await prisma.user.findFirst({ + select: { + name: true, + posts: { + select: { + title: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + name: "Elsa", + posts: [ { title: 'My first post' }, { title: 'How to make cookies' } ] +} +``` + + + + +You can also nest a `select` inside an `include` - the following example returns _all_ `User` fields and the `title` field of each post: + + + + +```ts +const user = await prisma.user.findFirst({ + include: { + posts: { + select: { + title: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + "id": 1, + "name": null, + "email": "martina@prisma.io", + "profileViews": 0, + "role": "USER", + "coinflips": [], + "posts": [ + { "title": "How to grow salad" }, + { "title": "How to ride a horse" } + ] +} +``` + + + + +Note that you **cannot** use `select` and `include` _on the same level_. This means that if you choose to `include` a user's post and `select` each post's title, you cannot `select` only the users' `email`: + + + + +```ts highlight=3,6;delete +// The following query returns an exception +const user = await prisma.user.findFirst({ + select: { // This won't work! + email: true + } + include: { // This won't work! + posts: { + select: { + title: true + } + } + }, +}) +``` + + + + +```code no-copy +Invalid `prisma.user.findUnique()` invocation: + +{ + where: { + id: 19 + }, + select: { + ~~~~~~ + email: true + }, + include: { + ~~~~~~~ + posts: { + select: { + title: true + } + } + } +} + + +Please either use `include` or `select`, but not both at the same time. +``` + + + + +Instead, use nested `select` options: + +```ts +const user = await prisma.user.findFirst({ + select: { + // This will work! + email: true, + posts: { + select: { + title: true, + }, + }, + }, +}) +``` + +## Relation count + +In [3.0.1](https://github.com/prisma/prisma/releases/3.0.1) and later, you can [`include` or `select` a count of relations](aggregation-grouping-summarizing#count-relations) alongside fields - for example, a user's post count. + + + + +```ts +const relationCount = await prisma.user.findMany({ + include: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```code no-copy +{ id: 1, _count: { posts: 3 } }, +{ id: 2, _count: { posts: 2 } }, +{ id: 3, _count: { posts: 2 } }, +{ id: 4, _count: { posts: 0 } }, +{ id: 5, _count: { posts: 0 } } +``` + + + + +## Filter a list of relations + +When you use `select` or `include` to return a subset of the related data, you can **filter and sort the list of relations** inside the `select` or `include`. + +For example, the following query returns all users and a list of titles of the unpublished posts associated with each user: + +```ts +const result = await prisma.user.findFirst({ + select: { + posts: { + where: { + published: false, + }, + orderBy: { + title: 'asc', + }, + select: { + title: true, + }, + }, + }, +}) +``` + +You can also write the same query using `include` as follows: + +```ts +const result = await prisma.user.findFirst({ + include: { + posts: { + where: { + published: true, + }, + orderBy: { + title: 'asc', + }, + }, + }, +}) +``` + +## Nested writes + +A nested write allows you to write **relational data** to your database in **a single transaction**. + +Nested writes: + +- Provide **transactional guarantees** for creating, updating or deleting data across multiple tables in a single Prisma Client query. If any part of the query fails (for example, creating a user succeeds but creating posts fails), Prisma Client rolls back all changes. +- Support any level of nesting supported by the data model. +- Are available for [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) when using the model's create or update query. The following section shows the nested write options that are available per query. + +### Create a related record + +You can create a record and one or more related records at the same time. The following query creates a `User` record and two related `Post` records: + + + + +```ts highlight=5-10;normal +const result = await prisma.user.create({ + data: { + email: 'elsa@prisma.io', + name: 'Elsa Prisma', + posts: { + create: [ + { title: 'How to make an omelette' }, + { title: 'How to eat an omelette' }, + ], + }, + }, + include: { + posts: true, // Include all posts in the returned object + }, +}) +``` + + + + +```js no-copy +{ + id: 29, + name: 'Elsa', + email: 'elsa@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + posts: [ + { + id: 22, + title: 'How to make an omelette', + published: true, + authorId: 29, + comments: null, + views: 0, + likes: 0 + }, + { + id: 23, + title: 'How to eat an omelette', + published: true, + authorId: 29, + comments: null, + views: 0, + likes: 0 + } + ] +} +``` + + + + +### Create a single record and multiple related records + +There are two ways to create or update a single record and multiple related records - for example, a user with multiple posts: + +- Use a nested [`create`](/orm/reference/prisma-client-reference#create-1) query +- Use a nested [`createMany`](/orm/reference/prisma-client-reference#createmany-1) query + +Each technique has pros and cons: + +| Feature | `create` | `createMany` | Notes | +| :------------------------------------ | :------- | :----------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Creates one record at a time | ✔ | ✘ | Potentially less performant. | +| Creates all records in one query | ✘ | ✔ | Potentially more performant. | +| Supports nesting additional relations | ✔ | ✘ \* | For example, you can create a user, several posts, and several comments per post in one query.
\* You can manually set a foreign key in a has-one relation - for example: `{ authorId: 9}` | +| Supports skipping duplicate records | ✘ | ✔ | Use `skipDuplicates` query option. | +| Supports has-many relations | ✔ | ✔ | For example, you can create a user and multiple posts (one user has many posts) | +| Supports many-to-many relations | ✔ | ✘ | For example, you can create a post and several categories (one post can have many categories, and one category can have many posts) | + +The following query uses nested [`create`](/orm/reference/prisma-client-reference#create) to create: + +- One user +- Two posts +- One post category + +The example uses a nested `include` to include all posts and post categories. + + + + +```ts highlight=5-17;normal +const result = await prisma.user.create({ + data: { + email: 'yvette@prisma.io', + name: 'Yvette', + posts: { + create: [ + { + title: 'How to make an omelette', + categories: { + create: { + name: 'Easy cooking', + }, + }, + }, + { title: 'How to eat an omelette' }, + ], + }, + }, + include: { + // Include posts + posts: { + include: { + categories: true, // Include post categories + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + "id": 40, + "name": "Yvette", + "email": "yvette@prisma.io", + "profileViews": 0, + "role": "USER", + "coinflips": [], + "testing": [], + "city": null, + "country": "Sweden", + "posts": [ + { + "id": 66, + "title": "How to make an omelette", + "published": true, + "authorId": 40, + "comments": null, + "views": 0, + "likes": 0, + "categories": [ + { + "id": 3, + "name": "Easy cooking" + } + ] + }, + { + "id": 67, + "title": "How to eat an omelette", + "published": true, + "authorId": 40, + "comments": null, + "views": 0, + "likes": 0, + "categories": [] + } + ] +} +``` + + + + +The following query uses a nested [`createMany`](/orm/reference/prisma-client-reference#create) to create: + +- One user +- Two posts + +The example uses a nested `include` to include all posts. + + + + +```ts highlight=4-8;normal +const result = await prisma.user.create({ + data: { + email: 'saanvi@prisma.io', + posts: { + createMany: { + data: [{ title: 'My first post' }, { title: 'My second post' }], + }, + }, + }, + include: { + posts: true, + }, +}) +``` + + + + +```js no-copy +{ + "id": 43, + "name": null, + "email": "saanvi@prisma.io", + "profileViews": 0, + "role": "USER", + "coinflips": [], + "testing": [], + "city": null, + "country": "India", + "posts": [ + { + "id": 70, + "title": "My first post", + "published": true, + "authorId": 43, + "comments": null, + "views": 0, + "likes": 0 + }, + { + "id": 71, + "title": "My second post", + "published": true, + "authorId": 43, + "comments": null, + "views": 0, + "likes": 0 + } + ] +} +``` + + + + + + +**Note**: It is **not possible** to nest an additional `create` or `createMany` inside the highlighted query, which means that you cannot create a user, posts, and post categories at the same time. + + + +### Create multiple records and multiple related records + +You cannot access relations in a `createMany` query, which means that you cannot create multiple users and multiple posts in a single nested write. The following is **not** possible: + +```ts highlight=6-8,13-15;delete +const createMany = await prisma.user.createMany({ + data: [ + { + name: 'Yewande', + email: 'yewande@prisma.io', + posts: { + // Not possible to create posts! + }, + }, + { + name: 'Noor', + email: 'noor@prisma.io', + posts: { + // Not possible to create posts! + }, + }, + ], +}) +``` + +### Connect multiple records + +The following query creates ([`create`](/orm/reference/prisma-client-reference#create) ) a new `User` record and connects that record ([`connect`](/orm/reference/prisma-client-reference#connect) ) to three existing posts: + + + + +```ts highlight=4-6;normal +const result = await prisma.user.create({ + data: { + email: 'vlad@prisma.io', + posts: { + connect: [{ id: 8 }, { id: 9 }, { id: 10 }], + }, + }, + include: { + posts: true, // Include all posts in the returned object + }, +}) +``` + + + + +```js no-copy +{ + id: 27, + name: null, + email: 'vlad@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + posts: [ + { + id: 10, + title: 'An existing post', + published: true, + authorId: 27, + comments: {}, + views: 0, + likes: 0 + } + ] +} +``` + + + + +> **Note**: Prisma Client throws an exception if any of the post records cannot be found: `connect: [{ id: 8 }, { id: 9 }, { id: 10 }]` + +### Connect a single record + +You can [`connect`](/orm/reference/prisma-client-reference#connect) an existing record to a new or existing user. The following query connects an existing post (`id: 11`) to an existing user (`id: 9`) + +```ts highlight=6-9;normal +const result = await prisma.user.update({ + where: { + id: 9, + }, + data: { + posts: { + connect: { + id: 11, + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Connect _or_ create a record + +If a related record may or may not already exist, use [`connectOrCreate`](/orm/reference/prisma-client-reference#connectorcreate) to connect the related record: + +- Connect a `User` with the email address `viola@prisma.io` _or_ +- Create a new `User` with the email address `viola@prisma.io` if the user does not already exist + + + + +```ts highlight=4-14;normal +const result = await prisma.post.create({ + data: { + title: 'How to make croissants', + author: { + connectOrCreate: { + where: { + email: 'viola@prisma.io', + }, + create: { + email: 'viola@prisma.io', + name: 'Viola', + }, + }, + }, + }, + include: { + author: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 26, + title: 'How to make croissants', + published: true, + authorId: 43, + views: 0, + likes: 0, + author: { + id: 43, + name: 'Viola', + email: 'viola@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [] + } +} +``` + + + + +### Disconnect a related record + +To `disconnect` one out of a list of records (for example, a specific blog post) provide the ID or unique identifier of the record(s) to disconnect: + + + + +```ts highlight=6-8;normal +const result = await prisma.user.update({ + where: { + id: 16, + }, + data: { + posts: { + disconnect: [{ id: 12 }, { id: 19 }], + }, + }, + include: { + posts: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 16, + name: null, + email: 'orla@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + posts: [] +} +``` + + + + +To `disconnect` _one_ record (for example, a post's author), use `disconnect: true`: + + + + +```ts highlight=6-8;normal +const result = await prisma.post.update({ + where: { + id: 23, + }, + data: { + author: { + disconnect: true, + }, + }, + include: { + author: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 23, + title: 'How to eat an omelette', + published: true, + authorId: null, + comments: null, + views: 0, + likes: 0, + author: null +} +``` + + + + +### Disconnect all related records + +To [`disconnect`](/orm/reference/prisma-client-reference#disconnect) _all_ related records in a one-to-many relation (a user has many posts), `set` the relation to an empty list as shown: + + + + +```ts highlight=6-8;normal +const result = await prisma.user.update({ + where: { + id: 16, + }, + data: { + posts: { + set: [], + }, + }, + include: { + posts: true, + }, +}) +``` + + + + +```js no-copy +{ + id: 16, + name: null, + email: 'orla@prisma.io', + profileViews: 0, + role: 'USER', + coinflips: [], + posts: [] +} +``` + + + + +### Delete all related records + +Delete all related `Post` records: + +```ts highlight=6-8;normal +const result = await prisma.user.update({ + where: { + id: 11, + }, + data: { + posts: { + deleteMany: {}, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Delete specific related records + +Update a user by deleting all unpublished posts: + +```ts highlight=6-10;normal +const result = await prisma.user.update({ + where: { + id: 11, + }, + data: { + posts: { + deleteMany: { + published: false, + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +Update a user by deleting specific posts: + +```ts highlight=6-8;normal +const result = await prisma.user.update({ + where: { + id: 6, + }, + data: { + posts: { + deleteMany: [{ id: 7 }], + }, + }, + include: { + posts: true, + }, +}) +``` + +### Update all related records (or filter) + +You can use a nested `updateMany` to update _all_ related records for a particular user. The following query unpublishes all posts for a specific user: + +```ts highlight=6-15;normal +const result = await prisma.user.update({ + where: { + id: 6, + }, + data: { + posts: { + updateMany: { + where: { + published: true, + }, + data: { + published: false, + }, + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Update a specific related record + +```ts highlight=6-15;normal +const result = await prisma.user.update({ + where: { + id: 6, + }, + data: { + posts: { + update: { + where: { + id: 9, + }, + data: { + title: 'My updated title', + }, + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Update _or_ create a related record + +The following query uses a nested `upsert` to update `"bob@prisma.io"` if that user exists, or create the user if they do not exist: + +```ts highlight=6-17;normal +const result = await prisma.post.update({ + where: { + id: 6, + }, + data: { + author: { + upsert: { + create: { + email: 'bob@prisma.io', + name: 'Bob the New User', + }, + update: { + email: 'bob@prisma.io', + name: 'Bob the existing user', + }, + }, + }, + }, + include: { + author: true, + }, +}) +``` + +### Add new related records to an existing record + +You can nest `create` or `createMany` inside an `update` to add new related records to an existing record. The following query adds two posts to a user with an `id` of 9: + +```ts highlight=6-10;normal +const result = await prisma.user.update({ + where: { + id: 9, + }, + data: { + posts: { + createMany: { + data: [{ title: 'My first post' }, { title: 'My second post' }], + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +## Relation filters + +### Filter on "-to-many" relations + +Prisma Client provides the [`some`](/orm/reference/prisma-client-reference#some), [`every`](/orm/reference/prisma-client-reference#every), and [`none`](/orm/reference/prisma-client-reference#none) options to filter records by the properties of related records on the "-to-many" side of the relation. For example, filtering users based on properties of their posts. + +For example: + +| Requirement | Query option to use | +| --------------------------------------------------------------------------------- | ----------------------------------- | +| "I want a list of every `User` that has _at least one_ unpublished `Post` record" | `some` posts are unpublished | +| "I want a list of every `User` that has _no_ unpublished `Post` records" | `none` of the posts are unpublished | +| "I want a list of every `User` that has _only_ unpublished `Post` records" | `every` post is unpublished | + +For example, the following query returns `User` that meet the following criteria: + +- No posts with more than 100 views +- All posts have less than, or equal to 50 likes + +```ts highlight=3-14;normal +const users = await prisma.user.findMany({ + where: { + posts: { + none: { + views: { + gt: 100, + }, + }, + every: { + likes: { + lte: 50, + }, + }, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Filter on "-to-one" relations + +Prisma Client provides the [`is`](/orm/reference/prisma-client-reference#is) and [`isNot`](/orm/reference/prisma-client-reference#isnot) options to filter records by the properties of related records on the "-to-one" side of the relation. For example, filtering posts based on properties of their author. + +For example, the following query returns `Post` records that meet the following criteria: + +- Author's name is not Bob +- Author is older than 40 + +```ts highlight=3-13;normal +const users = await prisma.post.findMany({ + where: { + author: { + isNot: { + name: 'Bob', + }, + is: { + age: { + gt: 40, + }, + }, + }, + }, + include: { + author: true, + }, +}) +``` + +### Filter on absence of "-to-many" records + +For example, the following query uses `none` to return all users that have zero posts: + +```ts highlight=3-5;normal +const usersWithZeroPosts = await prisma.user.findMany({ + where: { + posts: { + none: {}, + }, + }, + include: { + posts: true, + }, +}) +``` + +### Filter on absence of "-to-one" relations + +The following query returns all posts that don't have an author relation: + +```js highlight=3;normal +const postsWithNoAuthor = await prisma.post.findMany({ + where: { + author: null, // or author: { } + }, + include: { + author: true, + }, +}) +``` + +### Filter on presence of related records + +The following query returns all users with at least one post: + +```ts highlight=3-5;normal +const usersWithSomePosts = await prisma.user.findMany({ + where: { + posts: { + some: {}, + }, + }, + include: { + posts: true, + }, +}) +``` + +## Fluent API + +The fluent API lets you _fluently_ traverse the [relations](/orm/prisma-schema/data-model/relations) of your models via function calls. Note that the _last_ function call determines the return type of the entire query (the respective type annotations are added in the code snippets below to make that explicit). + +This query returns all `Post` records by a specific `User`: + +```ts +const postsByUser: Post[] = await prisma.user + .findUnique({ where: { email: 'alice@prisma.io' } }) + .posts() +``` + +This is equivalent to the following `findMany` query: + +```ts +const postsByUser = await prisma.post.findMany({ + where: { + author: { + email: 'alice@prisma.io', + }, + }, +}) +``` + +The main difference between the queries is that the fluent API call is translated into two separate database queries while the other one only generates a single query (see this [GitHub issue](https://github.com/prisma/prisma/issues/1984)) + +> **Note**: You can use the fact that `.findUnique({ where: { email: 'alice@prisma.io' } }).posts()` queries are automatically batched by the Prisma dataloader to [avoid the n+1 problem in GraphQL resolvers](/orm/prisma-client/queries/query-optimization-performance#solving-n1-in-graphql-with-findunique-and-prismas-dataloader). + +This request returns all categories by a specific post: + +```ts +const categoriesOfPost: Category[] = await prisma.post + .findUnique({ where: { id: 1 } }) + .categories() +``` + +Note that you can chain as many queries as you like. In this example, the chaining starts at `Profile` and goes over `User` to `Post`: + +```ts +const posts: Post[] = await prisma.profile + .findUnique({ where: { id: 1 } }) + .user() + .posts() +``` + +The only requirement for chaining is that the previous function call must return only a _single object_ (e.g. as returned by a `findUnique` query or a "to-one relation" like `profile.user()`). + +The following query is **not possible** because `findMany` does not return a single object but a _list_: + +```ts +// This query is illegal +const posts = await prisma.user.findMany().posts() +``` diff --git a/docs/200-orm/200-prisma-client/100-queries/050-filtering-and-sorting.mdx b/docs/200-orm/200-prisma-client/100-queries/050-filtering-and-sorting.mdx new file mode 100644 index 0000000000..b40960888b --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/050-filtering-and-sorting.mdx @@ -0,0 +1,452 @@ +--- +title: 'Filtering and Sorting' +metaTitle: 'Filtering and Sorting (Concepts)' +metaDescription: 'Use Prisma Client API to filter records by any combination of fields or related record fields, and/or sort query results.' +tocDepth: 3 +--- + + + +Prisma Client supports [filtering](#filtering) with the `where` query option, and [sorting](#sorting) with the `orderBy` query option. + + + +## Filtering + +Prisma Client allows you to filter records on any combination of model fields, [including related models](#filter-on-relations), and supports a variety of [filter conditions](#filter-conditions-and-operators). + + + +Some filter conditions use the SQL operators `LIKE` and `ILIKE` which may cause unexpected behavior in your queries. Please refer to [our filtering FAQs](#filtering-faqs) for more information. + + + +The following query: + +- Returns all `User` records with: + - an email address that ends with `prisma.io` _and_ + - at least one published post (a relation query) +- Returns all `User` fields +- Includes all related `Post` records where `published` equals `true` + + + + +```ts +const result = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + }, + posts: { + some: { + published: true, + }, + }, + }, + include: { + posts: { + where: { + published: true, + }, + }, + }, +}) +``` + + + + +```json5 no-copy +[ + { + id: 1, + name: 'Ellen', + email: 'ellen@prisma.io', + role: 'USER', + posts: [ + { + id: 1, + title: 'How to build a house', + published: true, + authorId: 1, + }, + { + id: 2, + title: 'How to cook kohlrabi', + published: true, + authorId: 1, + }, + ], + }, +] +``` + + + + +### Filter conditions and operators + +Refer to Prisma Client's reference documentation for [a full list of operators](/orm/reference/prisma-client-reference#filter-conditions-and-operators) , such as `startsWith` and `contains`. + +#### Combining operators + +You can use operators (such as [`NOT`](/orm/reference/prisma-client-reference#not-1) and [`OR`](/orm/reference/prisma-client-reference#or) ) to filter by a combination of conditions. The following query returns all users with an `email` that ends in `"prisma.io"` or `"gmail.com"`, but not `"hotmail.com"`: + + + + +```ts +const result = await prisma.user.findMany({ + where: { + OR: [ + { + email: { + endsWith: 'prisma.io', + }, + }, + { email: { endsWith: 'gmail.com' } }, + ], + NOT: { + email: { + endsWith: 'hotmail.com', + }, + }, + }, + select: { + email: true, + }, +}) +``` + + + + +```json5 no-copy +[{ email: 'yewande@prisma.io' }, { email: `raheem@gmail.com` }] +``` + + + + +### Filter on null fields + +The following query returns all posts whose `content` field is `null`: + +```ts +const posts = await prisma.post.findMany({ + where: { + content: null, + }, +}) +``` + +### Filter for non-null fields + +The following query returns all posts whose `content` field is **not** `null`: + +```ts +const posts = await prisma.post.findMany({ + where: { + content: { not: null }, + }, +}) +``` + +### Filter on relations + +Prisma Client supports [filtering on related records](relation-queries#relation-filters). For example, in the following schema, a user can have many blog posts: + +```prisma highlight=5,12-13;normal +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] // User can have many posts +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +``` + +The one-to-many relation between `User` and `Post` allows you to query users based on their posts - for example, the following query returns all users where _at least one_ post (`some`) has more than 10 views: + +```ts +const result = await prisma.user.findMany({ + where: { + posts: { + some: { + views: { + gt: 10, + }, + }, + }, + }, +}) +``` + +You can also query posts based on the properties of the author. For example, the following query returns all posts where the author's `email` contains `"prisma.io"`: + +```ts +const res = await prisma.post.findMany({ + where: { + author: { + email: { + contains: 'prisma.io', + }, + }, + }, +}) +``` + +### Filter on scalar lists / arrays + +Scalar lists (for example, `String[]`) have a special set of [filter conditions](/orm/reference/prisma-client-reference#scalar-list-filters) - for example, the following query returns all posts where the `tags` array contains `databases`: + +```ts +const posts = await client.post.findMany({ + where: { + tags: { + has: 'databases', + }, + }, +}) +``` + +### Case-insensitive filtering + +Case-insensitive filtering [is available as a feature for the PostgreSQL and MongoDB providers](case-sensitivity#options-for-case-insensitive-filtering). MySQL, MariaDB and Microsoft SQL Server are case-insensitive by default, and do not require a Prisma Client feature to make case-insensitive filtering possible. + +To use case-insensitive filtering, add the `mode` property to a particular filter and specify `insensitive`: + +```ts highlight=5;normal +const users = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + mode: 'insensitive', // Default value: default + }, + name: { + equals: 'Archibald', // Default mode + }, + }, +}) +``` + +See also: [Case sensitivity](case-sensitivity) + +### Filtering FAQs + +#### How does filtering work at the database level? + +For MySQL and PostgreSQL, Prisma Client utilizes the [`LIKE`](https://www.w3schools.com/sql/sql_like.asp) (and [`ILIKE`](https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE)) operator to search for a given pattern. The operators have built-in pattern matching using symbols unique to `LIKE`. The pattern-matching symbols include `%` for zero or more characters (similar to `*` in other regex implementations) and `_` for one character (similar to `.`) + +To match the literal characters, `%` or `_`, make sure you escape those characters. For example: + + +```ts +const users = await prisma.user.findMany({ + where: { + name: { + startsWith: '_benny', + }, + }, +}) +``` + + +The above query will match any user whose name starts with a character followed by `benny` such as `7benny` or `&benny`. If you instead wanted to find any user whose name starts with the literal string `_benny`, you could do: + + +```ts highlight=4 +const users = await prisma.user.findMany({ + where: { + name: { + startsWith: '\\_benny', // note that the `_` character is escaped, preceding `\` with `\` when included in a string + }, + }, +}) +``` + + +## Sorting + +Use [`orderBy`](/orm/reference/prisma-client-reference#orderby) to sort a list of records or a nested list of records by a particular field or set of fields. For example, the following query returns all `User` records sorted by `role` and `name`, **and** each user's posts sorted by `title`: + + + + + +```ts +const usersWithPosts = await prisma.user.findMany({ + orderBy: [ + { + role: 'desc', + }, + { + name: 'desc', + }, + ], + include: { + posts: { + orderBy: { + title: 'desc', + }, + select: { + title: true, + }, + }, + }, +}) +``` + + + + + +```json no-copy +[ + { + "email": "kwame@prisma.io", + "id": 2, + "name": "Kwame", + "role": "USER", + "posts": [ + { + "title": "Prisma in five minutes" + }, + { + "title": "Happy Table Friends: Relations in Prisma" + } + ] + }, + { + "email": "emily@prisma.io", + "id": 5, + "name": "Emily", + "role": "USER", + "posts": [ + { + "title": "Prisma Day 2020" + }, + { + "title": "My first day at Prisma" + }, + { + "title": "All about databases" + } + ] + } +] +``` + + + + + +> **Note**: You can also [sort lists of nested records](relation-queries#filter-a-list-of-relations) +> to retrieve a single record by ID. + +### Sort by relation + +You can also sort by properties of a relation. For example, the following query sorts all posts by the author's email address: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + author: { + email: 'asc', + }, + }, +}) +``` + +### Sort by relation aggregate value + +In [2.19.0](https://github.com/prisma/prisma/releases/2.19.0) and later, you can sort by the **count of related records**. + +For example, the following query sorts users by the number of related posts: + +```ts +const getActiveUsers = await prisma.user.findMany({ + take: 10, + orderBy: { + posts: { + _count: 'desc', + }, + }, +}) +``` + +> **Note**: It is not currently possible to [return the count of a relation](https://github.com/prisma/prisma/issues/5079). + +### Sort by relevance (PostgreSQL) + +In [3.5.0](https://github.com/prisma/prisma/releases/3.5.0) and later, when using PostgreSQL you can sort records by relevance to the query using the `_relevance` keyword. This uses the relevance ranking functions from PostgreSQL's full text search feature, which are explained further in [the PostgreSQL documentation](https://www.postgresql.org/docs/12/textsearch-controls.html). + +Enable order by relevance with the `fullTextSearch` [preview feature](/orm/prisma-client/queries/full-text-search): + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch"] +} +``` + +Ordering by relevance can be used either separately from or together with the `search` filter: `_relevance` is used to order the list, while `search` filters the unordered list. For example, the following query uses `_relevance` to order the list of users by relevance to the search term `'developer'` in their bio, and `search` to filter the list: + +```ts +const getUsersByRelevance = await prisma.user.findMany({ + take: 10, + orderBy: { + _relevance: { + fields: ['bio'], + search: 'developer', + sort: 'asc', + }, + }, +}) +``` + +### Sort with null records first or last + + + +This feature is generally available in version `4.16.0` and later. To use this feature in versions [`4.1.0`](https://github.com/prisma/prisma/releases/tag/4.1.0) to [`4.15.0`](https://github.com/prisma/prisma/releases/tag/4.15.0) the [Preview feature](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature) `orderByNulls` will need to be enabled. + + + + + +Note: Prisma Client does not support this feature for MongoDB. + + + +You can sort the results so that records with `null` fields appear either first or last. + + + +**Note:** You can only sort by nulls on optional [scalar](/orm/prisma-schema/data-model/models#scalar-fields) fields. If you try to sort by nulls on a required or [relation](/orm/prisma-schema/data-model/models#relation-fields) field, Prisma Client throws a [P2009 error](/orm/reference/error-reference#p2009). + + + +Example: If `updatedAt` is an optional field, then the following query sorts posts by `updatedAt`, with null records at the end: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + updatedAt: { sort: 'asc', nulls: 'last' }, + }, +}) +``` + +### Sorting FAQs + +#### Can I perform case-insensitive sorting? + +Follow [issue #841 on GitHub](https://github.com/prisma/prisma-client-js/issues/841). diff --git a/docs/200-orm/200-prisma-client/100-queries/055-pagination.mdx b/docs/200-orm/200-prisma-client/100-queries/055-pagination.mdx new file mode 100644 index 0000000000..315509255a --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/055-pagination.mdx @@ -0,0 +1,224 @@ +--- +title: 'Pagination' +metaTitle: 'Pagination (Reference)' +metaDescription: 'Prisma Client supports both offset pagination and cursor-based pagination. Learn more about the pros and cons of different pagination approaches and how to implement them.' +--- + + + +Prisma Client supports both offset pagination and cursor-based pagination. + + + +## Offset pagination + +Offset pagination uses `skip` and `take` to skip a certain number of results and select a limited range. The following query skips the first 3 `Post` records and returns records 4 - 7: + +```ts line-number +const results = await prisma.post.findMany({ + skip: 3, + take: 4, +}) +``` + +![](/img/offset-skip-take.png) + +To implement pages of results, you would just `skip` the number of pages multiplied by the number of results you show per page. + +### ✔ Pros of offset pagination + +- You can jump to any page immediately. For example, you can `skip` 200 records and `take` 10, which simulates jumping straight to page 21 of the result set (the underlying SQL uses `OFFSET`). This is not possible with cursor-based pagination. +- You can paginate the same result set in any sort order. For example, you can jump to page 21 of a list of `User` records sorted by first name. This is not possible with cursor-based pagination, which requires sorting by a unique, sequential column. + +### ✘ Cons of offset pagination + +- Offset pagination **does not scale** at a database level. For example, if you skip 200,000 records and take the first 10, the database still has to traverse the first 200,000 records before returning the 10 that you asked for - this negatively affects performance. + +### Use cases for offset pagination + +- Shallow pagination of a small result set. For example, a blog interface that allows you to filter `Post` records by author and paginate the results. + +### Example: Filtering and offset pagination + +The following query returns all records where the `email` field contains `prisma.io`. The query skips the first 40 records and returns records 41 - 50. + +```ts line-number +const results = await prisma.post.findMany({ + skip: 40, + take: 10, + where: { + email: { + contains: 'prisma.io', + }, + }, +}) +``` + +### Example: Sorting and offset pagination + +The following query returns all records where the `email` field contains `Prisma`, and sorts the result by the `title` field. The query skips the first 200 records and returns records 201 - 220. + +```ts line-number +const results = await prisma.post.findMany({ + skip: 200, + take: 20, + where: { + email: { + contains: 'Prisma', + }, + }, + orderBy: { + title: 'desc', + }, +}) +``` + +## Cursor-based pagination + +Cursor-based pagination uses `cursor` and `take` to return a limited set of results before or after a given **cursor**. A cursor bookmarks your location in a result set and must be a unique, sequential column - such as an ID or a timestamp. + +The following example returns the first 4 `Post` records that contain the word `"Prisma"` and saves the ID of the last record as `myCursor`: + +> **Note**: Since this is the first query, there is no cursor to pass in. + +```ts line-number +const firstQueryResults = await prisma.post.findMany({ + take: 4, + where: { + title: { + contains: 'Prisma' /* Optional filter */, + }, + }, + orderBy: { + id: 'asc', + }, +}) + +// Bookmark your location in the result set - in this +// case, the ID of the last post in the list of 4. + +|const lastPostInResults = firstQueryResults[3] // Remember: zero-based index! :) +|const myCursor = lastPostInResults.id // Example: 29 +``` + +The following diagram shows the IDs of the first 4 results - or page 1. The cursor for the next query is **29**: + +![](/img/cursor-1.png) + +The second query returns the first 4 `Post` records that contain the word `"Prisma"` **after the supplied cursor** (in other words - IDs that are larger than **29**): + +```ts line-number +const secondQueryResults = await prisma.post.findMany({ + take: 4, + skip: 1, // Skip the cursor +| cursor: { +| id: myCursor, +| }, + where: { + title: { + contains: 'Prisma' /* Optional filter */, + }, + }, + orderBy: { + id: 'asc', + }, +}) + +const lastPostInResults = secondQueryResults[3] // Remember: zero-based index! :) +const myCursor = lastPostInResults.id // Example: 52 +``` + +The following diagram shows the first 4 `Post` records **after** the record with ID **29**. In this example, the new cursor is **52**: + +![](/img/cursor-2.png) + +### FAQ + +#### Do I always have to skip: 1? + +If you do not `skip: 1`, your result set will include your previous cursor. The first query returns four results and the cursor is **29**: + +![](/img/cursor-1.png) + +Without `skip: 1`, the second query returns 4 results after (and _including_) the cursor: + +![](/img/cursor-3.png) + +If you `skip: 1`, the cursor is not included: + +![](/img/cursor-2.png) + +You can choose to `skip: 1` or not depending on the pagination behavior that you want. + +#### Can I guess the value of the cursor? + +If you guess the value of the next cursor, you will page to an unknown location in your result set. Although IDs are sequential, you cannot predict the rate of increment (`2`, `20`, `32` is more likely than `1`, `2`, `3`, particularly in a filtered result set). + +#### Does cursor-based pagination use the concept of a cursor in the underlying database? + +No, cursor pagination does not use cursors in the underlying database ([e.g. PostgreSQL](https://www.postgresql.org/docs/9.2/plpgsql-cursors.html)). + +#### What happens if the cursor value does not exist? + +Using a nonexistent cursor returns `null`. Prisma does not try to locate adjacent values. + +### ✔ Pros of cursor-based pagination + +- Cursor-based pagination **scales**. The underlying SQL does not use `OFFSET`, but instead queries all `Post` records with an ID greater than the value of `cursor`. + +### ✘ Cons of cursor-based pagination + +- You must sort by your cursor, which has to be a unique, sequential column. +- You cannot jump to a specific page using only a cursor. For example, you cannot accurately predict which cursor represents the start of page 400 (page size 20) without first requesting pages 1 - 399. + +### Use cases for cursor-based pagination + +- Infinite scroll - for example, sort blog posts by date/time descending and request 10 blog posts at a time. +- Paging through an entire result set in batches - for example, as part of a long-running data export. + +### Example: Filtering and cursor-based pagination + +```ts line-number +const secondQuery = await prisma.post.findMany({ + take: 4, + cursor: { + id: myCursor, + }, +| where: { +| title: { +| contains: 'Prisma' /* Optional filter */, +| }, + }, + orderBy: { + id: 'asc', + }, +}) +``` + +### Sorting and cursor-based pagination + +Cursor-based pagination requires you to sort by a sequential, unique column such as an ID or a timestamp. This value - known as a cursor - bookmarks your place in the result set and allows you to request the next set. + +### Example: Paging backwards with cursor-based pagination + +To page backwards, set `take` to a negative value. The following query returns 4 `Post` records with an `id` of less than 200, excluding the cursor: + +```ts line-number +const myOldCursor = 200 + +const firstQueryResults = await prisma.post.findMany({ + take: -4, + skip: 1, + cursor: { + id: myOldCursor, + }, + where: { + title: { + contains: 'Prisma' /* Optional filter */, + }, + }, + orderBy: { + id: 'asc', + }, +}) +``` diff --git a/docs/200-orm/200-prisma-client/100-queries/056-aggregation-grouping-summarizing.mdx b/docs/200-orm/200-prisma-client/100-queries/056-aggregation-grouping-summarizing.mdx new file mode 100644 index 0000000000..789f63b9f8 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/056-aggregation-grouping-summarizing.mdx @@ -0,0 +1,725 @@ +--- +title: 'Aggregation, grouping, and summarizing' +metaTitle: 'Aggregation, grouping, and summarizing (Concepts)' +metaDescription: 'Use Prisma Client to aggregate, group by, count, and select distinct.' +tocDepth: 4 +--- + + + +Prisma Client allows you to count records, aggregate number fields, and select distinct field values. + + + +## Aggregate + +Prisma Client allows you to [`aggregate`](/orm/reference/prisma-client-reference#aggregate) on the **number** fields (such as `Int` and `Float`) of a model. The following query returns the average age of all users: + +```ts +const aggregations = await prisma.user.aggregate({ + _avg: { + age: true, + }, +}) + +console.log('Average age:' + aggregations._avg.age) +``` + +You can combine aggregation with filtering and ordering. For example, the following query returns the average age of users: + +- Ordered by `age` ascending +- Where `email` contains `prisma.io` +- Limited to the 10 users + +```ts +const aggregations = await prisma.user.aggregate({ + _avg: { + age: true, + }, + where: { + email: { + contains: 'prisma.io', + }, + }, + orderBy: { + age: 'asc', + }, + take: 10, +}) + +console.log('Average age:' + aggregations._avg.age) +``` + +### Aggregate values are nullable + +In [2.21.0](https://github.com/prisma/prisma/releases/tag/2.21.0) and later, aggregations on **nullable fields** can return a `number` or `null`. This excludes `count`, which always returns 0 if no records are found. + +Consider the following query, where `age` is nullable in the schema: + + + + +```ts +const aggregations = await prisma.user.aggregate({ + _avg: { + age: true, + }, + _count: { + age: true, + }, +}) +``` + + + + +```js no-copy +{ + _avg: { + age: null + }, + _count: { + age: 9 + } +} +``` + + + + +The query returns `{ _avg: { age: null } }` in either of the following scenarios: + +- There are no users +- The value of every user's `age` field is `null` + +This allows you to differentiate between the true aggregate value (which could be zero) and no data. + +## Group by + +Prisma Client's [`groupBy`](/orm/reference/prisma-client-reference#groupby) allows you to **group records** by one or more field values - such as `country`, or `country` and `city` and **perform aggregations** on each group, such as finding the average age of people living in a particular city. `groupBy` is a GA in [2.20.0](https://github.com/prisma/prisma/releases/2.20.0) and later. + +The following video uses `groupBy` to summarize total COVID-19 cases by continent: + +
+ + + +
+ +The following example groups all users by the `country` field and returns the total number of profile views for each country: + + + + +```ts +const groupUsers = await prisma.user.groupBy({ + by: ['country'], + _sum: { + profileViews: true, + }, +}) +``` + + + + +```js no-copy +;[ + { country: 'Germany', _sum: { profileViews: 126 } }, + { country: 'Sweden', _sum: { profileViews: 0 } }, +] +``` + + + + +If you have a single element in the `by` option, you can use the following shorthand syntax to express your query: + +```ts +const groupUsers = await prisma.user.groupBy({ + by: 'country', +}) +``` + +### `groupBy` and filtering + +`groupBy` supports two levels of filtering: `where` and `having`. + +#### Filter records with `where` + +Use `where` to filter all records **before grouping**. The following example groups users by country and sums profile views, but only includes users where the email address contains `prisma.io`: + +```ts highlight=3-7;normal +const groupUsers = await prisma.user.groupBy({ + by: ['country'], + where: { + email: { + contains: 'prisma.io', + }, + }, + _sum: { + profileViews: true, + }, +}) +``` + +#### Filter groups with `having` + +Use `having` to filter **entire groups** by an aggregate value such as the sum or average of a field, not individual records - for example, only return groups where the _average_ `profileViews` is greater than 100: + +```ts highlight=11-17;normal +const groupUsers = await prisma.user.groupBy({ + by: ['country'], + where: { + email: { + contains: 'prisma.io', + }, + }, + _sum: { + profileViews: true, + }, + having: { + profileViews: { + _avg: { + gt: 100, + }, + }, + }, +}) +``` + +##### Use case for `having` + +The primary use case for `having` is to filter on aggregations. We recommend that you use `where` to reduce the size of your data set as far as possible _before_ grouping, because doing so ✔ reduces the number of records the database has to return and ✔ makes use of indices. + +For example, the following query groups all users that are _not_ from Sweden or Ghana: + +```ts highlight=4-6;normal +const fd = await prisma.user.groupBy({ + by: ['country'], + where: { + country: { + notIn: ['Sweden', 'Ghana'], + }, + }, + _sum: { + profileViews: true, + }, + having: { + profileViews: { + _min: { + gte: 10, + }, + }, + }, +}) +``` + +The following query technically achieves the same result, but excludes users from Ghana _after_ grouping. This does not confer any benefit and is not recommended practice. + +```ts highlight=4-6,12-14;normal +const groupUsers = await prisma.user.groupBy({ + by: ['country'], + where: { + country: { + not: 'Sweden', + }, + }, + _sum: { + profileViews: true, + }, + having: { + country: { + not: 'Ghana', + }, + profileViews: { + _min: { + gte: 10, + }, + }, + }, +}) +``` + +> **Note**: Within `having`, you can only filter on aggregate values _or_ fields available in `by`. + +### `groupBy` and ordering + +The following constraints apply when you combine `groupBy` and `orderBy`: + +- You can `orderBy` fields that are present in `by` +- You can `orderBy` aggregate (Preview in 2.21.0 and later) +- If you use `skip` and/or `take` with `groupBy`, you must also include `orderBy` in the query + +#### Order by aggregate group + +You can **order by aggregate group**. Prisma added support for using `orderBy with aggregated groups in relational databases in version [2.21.0](https://github.com/prisma/prisma/releases/2.21.0) and support for MongoDB in [3.4.0](https://github.com/prisma/prisma/releases/3.4.0). + +The following example sorts each `city` group by the number of users in that group (largest group first): + + + + +```ts +const groupBy = await prisma.user.groupBy({ + by: ['city'], + _count: { + city: true, + }, + orderBy: { + _count: { + city: 'desc', + }, + }, +}) +``` + + + + +```js no-copy +;[ + { city: 'Berlin', count: { city: 3 } }, + { city: 'Paris', count: { city: 2 } }, + { city: 'Amsterdam', count: { city: 1 } }, +] +``` + + + + +#### Order by field + +The following query orders groups by country, skips the first two groups, and returns the 3rd and 4th group: + +```ts +const groupBy = await prisma.user.groupBy({ + by: ['country'], + _sum: { + profileViews: true, + }, + orderBy: { + country: 'desc', + }, + skip: 2, + take: 2, +}) +``` + +### `groupBy` FAQ + +#### Can I use `select` with `groupBy`? + +You cannot use `select` with `groupBy`. However, all fields included in `by` are automatically returned. + +#### What is the difference between using `where` and `having` with `groupBy`? + +`where` filters all records before grouping, and `having` filters entire groups and supports filtering on an aggregate field value, such as the average or sum of a particular field in that group. + +#### What is the difference between `groupBy` and `distinct`? + +Both `distinct` and `groupBy` group records by one or more unique field values. `groupBy` allows you to aggregate data within each group - for example, return the average number of views on posts from Denmark - whereas distinct does not. + +## Count + +Use [`count`](/orm/reference/prisma-client-reference#count) to count the number of records or non-`null` field values. The following example query counts all users: + +```ts +const userCount = await prisma.user.count() +``` + +### Count relations + +The ability to count relations is available in version [3.0.1](https://github.com/prisma/prisma/releases/3.0.1) and later. + + + +**For versions before 3.0.1**
+You need to add the [preview feature](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature) `selectRelationCount` and then run `prisma generate`. + +
+ +To return a count of relations (for example, a user's post count), use the `_count` parameter with a nested `select` as shown: + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + include: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```js no-copy +{ id: 1, _count: { posts: 3 } }, +{ id: 2, _count: { posts: 2 } }, +{ id: 3, _count: { posts: 2 } }, +{ id: 4, _count: { posts: 0 } }, +{ id: 5, _count: { posts: 0 } } +``` + + + + +The `_count` parameter: + +- Can be used inside a top-level `include` _or_ `select` +- Can be used with any query that returns records (including `delete`, `update`, and `findFirst`) +- Can return [multiple relation counts](#return-multiple-relation-counts) +- From version 4.3.0, can [filter relation counts](#filter-the-relation-count) + +#### Return a relations count with `include` + +The following query includes each user's post count in the results: + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + include: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```js no-copy +{ id: 1, _count: { posts: 3 } }, +{ id: 2, _count: { posts: 2 } }, +{ id: 3, _count: { posts: 2 } }, +{ id: 4, _count: { posts: 0 } }, +{ id: 5, _count: { posts: 0 } } +``` + + + + +#### Return a relations count with `select` + +The following query uses `select` to return each user's post count and no other fields: + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + select: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```js no-copy +{ + _count: { + posts: 3 + } +} +``` + + + + +#### Return multiple relation counts + +The following query returns a count of each user's `posts` and `recipes` and no other fields: + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + select: { + _count: { + select: { + posts: true, + recipes: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +{ + _count: { + posts: 3, + recipes: 9 + } +} +``` + + + + +#### Filter the relation count + + + +This feature is generally available in version `4.16.0` and later. To use this feature in versions [`4.3.0`](https://github.com/prisma/prisma/releases/tag/4.3.0) to [`4.15.0`](https://github.com/prisma/prisma/releases/tag/4.15.0) the [Preview feature](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature) `filteredRelationCount` will need to be enabled. + + + +Use `where` to filter the fields returned by the `_count` output type. You can do this on [scalar fields](/orm/prisma-schema/data-model/models#scalar-fields), [relation fields](/orm/prisma-schema/data-model/models#relation-fields) and fields of a [composite type](/orm/prisma-schema/data-model/models#defining-composite-types). + +For example, the following query returns all user posts with the title "Hello!": + +```ts +// Count all user posts with the title "Hello!" +await prisma.user.findMany({ + select: { + _count: { + select: { + posts: { where: { title: 'Hello!' } }, + }, + }, + }, +}) +``` + +The following query finds all user posts with comments from an author named "Alice": + +```ts +// Count all user posts that have comments +// whose author is named "Alice" +await prisma.user.findMany({ + select: { + _count: { + select: { + posts: { + where: { comments: { some: { author: { is: { name: 'Alice' } } } } }, + }, + }, + }, + }, +}) +``` + +### Count non-`null` field values + +In [2.15.0](https://github.com/prisma/prisma/releases/2.15.0) and later, you can count all records as well as all instances of non-`null` field values. The following query returns a count of: + +- All `User` records (`_all`) +- All non-`null` `name` values (not distinct values, just values that are not `null`) + + + + +```ts +const userCount = await prisma.user.count({ + select: { + _all: true, // Count all records + name: true, // Count all non-null field values + }, +}) +``` + + + + +```js no-copy +{ _all: 30, name: 10 } +``` + + + + +### Filtered count + +`count` supports filtering. The following example query counts all users with more than 100 profile views: + +```ts +const userCount = await prisma.user.count({ + where: { + profileViews: { + gte: 100, + }, + }, +}) +``` + +The following example query counts a particular user's posts: + +```ts +const postCount = await prisma.post.count({ + where: { + authorId: 29, + }, +}) +``` + +## Select distinct + +Prisma Client allows you to filter duplicate rows from a Prisma Query response to a [`findMany`](/orm/reference/prisma-client-reference#findmany) query using [`distinct`](/orm/reference/prisma-client-reference#distinct) . `distinct` is often used in combination with [`select`](/orm/reference/prisma-client-reference#select) to identify certain unique combinations of values in the rows of your table. + +The following example returns all fields for all `User` records with distinct `name` field values: + +```ts +const result = await prisma.user.findMany({ + where: {}, + distinct: ['name'], +}) +``` + +The following example returns distinct `role` field values (for example, `ADMIN` and `USER`): + + + + +```ts +const distinctRoles = await prisma.user.findMany({ + distinct: ['role'], + select: { + role: true, + }, +}) +``` + + + + +```js no-copy +;[ + { + role: 'USER', + }, + { + role: 'ADMIN', + }, +] +``` + + + + +### `distinct` under the hood + +Prisma's `distinct` option does not use SQL `SELECT DISTINCT`. Instead, `distinct` uses: + +- A `SELECT` query +- In-memory post-processing to select distinct + +It was designed in this way in order to **support `select` and `include`** as part of `distinct` queries. + +The following example selects distinct on `gameId` and `playerId`, ordered by `score`, in order to return **each player's highest score per game**. The query uses `include` and `select` to include additional data: + +- Select `score` (field on `Play`) +- Select related player name (relation between `Play` and `User`) +- Select related game name (relation between `Play` and `Game`) + +
+ +Expand for sample schema + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + play Play[] +} + +model Game { + id Int @id @default(autoincrement()) + name String? + play Play[] +} + +model Play { + id Int @id @default(autoincrement()) + score Int? @default(0) + playerId Int? + player User? @relation(fields: [playerId], references: [id]) + gameId Int? + game Game? @relation(fields: [gameId], references: [id]) +} +``` + +
+ + + + +```ts +const distinctScores = await prisma.play.findMany({ + distinct: ['playerId', 'gameId'], + orderBy: { + score: 'desc', + }, + select: { + score: true, + game: { + select: { + name: true, + }, + }, + player: { + select: { + name: true, + }, + }, + }, +}) +``` + + + + +```code no-copy +[ + { + score: 900, + game: { name: 'Pacman' }, + player: { name: 'Bert Bobberton' } + }, + { + score: 400, + game: { name: 'Pacman' }, + player: { name: 'Nellie Bobberton' } + } +] +``` + + + + +Without `select` and `distinct`, the query would return: + +``` +[ + { + gameId: 2, + playerId: 5 + }, + { + gameId: 2, + playerId: 10 + } +] +``` diff --git a/docs/200-orm/200-prisma-client/100-queries/058-transactions.mdx b/docs/200-orm/200-prisma-client/100-queries/058-transactions.mdx new file mode 100644 index 0000000000..3afed0a1fa --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/058-transactions.mdx @@ -0,0 +1,1334 @@ +--- +title: 'Transactions and batch queries' +metaTitle: 'Transactions and batch queries (Reference)' +metaDescription: 'This page explains the transactions API of Prisma Client.' +tocDepth: 3 +--- + + + +A database transaction refers to a sequence of read/write operations that are _guaranteed_ to either succeed or fail as a whole. This section describes the ways in which the Prisma Client API supports transactions. + +- For more in-depth examples and use cases, refer to the 📖 [transactions guide](/orm/prisma-client/queries/transactions). +- For information about transactions in general and the reasoning behind Prisma's current solutions, see [Blog: How Prisma supports transactions](https://www.prisma.io/blog/how-prisma-supports-transactions-x45s1d5l0ww1). + + + +## Transactions overview + + + +Before Prisma version 4.4.0, you could not set isolation levels on transactions. The isolation level in your database configuration always applied. + + + +Developers take advantage of the safety guarantees provided by the database by wrapping the operations in a transaction. These guarantees are often summarized using the ACID acronym: + +- **Atomic**: Ensures that either _all_ or _none_ operations of the transactions succeed. The transaction is either _committed_ successfully or _aborted_ and _rolled back_. +- **Consistent**: Ensures that the states of the database before and after the transaction are _valid_ (i.e. any existing invariants about the data are maintained). +- **Isolated**: Ensures that concurrently running transactions have the same effect as if they were running in serial. +- **Durability**: Ensures that after the transaction succeeded, any writes are being stored persistently. + +While there's a lot of ambiguity and nuance to each of these properties (for example, consistency could actually be considered an _application-level responsibility_ rather than a database property or isolation is typically guaranteed in terms of stronger and weaker _isolation levels_), overall they serve as a good high-level guideline for expectations developers have when thinking about database transactions. + +> "Transactions are an abstraction layer that allows an application to pretend that certain concurrency problems and certain kinds of hardware and software faults don’t exist. A large class of errors is reduced down to a simple transaction abort, and the application just needs to try again." [Designing Data-Intensive Applications](https://dataintensive.net/), [Martin Kleppmann](https://twitter.com/martinkl) + +Prisma Client supports six different ways of handling transactions for three different scenarios: + +| Scenario | Available techniques | +| :------------------ | :-------------------------------------------------------------------------------------------------------------- | +| Dependent writes |
  • Nested writes
| +| Independent writes |
  • `$transaction([])` API
  • Batch operations
| +| Read, modify, write |
  • Idempotent operations
  • Optimistic concurrency control
  • Interactive transactions
| + +The technique you choose depends on your particular use case. + +> **Note**: For the purposes of this guide, _writing_ to a database encompasses creating, updating, and deleting data. + +## About transactions in Prisma + +Prisma provides the following options for using transactions: + +- [Nested writes](#nested-writes): use the Prisma Client API to process multiple operations on one or more related records inside the same transaction. +- [Batch / bulk transactions](#batchbulk-operations): process one or more operations in bulk with `updateMany`, `deleteMany`, and `createMany`. +- The `$transaction` API in Prisma Client: + - [Sequential operations](#sequential-prisma-client-operations): pass an array of Prisma Client queries to be executed sequentially inside a transaction, using `$transaction(queries: PrismaPromise[]): Promise`. + - [Interactive transactions](#interactive-transactions): pass a function that can contain user code including Prisma Client queries, non-Prisma code and other control flow to be executed in a transaction, using `$transaction(fn: (prisma: PrismaClient) => R, options?: object): R` + +## Nested writes + +A [nested write](relation-queries#nested-writes) lets you perform a single Prisma Client API call with multiple _operations_ that touch multiple [_related_](/orm/prisma-schema/data-model/relations) records. For example, creating a _user_ together with a _post_ or updating an _order_ together with an _invoice_. Prisma Client ensures that all operations succeed or fail as a whole. + +The following example demonstrates a nested write with `create`: + +```ts +// Create a new user with two posts in a +// single transaction +const newUser: User = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + posts: { + create: [ + { title: 'Join the Prisma Slack on https://slack.prisma.io' }, + { title: 'Follow @prisma on Twitter' }, + ], + }, + }, +}) +``` + +The following example demonstrates a nested write with `update`: + +```ts +// Change the author of a post in a single transaction +const updatedPost: Post = await prisma.post.update({ + where: { id: 42 }, + data: { + author: { + connect: { email: 'alice@prisma.io' }, + }, + }, +}) +``` + +> Refer to the 📖 [transactions guide](/orm/prisma-client/queries/transactions#nested-writes) for more examples. + +## Batch/bulk operations + +The following bulk operations run as transactions: + +- `deleteMany` +- `updateMany` +- `createMany` + +> Refer to the 📖 [transactions guide](/orm/prisma-client/queries/transactions#bulk-operations) for more examples. + +## The `$transaction` API + +The `$transaction` API can be used in two ways: + +- [Sequential operations](#sequential-prisma-client-operations): Pass an array of Prisma Client queries to be executed sequentially inside of a transaction. + + `$transaction(queries: PrismaPromise[]): Promise` + +- [Interactive transactions](#interactive-transactions): Pass a function that can contain user code including Prisma Client queries, non-Prisma code and other control flow to be executed in a transaction. + + `$transaction(fn: (prisma: PrismaClient) => R): R` + +### Sequential Prisma Client operations + +The following query returns all posts that match the provided filter as well as a count of all posts: + +```ts +const [posts, totalPosts] = await prisma.$transaction([ + prisma.post.findMany({ where: { title: { contains: 'prisma' } } }), + prisma.post.count(), +]) +``` + +You can also use raw queries inside of a `$transaction`: + + + + + +```ts +const [userList, updateUser] = await prisma.$transaction([ + prisma.$queryRaw`SELECT 'title' FROM User`, + prisma.$executeRaw`UPDATE User SET name = 'Hello' WHERE id = 2;`, +]) +``` + + + + + +```ts +const [findRawData, aggregateRawData, commandRawData] = + await prisma.$transaction([ + prisma.user.findRaw({ + filter: { age: { $gt: 25 } }, + }), + prisma.user.aggregateRaw({ + pipeline: [ + { $match: { status: 'registered' } }, + { $group: { _id: '$country', total: { $sum: 1 } } }, + ], + }), + prisma.$runCommandRaw({ + aggregate: 'User', + pipeline: [ + { $match: { name: 'Bob' } }, + { $project: { email: true, _id: false } }, + ], + explain: false, + }), + ]) +``` + + + + + +Instead of immediately awaiting the result of each operation when it's performed, the operation itself is stored in a variable first which later is submitted to the database with a method called `$transaction`. Prisma Client will ensure that either all three `create` operations succeed or none of them succeed. + +> **Note**: Operations are executed according to the order they are placed in the transaction. Using a query in a transaction does not influence the order of operations in the query itself. +> +> Refer to the 📖 [transactions guide](/orm/prisma-client/queries/transactions#transaction-api) for more examples. + +From version 4.4.0, the sequential operations transaction API has a second parameter. You can use the following optional configuration option in this parameter: + +- `isolationLevel`: Sets the [transaction isolation level](#transaction-isolation-level). By default this is set to the value currently configured in your database. + +For example: + +```ts +await prisma.$transaction( + [ + prisma.resource.deleteMany({ where: { name: 'name' } }), + prisma.resource.createMany({ data }), + ], + { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, // optional, default defined by database configuration + } +) +``` + +### Interactive transactions + +Sometimes you need more control over what queries execute within a transaction. Interactive transactions are meant to provide you with an escape hatch. + + + +Interactive transactions have been generally available from version 4.7.0. + +If you use interactive transactions in preview from version 2.29.0 to 4.6.1 (included), you need to add the `interactiveTransactions` preview feature to the generator block of your Prisma schema. + + + +To use interactive transactions, you can pass an async function into [`$transaction`](/orm/prisma-client/queries/transactions#transaction-api). + +The first argument passed into this async function is an instance of Prisma Client. Below, we will call this instance `tx`. Any Prisma call invoked on this `tx` instance is encapsulated into the transaction. + +Let's look at an example: + +Imagine that you are building an online banking system. One of the actions to perform is to send money from one person to another. + +As experienced developers, we want to make sure that during the transfer, + +- the amount doesn't disappear +- the amount isn't doubled + +This is a great use-case for interactive transactions because we need to perform logic in-between the writes to check the balance. + +In the example below, Alice and Bob each have $100 in their account. If they try to send more money than they have, the transfer is rejected. + +Alice is expected to be able to make 1 transfer for $100 while the other transfer would be rejected. This would result in Alice having $0 and Bob having $200. + +```tsx +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() + +function transfer(from: string, to: string, amount: number) { + return prisma.$transaction(async (tx) => { + // 1. Decrement amount from the sender. + const sender = await tx.account.update({ + data: { + balance: { + decrement: amount, + }, + }, + where: { + email: from, + }, + }) + + // 2. Verify that the sender's balance didn't go below zero. + if (sender.balance < 0) { + throw new Error(`${from} doesn't have enough to send ${amount}`) + } + + // 3. Increment the recipient's balance by amount + const recipient = await tx.account.update({ + data: { + balance: { + increment: amount, + }, + }, + where: { + email: to, + }, + }) + + return recipient + }) +} + +async function main() { + // This transfer is successful + await transfer('alice@prisma.io', 'bob@prisma.io', 100) + // This transfer fails because Alice doesn't have enough funds in her account + await transfer('alice@prisma.io', 'bob@prisma.io', 100) +} + +main() +``` + +In the example above, both `update` queries run within a database transaction. When the application reaches the end of the function, the transaction is **committed** to the database. + +If your application encounters an error along the way, the async function will throw an exception and automatically **rollback** the transaction. + +To catch the exception, you can wrap `$transaction` in a try-catch block: + +```js +try { + await prisma.$transaction(async (tx) => { + // Code running in a transaction... + }) +} catch (err) { + // Handle the rollback... +} +``` + +The transaction API has a second parameter. For interactive transactions, you can use the following optional configuration options in this parameter: + +- `maxWait`: The maximum amount of time Prisma Client will wait to acquire a transaction from the database. The default value is 2 seconds. +- `timeout`: The maximum amount of time the interactive transaction can run before being canceled and rolled back. The default value is 5 seconds. +- `isolationLevel`: Sets the [transaction isolation level](#transaction-isolation-level). By default this is set to the value currently configured in your database. + +For example: + +```jsx +await prisma.$transaction( + async (tx) => { + // Code running in a transaction... + }, + { + maxWait: 5000, // default: 2000 + timeout: 10000, // default: 5000 + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, // optional, default defined by database configuration + } +) +``` + + + +**Use interactive transactions with caution**. Keeping transactions +open for a long time hurts database performance and can even cause deadlocks. +Try to avoid performing network requests and executing slow queries inside your +transaction functions. We recommend you get in and out as quick as possible! + + + +### Transaction isolation level + + + +This feature is not available on MongoDB, because MongoDB does not support isolation levels. + + + +You can set the transaction [isolation level](https://www.prisma.io/dataguide/intro/database-glossary#isolation-levels) for transactions. + + + +This is available in the following Prisma versions for interactive transactions from version 4.2.0, for sequential operations from version 4.4.0. + +In versions before 4.2.0 (for interactive transactions), or 4.4.0 (for sequential operations), you cannot configure the transaction isolation level at a Prisma level. Prisma does not explicitly set the isolation level, so the [isolation level configured in your database](#database-specific-information-on-isolation-levels) is used. + + + +#### Set the isolation level + +To set the transaction isolation level, use the `isolationLevel` option in the second parameter of the API. + +For sequential operations: + +```ts +await prisma.$transaction( + [ + // Prisma Client operations running in a transaction... + ], + { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, // optional, default defined by database configuration + } +) +``` + +For an interactive transaction: + +```jsx +await prisma.$transaction( + async (prisma) => { + // Code running in a transaction... + }, + { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, // optional, default defined by database configuration + maxWait: 5000, // default: 2000 + timeout: 10000, // default: 5000 + } +) +``` + +#### Supported isolation levels + +Prisma Client supports the following isolation levels if they are available in the underlying database: + +- `ReadUncommitted` +- `ReadCommitted` +- `RepeatableRead` +- `Snapshot` +- `Serializable` + +The isolation levels available for each database connector are as follows: + +| Database | `ReadUncommitted` | `ReadCommitted` | `RepeatableRead` | `Snapshot` | `Serializable` | +| ----------- | ----------------- | --------------- | ---------------- | ---------- | -------------- | +| PostgreSQL | ✔️ | ✔️ | ✔️ | No | ✔️ | +| MySQL | ✔️ | ✔️ | ✔️ | No | ✔️ | +| SQL Server | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| CockroachDB | No | No | No | No | ✔️ | +| SQLite | No | No | No | No | ✔️ | + +By default, Prisma Client sets the isolation level to the value currently configured in your database. + +The isolation levels configured by default in each database are as follows: + +| Database | Default | +| ----------- | ---------------- | +| PostgreSQL | `ReadCommitted` | +| MySQL | `RepeatableRead` | +| SQL Server | `ReadCommitted` | +| CockroachDB | `Serializable` | +| SQLite | `Serializable` | + +#### Database-specific information on isolation levels + +See the following resources: + +- [Transaction isolation levels in PostgreSQL](https://www.postgresql.org/docs/9.3/runtime-config-client.html#GUC-DEFAULT-TRANSACTION-ISOLATION) +- [Transaction isolation levels in Microsoft SQL Server](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-ver15) +- [Transaction isolation levels in MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html) + +CockroachDB and SQLite only support the `Serializable` isolation level. + +### Transaction timing issues + + + +- The solution in this section does not apply to MongoDB, because MongoDB does not support [isolation levels](https://www.prisma.io/dataguide/intro/database-glossary#isolation-levels). +- The timing issues discussed in this section do not apply to CockroachDB and SQLite, because these databases only support the highest `Serializable` isolation level. + + + +When two or more transactions run concurrently in certain [isolation levels](https://www.prisma.io/dataguide/intro/database-glossary#isolation-levels), timing issues can cause write conflicts or deadlocks, such as the violation of unique constraints. For example, consider the following sequence of events where Transaction A and Transaction B both attempt to execute a `deleteMany` and a `createMany` operation: + +1. Transaction B: `createMany` operation creates a new set of rows. +1. Transaction B: The application commits transaction B. +1. Transaction A: `createMany` operation. +1. Transaction A: The application commits transaction A. The new rows conflict with the rows that transaction B added at step 2. + +This conflict can occur at the isolation level `ReadCommited`, which is the default isolation level in PostgreSQL and Microsoft SQL Server. To avoid this problem, you can set a higher isolation level (`RepeatableRead` or `Serializable`). You can set the isolation level on a transaction. This overrides your database isolation level for that transaction. + +To avoid transaction write conflicts and deadlocks on a transaction: + +1. On your transaction, use the `isolationLevel` parameter to `Prisma.TransactionIsolationLevel.Serializable`. + + This ensures that your application commits multiple concurrent or parallel transactions as if they were run serially. When a transaction fails due to a write conflict or deadlock, Prisma Client returns a [P2034 error](/orm/reference/error-reference#p2034). + +2. In your application code, add a retry around your transaction to handle any P2034 errors, as shown in this example: + + ```ts + import { Prisma, PrismaClient } from '@prisma/client' + + const prisma = new PrismaClient() + async function main() { + const MAX_RETRIES = 5 + let retries = 0 + + let result + while (retries < MAX_RETRIES) { + try { + result = await prisma.$transaction( + [ + prisma.user.deleteMany({ + where: { + /** args */ + }, + }), + prisma.post.createMany({ + data: { + /** args */ + }, + }), + ], + { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, + } + ) + break + } catch (error) { + if (error.code === 'P2034') { + retries++ + continue + } + throw error + } + } + } + ``` + +## Dependent writes + +Writes are considered **dependent** on each other if: + +- Operations depend on the result of a preceding operation (for example, the database generating an ID) + +The most common scenario is creating a record and using the generated ID to create or update a related record. Examples include: + +- Creating a user and two related blog posts (a one-to-many relationship) - the author ID must be known before creating blog posts +- Creating a team and assigning members (a many-to-many relationship) - the team ID must be known before assigning members + +Dependent writes must succeed together in order to maintain data consistency and prevent unexpected behavior, such as blog post without an author or a team without members. + +### Nested writes + +Prisma's solution to dependent writes is the **nested writes** feature, which is supported by `create` and `update`. The following nested write creates one user and two blog posts: + +```ts +const nestedWrite = await prisma.user.create({ + data: { + email: 'imani@prisma.io', + posts: { + create: [ + { title: 'My first day at Prisma' }, + { title: 'How to configure a unique constraint in PostgreSQL' }, + ], + }, + }, +}) +``` + +If any operation fails, Prisma rolls back the entire transaction. Nested writes are not currently supported by top-level bulk operations like `client.user.deleteMany` and `client.user.updateMany`. + +#### When to use nested writes + +Consider using nested writes if: + +- ✔ You want to create two or more records related by ID at the same time (for example, create a blog post and a user) +- ✔ You want to update and create records related by ID at the same time (for example, change a user's name and create a new blog post) + +:::tip + +If you [pre-compute your IDs, you can choose between a nested write or using the `$transaction([])` API](#scenario-pre-computed-ids-and-the-transaction-api). + +::: + +#### Scenario: Sign-up flow + +Consider the Slack sign-up flow, which: + +1. Creates a team +2. Adds one user to that team, which automatically becomes that team's administrator + +This scenario can be represented by the following schema - note that users can belong to many teams, and teams can have many users (a many-to-many relationship): + +```prisma +model Team { + id Int @id @default(autoincrement()) + name String + members User[] // Many team members +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + teams Team[] // Many teams +} +``` + +The most straightforward approach is to create a team, then create and attach a user to that team: + +```ts +// Create a team +const team = await prisma.team.create({ + data: { + name: 'Aurora Adventures', + }, +}) + +// Create a user and assign them to the team +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + team: { + connect: { + id: team.id, + }, + }, + }, +}) +``` + +However, this code has a problem - consider the following scenario: + +1. Creating the team succeeds - "Aurora Adventures" is now taken +2. Creating and connecting the user fails - the team "Aurora Adventures" exists, but has no users +3. Going through the sign-up flow again and attempting to recreate "Aurora Adventures" fails - the team already exists + +Creating a team and adding a user should be one atomic operation that **succeeds or fails as a whole**. + +To implement atomic writes in a low-level database clients, you must wrap your inserts in `BEGIN`, `COMMIT` and `ROLLBACK` statements. Prisma Client solves the problem with [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes). The following query creates a team, creates a user, and connects the records in a single transaction: + +```ts +const team = await prisma.team.create({ + data: { + name: 'Aurora Adventures', + members: { + create: { + email: 'alice@prisma.io', + }, + }, + }, +}) +``` + +Furthermore, if an error occurs at any point, Prisma Client rolls back the entire transaction. + +#### Nested writes FAQs + +##### Why can't I use the `$transaction([])` API to solve the same problem? + +The `$transaction([])` API does not allow you to pass IDs between distinct operations. In the following example, `createUserOperation.id` is not available yet: + +```ts highlight=12;delete +const createUserOperation = prisma.user.create({ + data: { + email: 'ebony@prisma.io', + }, +}) + +const createTeamOperation = prisma.team.create({ + data: { + name: 'Aurora Adventures', + members: { + connect: { + id: createUserOperation.id, // Not possible, ID not yet available + }, + }, + }, +}) + +await prisma.$transaction([createUserOperation, createTeamOperation]) +``` + +##### Nested writes support nested updates, but updates are not dependent writes - should I use the `$transaction([])` API? + +It is correct to say that because you know the ID of the team, you can update the team and its team members independently within a `$transaction([])`. The following example performs both operations in a `$transaction([])`: + +```ts +const updateTeam = prisma.team.update({ + where: { + id: 1, + }, + data: { + name: 'Aurora Adventures Ltd', + }, +}) + +const updateUsers = prisma.user.updateMany({ + where: { + teams: { + some: { + id: 1, + }, + }, + name: { + equals: null, + }, + }, + data: { + name: 'Unknown User', + }, +}) + +await prisma.$transaction([updateUsers, updateTeam]) +``` + +However, you can achieve the same result with a nested write: + +```ts +const updateTeam = await prisma.team.update({ + where: { + id: 1, + }, + data: { + name: 'Aurora Adventures Ltd', // Update team name + members: { + updateMany: { + // Update team members that do not have a name + data: { + name: 'Unknown User', + }, + where: { + name: { + equals: null, + }, + }, + }, + }, + }, +}) +``` + +##### Can I perform multiple nested writes - for example, create two new teams and assign users? + +Yes, but this is a combination of scenarios and techniques: + +- Creating a team and assigning users is a dependent write - use nested writes +- Creating all teams and users at the same time is an independent write because team/user combination #1 and team/user combination #2 are unrelated writes - use the `$transaction([])` API + +```ts +// Nested write +const createOne = prisma.team.create({ + data: { + name: 'Aurora Adventures', + members: { + create: { + email: 'alice@prisma.io', + }, + }, + }, +}) + +// Nested write +const createTwo = prisma.team.create({ + data: { + name: 'Cool Crew', + members: { + create: { + email: 'elsa@prisma.io', + }, + }, + }, +}) + +// $transaction([]) API +await prisma.$transaction([createTwo, createOne]) +``` + +## Independent writes + +Writes are considered **independent** if they do not rely on the result of a previous operation. The following groups of independent writes can occur in any order: + +- Updating the status field of a list of orders to "Dispatched" +- Marking a list of emails as "Read" + +> **Note**: Independent writes may have to occur in a specific order if constraints are present - for example, you must delete blog posts before the blog author if the post have a mandatory `authorId` field. However, they are still considered independent writes because no operations depend on the _result_ of a previous operation, such as the database returning a generated ID. + +Depending on your requirements, Prisma Client has four options for handling independent writes that should succeed or fail together. + +### Bulk operations + +Bulk writes allow you to write multiple records of the same type in a single transaction - if any operation fails, Prisma rolls back the entire transaction. Prisma currently supports: + +- `updateMany` +- `deleteMany` +- `createMany` + +#### When to use bulk operations + +Consider bulk operations as a solution if: + +- ✔ You want to update a batch of the _same type_ of record, like a batch of emails + +#### Scenario: Marking emails as read + +You are building a service like gmail.com, and your customer wants a **"Mark as read"** feature that allows users to mark all emails as read. Each update to the status of an email is an independent write because the emails do not depend on one another - for example, the "Happy Birthday! 🍰" email from your aunt is unrelated to the promotional email from IKEA. + +In the following schema, a `User` can have many received emails (a one-to-many relationship): + +```ts +model User { + id Int @id @default(autoincrement()) + email String @unique + receivedEmails Email[] // Many emails +} + +model Email { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int + subject String + body String + unread Boolean +} +``` + +Based on this schema, you can use `updateMany` to mark all unread emails as read: + +```ts +await prisma.email.updateMany({ + where: { + user: { + id: 10, + }, + unread: true, + }, + data: { + unread: false, + }, +}) +``` + +#### Can I use nested writes with bulk operations? + +No - neither `updateMany` nor `deleteMany` currently supports nested writes. For example, you cannot delete multiple teams and all of their members (a cascading delete): + +```ts highlight=8;delete +await prisma.team.deleteMany({ + where: { + id: { + in: [2, 99, 2, 11], + }, + }, + data: { + members: {}, // Cannot access members here + }, +}) +``` + +#### Can I use bulk operations with the `$transaction([])` API? + +Yes - for example, you can include multiple `deleteMany` operations inside a `$transaction([])`. + +### `$transaction([])` API + +The `$transaction([])` API is generic solution to independent writes that allows you to run multiple operations as a single, atomic operation - if any operation fails, Prisma rolls back the entire transaction. + +Its also worth noting that operations are executed according to the order they are placed in the transaction. + +```ts +await prisma.$transaction([iRunFirst, iRunSecond, iRunThird]) +``` + +> **Note**: Using a query in a transaction does not influence the order of operations in the query itself. + +As Prisma Client evolves, use cases for the `$transaction([])` API will increasingly be replaced by more specialized bulk operations (such as `createMany`) and nested writes. + +#### When to use the `$transaction([])` API + +Consider the `$transaction([])` API if: + +- ✔ You want to update a batch that includes different types of records, such as emails and users. The records do not need to be related in any way. +- ✔ You want to batch raw SQL queries (`$executeRaw`) - for example, for features that Prisma Client does not yet support. + +#### Scenario: Privacy legislation + +GDPR and other privacy legislation give users the right to request that an organization deletes all of their personal data. In the following example schema, a `User` can have many posts and private messages: + +```prisma +model User { + id Int @id @default(autoincrement()) + posts Post[] + privateMessages PrivateMessage[] +} + +model Post { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int + title String + content String +} + +model PrivateMessage { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int + message String +} +``` + +If a user invokes the right to be forgotten, we must delete three records: the user record, private messages, and posts. It is critical that _all_ delete operations succeed together or not at all, which makes this a use case for a transaction. However, using a single bulk operation like `deleteMany` is not possible in this scenario because we need to delete across three models. Instead, we can use the `$transaction([])` API to run three operations together - two `deleteMany` and one `delete`: + +```ts +const id = 9 // User to be deleted + +const deletePosts = prisma.post.deleteMany({ + where: { + userId: id, + }, +}) + +const deleteMessages = prisma.privateMessage.deleteMany({ + where: { + userId: id, + }, +}) + +const deleteUser = prisma.user.delete({ + where: { + id: id, + }, +}) + +await prisma.$transaction([deletePosts, deleteMessages, deleteUser]) // Operations succeed or fail together +``` + +#### Scenario: Pre-computed IDs and the `$transaction([])` API + +Dependent writes are not supported by the `$transaction([])` API - if operation A relies on the ID generated by operation B, use [nested writes](#nested-writes). However, if you _pre-computed_ IDs (for example, by generating GUIDs), your writes become independent. Consider the sign-up flow from the nested writes example: + +```ts +await prisma.team.create({ + data: { + name: 'Aurora Adventures', + members: { + create: { + email: 'alice@prisma.io', + }, + }, + }, +}) +``` + +Instead of auto-generating IDs, change the `id` fields of `Team` and `User` to a `String` (if you do not provide a value, a UUID is generated automatically). This example uses UUIDs: + +```prisma highlight=2,9;delete|3,10;add +model Team { + id Int @id @default(autoincrement()) + id String @id @default(uuid()) + name String + members User[] +} + +model User { + id Int @id @default(autoincrement()) + id String @id @default(uuid()) + email String @unique + teams Team[] +} +``` + +Refactor the sign-up flow example to use the `$transaction([])` API instead of nested writes: + +```ts +import { v4 } from 'uuid' + +const teamID = v4() +const userID = v4() + +await prisma.$transaction([ + prisma.user.create({ + data: { + id: userID, + email: 'alice@prisma.io', + team: { + id: teamID, + }, + }, + }), + prisma.team.create({ + data: { + id: teamID, + name: 'Aurora Adventures', + }, + }), +]) +``` + +Technically you can still use nested writes with pre-computed APIs if you prefer that syntax: + +```ts +import { v4 } from 'uuid' + +const teamID = v4() +const userID = v4() + +await prisma.team.create({ + data: { + id: teamID, + name: 'Aurora Adventures', + members: { + create: { + id: userID, + email: 'alice@prisma.io', + team: { + id: teamID, + }, + }, + }, + }, +}) +``` + +There's no compelling reason to switch to manually generated IDs and the `$transaction([])` API if you are already using auto-generated IDs and nested writes. + +## Read, modify, write + +In some cases you may need to perform custom logic as part of an atomic operation - also known as the [read-modify-write pattern](https://en.wikipedia.org/wiki/Read%E2%80%93modify%E2%80%93write). The following is an example of the read-modify-write pattern: + +- Read a value from the database +- Run some logic to manipulate that value (for example, contacting an external API) +- Write the value back to the database + +All operations should **succeed or fail together** without making unwanted changes to the database, but you do not necessarily need to use an actual database transaction. This section of the guide describes two ways to work with Prisma Client and the read-modify-write pattern: + +- Designing idempotent APIs +- Optimistic concurrency control + +### Idempotent APIs + +Idempotency is the ability to run the same logic with the same parameters multiple times with the same result: the **effect on the database** is the same whether you run the logic once or one thousand times. For example: + +- **NOT IDEMPOTENT**: Upsert (update-or-insert) a user in the database with email address `"letoya@prisma.io"`. The `User` table **does not** enforce unique email addresses. The effect on the database is different if you run the logic once (one user created) or ten times (ten users created). +- **IDEMPOTENT**: Upsert (update-or-insert) a user in the database with the email address `"letoya@prisma.io"`. The `User` table **does** enforce unique email addresses. The effect on the database is the same if you run the logic once (one user created) or ten times (existing user is updated with the same input). + +Idempotency is something you can and should actively design into your application wherever possible. + +#### When to design an idempotent API + +- ✔ You need to be able to retry the same logic without creating unwanted side-effects in the databases + +#### Scenario: Upgrading a Slack team + +You are creating an upgrade flow for Slack that allows teams to unlock paid features. Teams can choose between different plans and pay per user, per month. You use Stripe as your payment gateway, and extend your `Team` model to store a `stripeCustomerId`. Subscriptions are managed in Stripe. + +```prisma highlight=5;normal +model Team { + id Int @id @default(autoincrement()) + name String + User User[] + stripeCustomerId String? +} +``` + +The upgrade flow looks like this: + +1. Count the number of users +2. Create a subscription in Stripe that includes the number of users +3. Associate the team with the Stripe customer ID to unlock paid features + +```ts +const teamId = 9 +const planId = 'plan_id' + +// Count team members +const numTeammates = await prisma.user.count({ + where: { + teams: { + some: { + id: teamId, + }, + }, + }, +}) + +// Create a customer in Stripe for plan-9454549 +const customer = await stripe.customers.create({ + externalId: teamId, + plan: planId, + quantity: numTeammates, +}) + +// Update the team with the customer id to indicate that they are a customer +// and support querying this customer in Stripe from our application code. +await prisma.team.update({ + data: { + customerId: customer.id, + }, + where: { + id: teamId, + }, +}) +``` + +This example has a problem: you can only run the logic _once_. Consider the following scenario: + +1. Stripe creates a new customer and subscription, and returns a customer ID +2. Updating the team **fails** - the team is not marked as a customer in the Slack database +3. The customer is charged by Stripe, but paid features are not unlocked in Slack because the team lacks a valid `customerId` +4. Running the same code again either: + + - Results in an error because the team (defined by `externalId`) already exists - Stripe never returns a customer ID + - If `externalId` is not subject to a unique constraint, Stripe creates yet another subscription (**not idempotent**) + +You cannot re-run this code in case of an error and you cannot change to another plan without being charged twice. + +The following refactor (highlighted) introduces a mechanism that checks if a subscription already exists, and either creates the description or updates the existing subscription (which will remain unchanged if the input is identical): + +```ts highlight=12-27;normal +// Calculate the number of users times the cost per user +const numTeammates = await prisma.user.count({ + where: { + teams: { + some: { + id: teamId, + }, + }, + }, +}) + +// Find customer in Stripe +let customer = await stripe.customers.get({ externalId: teamID }) + +if (customer) { + // If team already exists, update + customer = await stripe.customers.update({ + externalId: teamId, + plan: 'plan_id', + quantity: numTeammates, + }) +} else { + customer = await stripe.customers.create({ + // If team does not exist, create customer + externalId: teamId, + plan: 'plan_id', + quantity: numTeammates, + }) +} + +// Update the team with the customer id to indicate that they are a customer +// and support querying this customer in Stripe from our application code. +await prisma.team.update({ + data: { + customerId: customer.id, + }, + where: { + id: teamId, + }, +}) +``` + +You can now retry the same logic multiple times with the same input without adverse effect. To further enhance this example, you can introduce a mechanism whereby the subscription is cancelled or temporarily deactivated if the update does not succeed after a set number of attempts. + +### Optimistic concurrency control + +Optimistic concurrency control (OCC) is a model for handling concurrent operations on a single entity that does not rely on 🔒 locking. Instead, we **optimistically** assume that a record will remain unchanged in between reading and writing, and use a concurrency token (a timestamp or version field) to detect changes to a record. + +If a ❌ conflict occurs (someone else has changed the record since you read it), you cancel the transaction. Depending on your scenario, you can then: + +- Re-try the transaction (book another cinema seat) +- Throw an error (alert the user that they are about to overwrite changes made by someone else) + +This section describes how to build your own optimistic concurrency control. See also: Plans for [application-level optimistic concurrency control on GitHub](https://github.com/prisma/prisma/issues/4988) + + + +- If you use version 4.4.0 or earlier, you cannot use optimistic concurrency control on `update` operations, because you cannot filter on non-unique fields. The `version` field you need to use with optimistic concurrency control is a non-unique field. + +- Since version 5.0.0 you are able to [filter on non-unique fields in `update` operations](/orm/reference/prisma-client-reference#filter-on-non-unique-fields-with-userwhereuniqueinput) so that optimistic concurrency control is being used. The feature was also available via the Preview flag `extendedWhereUnique` from versions 4.5.0 to 4.16.2. + + + +#### When to use optimistic concurrency control + +- ✔ You anticipate a high number of concurrent requests (multiple people booking cinema seats) +- ✔ You anticipate that conflicts between those concurrent requests will be rare + +Avoiding locks in a application with a high number of concurrent requests makes the application more resilient to load and more scalable overall. Although locking is not inherently bad, locking in a high concurrency environment can lead to unintended consequences - even if you are locking individual rows, and only for a short amount of time. For more information, see: + +- [Why ROWLOCK Hints Can Make Queries Slower and Blocking Worse in SQL Server](https://littlekendra.com/2016/02/04/why-rowlock-hints-can-make-queries-slower-and-blocking-worse-in-sql-server/) +- [The High Concurrency strategy](https://www.ibm.com/developerworks/library/j-ts5/index.html) + +#### Scenario: Reserving a seat at the cinema + +You are creating a booking system for a cinema. Each movie has a set number of seats. The following schema models movies and seats: + +```ts +model Seat { + id Int @id @default(autoincrement()) + userId Int? + claimedBy User? @relation(fields: [userId], references: [id]) + movieId Int + movie Movie @relation(fields: [movieId], references: [id]) +} + +model Movie { + id Int @id @default(autoincrement()) + name String @unique + seats Seat[] +} +``` + +The following sample code finds the first available seat and assigns that seat to a user: + +```ts +const movieName = 'Hidden Figures' + +// Find first available seat +const availableSeat = await prisma.seat.findFirst({ + where: { + movie: { + name: movieName, + }, + claimedBy: null, + }, +}) + +// Throw an error if no seats are available +if (!availableSeat) { + throw new Error(`Oh no! ${movieName} is all booked.`) +} + +// Claim the seat +await prisma.seat.update({ + data: { + claimedBy: userId, + }, + where: { + id: availableSeat.id, + }, +}) +``` + +However, this code suffers from the "double-booking problem" - it is possible for two people to book the same seats: + +1. Seat 3A returned to Sorcha (`findFirst`) +2. Seat 3A returned to Ellen (`findFirst`) +3. Seat 3A claimed by Sorcha (`update`) +4. Seat 3A claimed by Ellen (`update` - overwrites Sorcha's claim) + +Even though Sorcha has successfully booked the seat, the system ultimately stores Ellen's claim. To solve this problem with optimistic concurrency control, add a `version` field to the seat: + +```prisma highlight=7;normal +model Seat { + id Int @id @default(autoincrement()) + userId Int? + claimedBy User? @relation(fields: [userId], references: [id]) + movieId Int + movie Movie @relation(fields: [movieId], references: [id]) + version Int +} +``` + +Next, adjust the code to check the `version` field before updating: + +```ts highlight=19-38;normal +const userEmail = 'alice@prisma.io' +const movieName = 'Hidden Figures' + +// Find the first available seat +// availableSeat.version might be 0 +const availableSeat = await client.seat.findFirst({ + where: { + Movie: { + name: movieName, + }, + claimedBy: null, + }, +}) + +if (!availableSeat) { + throw new Error(`Oh no! ${movieName} is all booked.`) +} + +// Only mark the seat as claimed if the availableSeat.version +// matches the version we're updating. Additionally, increment the +// version when we perform this update so all other clients trying +// to book this same seat will have an outdated version. +const seats = await client.seat.updateMany({ + data: { + claimedBy: userEmail, + version: { + increment: 1, + }, + }, + where: { + id: availableSeat.id, + version: availableSeat.version, // This version field is the key; only claim seat if in-memory version matches database version, indicating that the field has not been updated + }, +}) + +if (seats.count === 0) { + throw new Error(`That seat is already booked! Please try again.`) +} +``` + +It is now impossible for two people to book the same seat: + +1. Seat 3A returned to Sorcha (`version` is 0) +2. Seat 3A returned to Ellen (`version` is 0) +3. Seat 3A claimed by Sorcha (`version` is incremented to 1, booking succeeds) +4. Seat 3A claimed by Ellen (in-memory `version` (0) does not match database `version` (1) - booking does not succeed) + +### Interactive transactions + +If you have an existing application, it can be a significant undertaking to refactor your application to use optimistic concurrency control. Interactive Transactions offers a useful escape hatch for cases like this. + +To create an interactive transaction, pass an async function into [$transaction](#transaction-api). + +The first argument passed into this async function is an instance of Prisma Client. Below, we will call this instance `tx`. Any Prisma call invoked on this `tx` instance is encapsulated into the transaction. + +In the example below, Alice and Bob each have $100 in their account. If they try to send more money than they have, the transfer is rejected. + +The expected outcome would be for Alice to make 1 transfer for $100 and the other transfer would be rejected. This would result in Alice having $0 and Bob having $200. + +```ts +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() + +async function transfer(from: string, to: string, amount: number) { + return await prisma.$transaction(async (tx) => { + // 1. Decrement amount from the sender. + const sender = await tx.account.update({ + data: { + balance: { + decrement: amount, + }, + }, + where: { + email: from, + }, + }) + + // 2. Verify that the sender's balance didn't go below zero. + if (sender.balance < 0) { + throw new Error(`${from} doesn't have enough to send ${amount}`) + } + + // 3. Increment the recipient's balance by amount + const recipient = tx.account.update({ + data: { + balance: { + increment: amount, + }, + }, + where: { + email: to, + }, + }) + + return recipient + }) +} + +async function main() { + // This transfer is successful + await transfer('alice@prisma.io', 'bob@prisma.io', 100) + // This transfer fails because Alice doesn't have enough funds in her account + await transfer('alice@prisma.io', 'bob@prisma.io', 100) +} + +main() +``` + +In the example above, both `update` queries run within a database transaction. When the application reaches the end of the function, the transaction is **committed** to the database. + +If the application encounters an error along the way, the async function will throw an exception and automatically **rollback** the transaction. + +You can learn more about interactive transactions in our [Transactions and Batch Queries documentation](/orm/prisma-client/queries/transactions#interactive-transactions). + + + +**Use interactive transactions with caution**. Keeping transactions +open for a long time hurts database performance and can even cause deadlocks. +Try to avoid performing network requests and executing slow queries inside your +transaction functions. We recommend you get in and out as quick as possible! + + + +## Conclusion + +Prisma supports multiple ways of handling transactions, either directly through the API or by supporting your ability to introduce optimistic concurrency control and idempotency into your application. If you feel like you have use cases in your application that are not covered by any of the suggested options, please open a [GitHub issue](https://github.com/prisma/prisma/issues/new/choose) to start a discussion. diff --git a/docs/200-orm/200-prisma-client/100-queries/060-full-text-search.mdx b/docs/200-orm/200-prisma-client/100-queries/060-full-text-search.mdx new file mode 100644 index 0000000000..40eba5ca4f --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/060-full-text-search.mdx @@ -0,0 +1,245 @@ +--- +title: 'Full-text search' +metaTitle: 'Full-text search (Preview)' +metaDescription: 'This page explains how to search for text within a field.' +preview: true +--- + + + +Prisma Client supports full-text search for **PostgreSQL** databases in versions 2.30.0 and later, and **MySQL** databases in versions 3.8.0 and later. With full-text search enabled, you can add search functionality to your application by searching for text within a database column. + + + +## Enabling full-text search + +The full-text search API is currently a Preview feature. To enable this feature, carry out the following steps: + +1. Update the [`previewFeatures`](/orm/reference/preview-features) block in your schema to include the `fullTextSearch` preview feature flag: + + ```prisma file=schema.prisma + generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch"] + } + ``` + + For MySQL, you will also need to include the `fullTextIndex` preview feature flag: + + ```prisma file=schema.prisma highlight=3;add + generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch", "fullTextIndex"] + } + ``` + +2. Generate Prisma Client: + + ```terminal copy + npx prisma generate + ``` + +After you regenerate your client, a new `search` field will be available on any `String` fields created on your models. For example, the following search will return all posts that contain the word 'cat'. + +```ts +// All posts that contain the word 'cat'. +const result = await prisma.posts.findMany({ + where: { + body: { + search: 'cat', + }, + }, +}) +``` + +## Querying the database + +The `search` field uses the database's native querying capabilities under the hood. This means that the exact [query operators](https://www.postgresql.org/docs/14/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES) available are also database-specific. + +### PostgreSQL + +The following examples demonstrate the use of the PostgreSQL 'and' (`&`) and 'or' (`|`) operators: + +```ts +// All posts that contain the words 'cat' or 'dog'. +const result = await prisma.posts.findMany({ + where: { + body: { + search: 'cat | dog', + }, + }, +}) + +// All drafts that contain the words 'cat' and 'dog'. +const result = await prisma.posts.findMany({ + where: { + status: 'Draft', + body: { + search: 'cat & dog', + }, + }, +}) +``` + +To get a sense of how the query format works, consider the following text: + +**"The quick brown fox jumps over the lazy dog"** + +Here's how the following queries would match that text: + +| Query | Match? | Description | +| :-------------------------------------- | :----- | :-------------------------------------- | +| `fox & dog` | Yes | The text contains 'fox' and 'dog' | +| `dog & fox` | Yes | The text contains 'dog' and 'fox' | +| `dog & cat` | No | The text contains 'dog' but not 'cat' | +| `!cat` | Yes | 'cat' is not in the text | +| `fox | cat` | Yes | The text contains 'fox' or 'cat' | +| `cat | pig` | No | The text doesn't contain 'cat' or 'pig' | +| `fox <-> dog` | Yes | 'dog' follows 'fox' in the text | +| `dog <-> fox` | No | 'fox' doesn't follow 'dog' in the text | + +For the full range of supported operations, see the [PostgreSQL full text search documentation](https://www.postgresql.org/docs/12/functions-textsearch.html). + +### MySQL + +The following examples demonstrate use of the MySQL 'and' (`+`) and 'not' (`-`) operators: + +```ts +// All posts that contain the words 'cat' or 'dog'. +const result = await prisma.posts.findMany({ + where: { + body: { + search: 'cat dog', + }, + }, +}) + +// All posts that contain the words 'cat' and not 'dog'. +const result = await prisma.posts.findMany({ + where: { + body: { + search: '+cat -dog', + }, + }, +}) + +// All drafts that contain the words 'cat' and 'dog'. +const result = await prisma.posts.findMany({ + where: { + status: 'Draft', + body: { + search: '+cat +dog', + }, + }, +}) +``` + +To get a sense of how the query format works, consider the following text: + +**"The quick brown fox jumps over the lazy dog"** + +Here's how the following queries would match that text: + +| Query | Match? | Description | +| :------------- | :----- | :----------------------------------------------------- | +| `+fox +dog` | Yes | The text contains 'fox' and 'dog' | +| `+dog +fox` | Yes | The text contains 'dog' and 'fox' | +| `+dog -cat` | Yes | The text contains 'dog' but not 'cat' | +| `-cat` | Yes | 'cat' is not in the text | +| `fox dog` | Yes | The text contains 'fox' or 'dog' | +| `-cat -pig` | No | The text does not contain 'cat' or 'pig' | +| `quic*` | Yes | The text contains a word starting with 'quic' | +| `quick fox @2` | Yes | 'fox' starts within a 2 word distance of 'quick' | +| `fox dog @2` | No | 'dog' does not start within a 2 word distance of 'fox' | +| `"jumps over"` | Yes | The text contains the whole phrase 'jumps over' | + +MySQL also has `>`, `<` and `~` operators for altering the ranking order of search results. As an example, consider the following two records: + +**1. "The quick brown fox jumps over the lazy dog"** + +**2. "The quick brown fox jumps over the lazy cat"** + +| Query | Result | Description | +| :---------------- | :----------------------- | :------------------------------------------------------------------------------------------------------ | +| `fox ~cat` | Return 1. first, then 2. | Return all records containing 'fox', but rank records containing 'cat' lower | +| `fox (dog)` | Return 1. first, then 2. | Return all records containing 'fox', but rank records containing 'cat' lower than rows containing 'dog' | + +For the full range of supported operations, see the [MySQL full text search documentation](https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html). + +## Sorting results by `\_relevance` + + + +Sorting by relevance is only available for PostgreSQL and MySQL. + + + +In addition to [Prisma's default `orderBy` behavior](/orm/reference/prisma-client-reference#orderby), full-text search also adds sorting by relevance to a given string or strings. As an example, if you wanted to order posts by their relevance to the term `'database'` in their title, you could use the following: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + _relevance: { + fields: ['title'], + search: 'database', + sort: 'asc' + }, +}) +``` + +## Adding indexes + +### PostgreSQL + +Prisma Client does not currently support using indexes to speed up full text search. There is an existing [GitHub Issue](https://github.com/prisma/prisma/issues/8950) for this. + +### MySQL + +For MySQL, it is necessary to add indexes to any columns you search using the `@@fulltext` argument in the `schema.prisma` file. To do this, the `"fullTextIndex"` preview feature must be enabled. + +In the following example, one full text index is added to the `content` field of the `Blog` model, and another is added to both the `content` and `title` fields together: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch", "fullTextIndex"] +} + +model Blog { + id Int @unique + content String + title String + + @@fulltext([content]) + @@fulltext([content, title]) +} +``` + +The first index allows searching the `content` field for occurrences of the word 'cat': + +```ts +const result = await prisma.blogs.findMany({ + where: { + content: { + search: 'cat', + }, + }, +}) +``` + +The second index allows searching both the `content` and `title` fields for occurrences of the word 'cat' in the `content` and 'food' in the `title`: + +```ts +const result = await prisma.blogs.findMany({ + where: { + content: { + search: 'cat', + }, + title: { + search: 'food', + }, + }, +}) +``` + +However, if you try to search on `title` alone, the search will fail with the error "Cannot find a fulltext index to use for the search" and the message code is `P2030`, because the index requires a search on both fields. diff --git a/docs/200-orm/200-prisma-client/100-queries/061-custom-validation.mdx b/docs/200-orm/200-prisma-client/100-queries/061-custom-validation.mdx new file mode 100644 index 0000000000..2184204496 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/061-custom-validation.mdx @@ -0,0 +1,190 @@ +--- +title: 'Custom validation' +metaTitle: 'Custom validation' +metaDescription: 'This page explains how to add custom validation to Prisma Client' +--- + + + +You can add runtime validation for your user input for Prisma Client queries in one of the following ways: + +- [Prisma Client extensions](/orm/prisma-client/client-extensions) +- A custom function + +You can use any validation library you'd like. The Node.js ecosystem offers a number of high-quality, easy-to-use validation libraries to choose from including: [joi](https://github.com/sideway/joi), [validator.js](https://github.com/validatorjs/validator.js), [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod) and [Superstruct](https://github.com/ianstormtaylor/superstruct). + + + +## Input validation with Prisma Client extensions + +This example adds runtime validation when creating and updating values using a Zod schema to check that the data passed to Prisma Client is valid. + + + +Query extensions do not currently work for nested operations. In this example, validations are only run on the top level data object passed to methods such as `prisma.product.create()`. Validations implemented this way do not automatically run for [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes). + + + + + + + +```ts copy +import { PrismaClient, Prisma } from '@prisma/client' +import { z } from 'zod' + +/** + * Zod schema + */ +export const ProductCreateInput = z.object({ + slug: z + .string() + .max(100) + .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), + name: z.string().max(100), + description: z.string().max(1000), + price: z + .instanceof(Prisma.Decimal) + .refine((price) => price.gte('0.01') && price.lt('1000000.00')), +}) satisfies z.Schema + +/** + * Prisma Client Extension + */ +const prisma = new PrismaClient().$extends({ + query: { + product: { + create({ args, query }) { + args.data = ProductCreateInput.parse(args.data) + return query(args) + }, + update({ args, query }) { + args.data = ProductCreateInput.partial().parse(args.data) + return query(args) + }, + updateMany({ args, query }) { + args.data = ProductCreateInput.partial().parse(args.data) + return query(args) + }, + upsert({ args, query }) { + args.create = ProductCreateInput.parse(args.create) + args.update = ProductCreateInput.partial().parse(args.update) + return query(args) + }, + }, + }, +}) + +async function main() { + /** + * Example usage + */ + // Valid product + const product = await prisma.product.create({ + data: { + slug: 'example-product', + name: 'Example Product', + description: 'Lorem ipsum dolor sit amet', + price: new Prisma.Decimal('10.95'), + }, + }) + + // Invalid product + try { + await prisma.product.create({ + data: { + slug: 'invalid-product', + name: 'Invalid Product', + description: 'Lorem ipsum dolor sit amet', + price: new Prisma.Decimal('-1.00'), + }, + }) + } catch (err: any) { + console.log(err?.cause?.issues) + } +} + +main() +``` + + + + + +```prisma copy +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model Product { + id String @id @default(cuid()) + slug String + name String + description String + price Decimal + reviews Review[] +} + +model Review { + id String @id @default(cuid()) + body String + stars Int + product Product @relation(fields: [productId], references: [id], onDelete: Cascade) + productId String +} +``` + + + + + +The above example uses a Zod schema to validate and parse data provided in a query at runtime before a record is written to the database. + +## Input validation with a custom validation function + +Here's an example using [Superstruct](https://github.com/ianstormtaylor/superstruct) to validate that the data needed to signup a new user is correct: + +```tsx +import { PrismaClient, Prisma, User } from '@prisma/client' +import { assert, object, string, size, refine } from 'superstruct' +import isEmail from 'isemail' + +const prisma = new PrismaClient() + +// Runtime validation +const Signup = object({ + // string and a valid email address + email: refine(string(), 'email', (v) => isEmail.validate(v)), + // password is between 7 and 30 characters long + password: size(string(), 7, 30), + // first name is between 2 and 50 characters long + firstName: size(string(), 2, 50), + // last name is between 2 and 50 characters long + lastName: size(string(), 2, 50), +}) + +type Signup = Omit + +// Signup function +async function signup(input: Signup): Promise { + // Assert that input conforms to Signup, throwing with a helpful + // error message if input is invalid. + assert(input, Signup) + return prisma.user.create({ + data: input.user, + }) +} +``` + +The example above shows how you can create a custom type-safe `signup` function that ensures the input is valid before creating a user. + +## Going further + +- Learn how you can use [Prisma Client extensions](/orm/prisma-client/client-extensions) to add input validation for your queries — [example](https://github.com/prisma/prisma-client-extensions/tree/main/input-validation). +- Learn how you can organize your code better by moving the `signup` function into [a custom model](/orm/prisma-client/queries/custom-models). +- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/3528) to bake user validation into Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case! diff --git a/docs/200-orm/200-prisma-client/100-queries/062-computed-fields.mdx b/docs/200-orm/200-prisma-client/100-queries/062-computed-fields.mdx new file mode 100644 index 0000000000..8947a1a5c3 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/062-computed-fields.mdx @@ -0,0 +1,174 @@ +--- +title: 'Computed fields' +metaTitle: 'Computed fields' +metaDescription: 'This page explains how to add computed fields to Prisma Client' +--- + + + +Computed fields allow you to derive a new field based on existing data. A common example is when you compute a full name from a first and last name. In your database, you may only store the first and last name, but you can define a function that computes a full name by combining the first and last name. This field is read-only and stored in your application's memory, not in your database. + + + +## Using a Prisma Client extension + +The following example illustrates how to create a [Prisma Client extension](/orm/prisma-client/client-extensions) that adds a `fullName` computed field at runtime to the `User` model in a Prisma schema. + + + + + + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient().$extends({ + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute(user) { + return `${user.firstName} ${user.lastName}` + }, + }, + }, + }, +}) + +async function main() { + /** + * Example query containing the `fullName` computed field in the response + */ + const user = await prisma.user.findFirst() +} + +main() +``` + + + + +```js no-copy +{ + id: 1, + firstName: 'Aurelia', + lastName: 'Schneider', + email: 'Jalen_Berge40@hotmail.com', + fullName: 'Aurelia Schneider', +} +``` + + + + + + + + + +```prisma copy +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + firstName String + lastName String + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + content String? + authorId Int? + author User? @relation(fields: [authorId], references: [id]) +} +``` + + + + + +The computed fields are type-safe and can return anything from a concatenated value to complex objects or functions that can act as an instance method for your models. + +## Using a computation function + +Prisma Client does not yet natively support computed fields, but, you can define a function that accepts a generic type as an input then extend that generic to ensure it conforms to a specific structure. Finally, you can return that generic with additional computed fields. Let's see how that might look: + + + + + +```tsx +// Define a type that needs a first and last name +type FirstLastName = { + firstName: string + lastName: string +} + +// Extend the T generic with the fullName attribute +type WithFullName = T & { + fullName: string +} + +// Take objects that satisfy FirstLastName and computes a full name +function computeFullName( + user: User +): WithFullName { + return { + ...user, + fullName: user.firstName + ' ' + user.lastName, + } +} + +async function main() { + const user = await prisma.user.findUnique({ where: 1 }) + const userWithFullName = computeFullName(user) +} +``` + + + + + +```js +function computeFullName(user) { + return { + ...user, + fullName: user.firstName + ' ' + user.lastName, + } +} + +async function main() { + const user = await prisma.user.findUnique({ where: 1 }) + const userWithFullName = computeFullName(user) +} +``` + + + + + +In the TypeScript example above, a `User` generic has been defined that extends the `FirstLastName` type. This means that whatever you pass into `computeFullName` must contain `firstName` and `lastName` keys. + +A `WithFullName` return type has also been defined, which takes whatever `User` is and tacks on a `fullName` string attribute. + +With this function, any object that contains `firstName` and `lastName` keys can compute a `fullName`. Pretty neat, right? + +## Going further + +- Learn how you can use [Prisma Client extensions](/orm/prisma-client/client-extensions) to add a computed field to your schema — [example](https://github.com/prisma/prisma-client-extensions/tree/main/computed-fields). +- Learn how you can move the `computeFullName` function into [a custom model](/orm/prisma-client/queries/custom-models). +- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/3394) to add native support to Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case! diff --git a/docs/200-orm/200-prisma-client/100-queries/063-excluding-fields.mdx b/docs/200-orm/200-prisma-client/100-queries/063-excluding-fields.mdx new file mode 100644 index 0000000000..aaf10c471d --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/063-excluding-fields.mdx @@ -0,0 +1,70 @@ +--- +title: 'Excluding fields' +metaTitle: 'Excluding fields' +metaDescription: 'This page explains how to exclude sensitive fields from Prisma Client' +--- + + + +By default Prisma Client returns all fields from a model. You can use `select` to narrow the result set, but that can be unwieldy if you have a large model and you only want to exclude one or two fields. + +Prisma Client doesn't have a native way of excluding fields yet, but it's easy to create a function that you can use to exclude certain fields in a type-safe way. + + + +## Excluding the password field + +The following is a type-safe `exclude` function returns a user without the `password` field. + + + + + +```tsx +// Exclude keys from user +function exclude( + user: User, + keys: Key[] +): Omit { + return Object.fromEntries( + Object.entries(user).filter(([key]) => !keys.includes(key)) + ) +} + +function main() { + const user = await prisma.user.findUnique({ where: 1 }) + const userWithoutPassword = exclude(user, ['password']) +} +``` + + + + + +```js +// Exclude keys from user +function exclude(user, keys) { + return Object.fromEntries( + Object.entries(user).filter(([key]) => !keys.includes(key)) + ); +} + +function main() { + const user = await prisma.user.findUnique({ where: 1 }) + const userWithoutPassword = exclude(user, ['password']) +} +``` + + + + + +In the TypeScript example, we've provided two generics: `User` and `Key`. The `Key` generic is defined as the keys of a `User` (e.g. `email`, `password`, `firstName`, etc.). + +These generics flow through the logic, returning a `User` that omits the list of `Key`s provided. + +## Going further + +- Learn how you can move the `exclude` function into [a custom model](/orm/prisma-client/queries/custom-models). +- Instead of excluding fields, another option is to [obfuscate the field](https://github.com/prisma/prisma-client-extensions/tree/main/obfuscated-fields). +- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/5042) to add exclude support natively in Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case! diff --git a/docs/200-orm/200-prisma-client/100-queries/064-custom-models.mdx b/docs/200-orm/200-prisma-client/100-queries/064-custom-models.mdx new file mode 100644 index 0000000000..1edd76569e --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/064-custom-models.mdx @@ -0,0 +1,176 @@ +--- +title: 'Custom models' +metaTitle: 'Custom models' +metaDescription: 'This page explains how to wrap Prisma Client in custom models' +--- + + + +As your application grows, you may find the need to group related logic together. We suggest either: + +- Creating static methods using a [Prisma Client extension](/orm/prisma-client/client-extensions) +- Wrapping a model in a class +- Extending Prisma Client model object + + + +## Static methods with Prisma Client extensions + +The following example demonstrates how to create a Prisma Client extension that adds a `signUp` and `findManyByDomain` methods to a User model. + + + + + +```tsx +import bcrypt from 'bcryptjs' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient().$extends({ + model: { + user: { + async signUp(email: string, password: string) { + const hash = await bcrypt.hash(password, 10) + return prisma.user.create({ + data: { + email, + password: { + create: { + hash, + }, + }, + }, + }) + }, + + async findManyByDomain(domain: string) { + return prisma.user.findMany({ + where: { email: { endsWith: `@${domain}` } }, + }) + }, + }, + }, +}) + +async function main() { + // Example usage + await prisma.user.signUp('user2@example2.com', 's3cret') + + await prisma.user.findManyByDomain('example2.com') +} +``` + + + + + +```prisma file="prisma/schema.prisma" copy +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id String @id @default(cuid()) + email String + password Password? +} + +model Password { + hash String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @unique +} +``` + + + + + +## Wrap a model in a class + +In the example below, you'll see how you can wrap the `user` model in the Prisma Client within a `Users` class. + +```tsx +import { PrismaClient, User } from '@prisma/client' + +type Signup = { + email: string + firstName: string + lastName: string +} + +class Users { + constructor(private readonly prismaUser: PrismaClient['user']) {} + + // Signup a new user + async signup(data: Signup): Promise { + // do some custom validation... + return this.prismaUser.create({ data }) + } +} + +async function main() { + const prisma = new PrismaClient() + const users = new Users(prisma.user) + const user = await users.signup({ + email: 'alice@prisma.io', + firstName: 'Alice', + lastName: 'Prisma', + }) +} +``` + +With this new `Users` class, you can define custom functions like `signup`: + +Note that in the example above, you're only exposing a `signup` method from Prisma Client. The Prisma Client is hidden within the `Users` class, so you're no longer be able to call methods like `findMany` and `upsert`. + +This approach works well when you have a large application and you want to intentionally limit what your models can do. + +## Extending Prisma Client model object + +But what if you don't want to hide existing functionality but still want to group custom functions together? In this case, you can use `Object.assign` to extend Prisma Client without limiting its functionality: + +```tsx +import { PrismaClient, User } from '@prisma/client' + +type Signup = { + email: string + firstName: string + lastName: string +} + +function Users(prismaUser: PrismaClient['user']) { + return Object.assign(prismaUser, { + /** + * Signup the first user and create a new team of one. Return the User with + * a full name and without a password + */ + async signup(data: Signup): Promise { + return prismaUser.create({ data }) + }, + }) +} + +async function main() { + const prisma = new PrismaClient() + const users = Users(prisma.user) + const user = await users.signup({ + email: 'alice@prisma.io', + firstName: 'Alice', + lastName: 'Prisma', + }) + const numUsers = await users.count() + console.log(user, numUsers) +} +``` + +Now you can use your custom `signup` method alongside `count`, `updateMany`, `groupBy` and all of the other wonderful methods that Prisma Client provides. Best of all, it's all type-safe! + +## Going further + +We recommend using [Prisma Client extensions](/orm/prisma-client/client-extensions) to extend your models with [custom model methods](https://github.com/prisma/prisma-client-extensions/tree/main/instance-methods). diff --git a/docs/200-orm/200-prisma-client/100-queries/070-case-sensitivity.mdx b/docs/200-orm/200-prisma-client/100-queries/070-case-sensitivity.mdx new file mode 100644 index 0000000000..d512cc35b3 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/070-case-sensitivity.mdx @@ -0,0 +1,187 @@ +--- +title: 'Case sensitivity' +metaTitle: 'Case sensitivity (Reference)' +metaDescription: 'How Prisma Client handles case sensitivity when filtering and sorting.' +preview: false +--- + + + +Case sensitivity affects **filtering** and **sorting** of data, and is determined by your [database collation](#database-collation-and-case-sensitivity). Sorting and filtering data yields different results depending on your settings: + +| Action | Case sensitive | Case insensitive | +| --------------- | -------------------------------------------- | -------------------------------------------- | +| Sort ascending | `Apple`, `Banana`, `apple pie`, `banana pie` | `Apple`, `apple pie`, `Banana`, `banana pie` | +| Match `"apple"` | `apple` | `Apple`, `apple` | + +If you use a **relational database connector**, [Prisma Client](/orm/prisma-client) respects your database collation. Options and recommendations for supporting **case-insensitive** filtering and sorting with Prisma Client depend on your [database provider](#options-for-case-insensitive-filtering). + +If you use the MongoDB connector, [Prisma Client](.) uses RegEx rules to enable case-insensitive filtering. The connector _does not_ use [MongoDB collation](https://docs.mongodb.com/manual/reference/collation/). + +> **Note**: Follow the progress of [case-insensitive sorting on GitHub](https://github.com/prisma/prisma-client-js/issues/841). + + + +## Database collation and case sensitivity + + + +In the context of Prisma Client, the following section refers to relational database connectors only. + + + +Collation specifies how data is **sorted and compared** in a database, which includes casing. Collation is something you choose when you set up a database. + +The following example demonstrates how to view the collation of a MySQL database: + + + + + +```sql no-lines +SELECT @@character_set_database, @@collation_database; +``` + + + + + +```no-lines no-copy + +--------------------------+----------------------+ + | @@character_set_database | @@collation_database | + +--------------------------+----------------------+ + | utf8mb4 | utf8mb4_0900_ai_ci | + +--------------------------+----------------------+ +``` + + + + + +The example collation, [`utf8mb4_0900_ai_ci`](https://dev.mysql.com/doc/refman/8.0/en/charset-collation-names.html), is: + +- Accent-insensitive (`ai`) +- Case-insensitive (`ci`). + +This means that `prisMa` will match `prisma`, `PRISMA`, `priSMA`, and so on: + + + + + +```sql no-lines +SELECT id, email FROM User WHERE email LIKE "%prisMa%" +``` + + + + + +```no-lines no-copy + +----+-----------------------------------+ + | id | email | + +----+-----------------------------------+ + | 61 | alice@prisma.io | + | 49 | birgitte@prisma.io | + +----+-----------------------------------+ +``` + + + + + +The same query with Prisma Client: + +```ts +const users = await prisma.user.findMany({ + where: { + email: { + contains: 'prisMa', + }, + }, + select: { + id: true, + name: true, + }, +}) +``` + +## Options for case-insensitive filtering + +The recommended way to support case-insensitive filtering with Prisma Client depends on your underlying provider. + +### PostgreSQL provider + +PostgreSQL uses [deterministic collation](https://www.postgresql.org/docs/current/collation.html#COLLATION-NONDETERMINISTIC) by default, which means that filtering is **case-sensitive**. To support case-insensitive filtering, use the `mode: 'insensitive'` property on a per-field basis. + +Use the `mode` property on a filter as shown: + +```ts highlight=5;normal +const users = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + mode: 'insensitive', // Default value: default + }, + }, +}) +``` + +See also: [Filtering (Case-insensitive filtering)](filtering-and-sorting#case-insensitive-filtering) + +#### Caveats + +- You cannot use case-insensitive filtering with C collation +- [`citext`](https://www.postgresql.org/docs/12/citext.html) columns are always case-insensitive and are not affected by `mode` + +#### Performance + +If you rely heavily on case-insensitive filtering, consider [creating indexes in the PostgreSQL database](https://www.postgresql.org/docs/current/indexes.html) to improve performance: + +- [Create an expression index](https://www.postgresql.org/docs/current/indexes-expressional.html) for Prisma Client queries that use `equals` or `not` +- Use the `pg_trgm` module to [create a trigram-based index](https://www.postgresql.org/docs/12/pgtrgm.html#id-1.11.7.40.7) for Prisma Client queries that use `startsWith`, `endsWith`, `contains` (maps to`LIKE` / `ILIKE` in PostgreSQL) + +### MySQL provider + +MySQL uses **case-insensitive collation** by default. Therefore, filtering with Prisma Client and MySQL is case-insensitive by default. + +`mode: 'insensitive'` property is not required and therefore not available in the generated Prisma Client API. + +#### Caveats + +- You _must_ use a case-insensitive (`_ci`) collation in order to support case-insensitive filtering. Prisma Client does no support the `mode` filter property for the MySQL provider. + +### MongoDB provider + +To support case-insensitive filtering, use the `mode: 'insensitive'` property on a per-field basis: + +```ts highlight=5;normal +const users = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + mode: 'insensitive', // Default value: default + }, + }, +}) +``` + +The MongoDB uses a RegEx rule for case-insensitive filtering. + +### SQLite provider + +By default, SQLite itself only [supports case-insensitive comparisons of ASCII characters](https://www.sqlite.org/faq.html#q18). Therefore, Prisma Client does not offer support for case-insensitive filtering with SQLite. + +To enable limited support (ASCII only) for case-insensitive filtering on a per-column basis, use `COLLATE NOCASE` when you define table columns: + +```sql +CREATE TABLE mytable ( + sample TEXT COLLATE NOCASE /* collating sequence NOCASE */ +); +``` + +### Microsoft SQL Server provider + +Microsoft SQL Server uses **case-insensitive collation** by default. Therefore, filtering with Prisma Client and Microsoft SQL Server is case-insensitive by default. + +`mode: 'insensitive'` property is not required and therefore not available in the generated Prisma Client API. diff --git a/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/050-raw-queries.mdx b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/050-raw-queries.mdx new file mode 100644 index 0000000000..1fe7089993 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/050-raw-queries.mdx @@ -0,0 +1,689 @@ +--- +title: 'Raw queries' +metaTitle: 'Raw queries' +metaDescription: 'Learn how you can send raw SQL and MongoDB queries to your database using the raw() methods from the Prisma Client API.' +tocDepth: 3 +--- + + + +Prisma Client supports the option of sending raw queries to your database. You may wish to use raw queries if: + +- you want to run a heavily optimized query +- you require a feature that Prisma Client does not yet support (please [consider raising an issue](https://github.com/prisma/prisma/issues/new/choose)) + +Raw queries are available for all relational databases Prisma supports. In addition, from version `3.9.0` raw queries are supported in MongoDB. For more details, see the relevant sections: + +- [Raw queries with relational databases](#raw-queries-with-relational-databases) +- [Raw queries with MongoDB](#raw-queries-with-mongodb) + + + +## Raw queries with relational databases + +For relational databases, Prisma Client exposes four methods that allow you to send raw queries. You can use: + +- `$queryRaw` to return actual records (for example, using `SELECT`) +- `$executeRaw` to return a count of affected rows (for example, after an `UPDATE` or `DELETE`) +- `$queryRawUnsafe` to return actual records (for example, using `SELECT`) using a raw string. **Potential SQL injection risk** +- `$executeRawUnsafe` to return a count of affected rows (for example, after an `UPDATE` or `DELETE`) using a raw string. **Potential SQL injection risk** + +### `$queryRaw` + +`$queryRaw` returns actual database records. For example, the following `SELECT` query returns all fields for each record in the `User` table: + +```ts no-lines +const result = await prisma.$queryRaw`SELECT * FROM User` +``` + +The method is implemented as a [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates), which allows you to pass a template literal where you can easily insert your [variables](#using-variables). In turn, Prisma creates prepared statements that are safe from SQL injections: + +```ts no-lines +const email = 'emelie@prisma.io' +const result = await prisma.$queryRaw`SELECT * FROM User WHERE email = ${email}` +``` + +You can also use the [`Prisma.sql`](#tagged-template-helpers) helper, in fact, the `$queryRaw` method will **only accept** a template string or the `Prisma.sql` helper: + +```ts no-lines +const email = 'emelie@prisma.io' +const result = await prisma.$queryRaw( + Prisma.sql`SELECT * FROM User WHERE email = ${email}` +) +``` + +#### Considerations + +Be aware that: + +- Template variables cannot be used inside SQL string literals. For example, the following query would **not** work: + + ```ts no-lines + const name = 'Bob' + await prisma.$queryRaw`SELECT 'My name is ${name}';` + ``` + + Instead, you can either pass the whole string as a variable, or use string concatenation: + + ```ts no-lines + const name = 'My name is Bob' + await prisma.$queryRaw`SELECT ${name};` + ``` + + ```ts no-lines + const name = 'Bob' + await prisma.$queryRaw`SELECT 'My name is ' || ${name};` + ``` + +- Template variables can only be used for data values (such as `email` in the example above). Variables cannot be used for identifiers such as column names, table names or database names, or for SQL keywords. For example, the following two queries would **not** work: + + ```ts no-lines + const myTable = 'user' + await prisma.$queryRaw`SELECT * FROM ${myTable};` + ``` + + ```ts no-lines + const ordering = 'desc' + await prisma.$queryRaw`SELECT * FROM Table ORDER BY ${ordering};` + ``` + +- Prisma maps any database values returned by `$queryRaw` and `$queryRawUnsafe` to their corresponding JavaScript types. [Learn more](#raw-query-type-mapping). + +- `$queryRaw` does not support dynamic table names in PostgreSQL databases. [Learn more](#dynamic-table-names-in-postgresql) + +#### Return type + +`$queryRaw` returns an array. Each object corresponds to a database record: + +```json5 +[ + { id: 1, email: 'emelie@prisma.io', name: 'Emelie' }, + { id: 2, email: 'yin@prisma.io', name: 'Yin' }, +] +``` + +You can also [type the results of `$queryRaw`](#typing-queryraw-results). + +#### Signature + +```ts no-lines +$queryRaw(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise; +``` + +#### Typing `$queryRaw` results + +`PrismaPromise` uses a [generic type parameter `T`](https://www.typescriptlang.org/docs/handbook/generics.html). You can determine the type of `T` when you invoke the `$queryRaw` method. In the following example, `$queryRaw` returns `User[]`: + +```ts +// import the generated `User` type from the `@prisma/client` module +import { User } from '@prisma/client' + +const result = await prisma.$queryRaw`SELECT * FROM User` +// result is of type: `User[]` +``` + +> **Note**: If you do not provide a type, `$queryRaw` defaults to `unknown`. + +If you are selecting **specific fields** of the model or want to include relations, refer to the documentation about [leveraging Prisma Client's generated types](/orm/prisma-client/type-safety) if you want to make sure that the results are properly typed. + +#### Type caveats when using raw SQL + +When you type the results of `$queryRaw`, the raw data might not always match the suggested TypeScript type. For example, the following Prisma model includes a `Boolean` field named `published`: + +```prisma highlight=3;normal +model Post { + id Int @id @default(autoincrement()) + published Boolean @default(false) + title String + content String? +} +``` + +The following query returns all posts. It then prints out the value of the `published` field for each `Post`: + +```ts +const result = await prisma.$queryRaw`SELECT * FROM Post` + +result.forEach((x) => { + console.log(x.published) +}) +``` + +> **Note**: The Prisma Client query engine standardizes the return type for all databases. **Using the raw queries does not**. If the database provider is MySQL, the values are `1` or `0`. However, if the database provider is PostgreSQL, the values are `true`, `false`, or `NULL`. + +> **Note**: Prisma sends JavaScript integers to PostgreSQL as `INT8`. This might conflict with your user-defined functions that accept only `INT4` as input. If you use `$queryRaw` in conjunction with a PostgreSQL database, update the input types to `INT8`, or cast your query parameters to `INT4`. + +#### Dynamic table names in PostgreSQL + +[It is not possible to interpolate table names](#considerations). This means that you cannot use dynamic table names with `$queryRaw`. Instead, you must use [`$queryRawUnsafe`](#queryrawunsafe), as follows: + +```ts +let userTable = 'User' +let result = await prisma.$queryRawUnsafe(`SELECT * FROM ${userTable}`) +``` + +Note that if you use `$queryRawUnsafe` in conjunction with user inputs, you risk SQL injection attacks. [Learn more](#queryrawunsafe). + +### `$queryRawUnsafe` + +The `$queryRawUnsafe` method allows you to pass a raw string (or template string) to the database. + + + +If you use this method with user inputs (in other words, `SELECT * FROM table WHERE columnx = ${userInput}`), then you open up the possibility for SQL injection attacks. SQL injection attacks can expose your data to modification or deletion.

+ +We strongly advise that you use the `$queryRaw` query instead. For more information on SQL injection attacks, see the [OWASP SQL Injection guide](https://www.owasp.org/index.php/SQL_Injection). + +
+ +The following query returns all fields for each record in the `User` table: + +```ts +// import the generated `User` type from the `@prisma/client` module +import { User } from '@prisma/client' + +const result = await prisma.$queryRawUnsafe('SELECT * FROM User') +``` + +You can also run a parameterized query. The following example returns all users whose email contains the string `emelie@prisma.io`: + +```ts +prisma.$queryRawUnsafe( + 'SELECT * FROM users WHERE email = $1', + 'emelie@prisma.io' +) +``` + +> **Note**: Prisma sends JavaScript integers to PostgreSQL as `INT8`. This might conflict with your user-defined functions that accept only `INT4` as input. If you use a parameterized `$queryRawUnsafe` query in conjunction with a PostgreSQL database, update the input types to `INT8`, or cast your query parameters to `INT4`. + +#### Signature + +```ts no-lines +$queryRawUnsafe(query: string, ...values: any[]): PrismaPromise; +``` + +#### Parameterized queries + +As an alternative to tagged templates, `$queryRawUnsafe` supports standard parameterized queries where each variable is represented by a symbol (`?` for mySQL, `$1`, `$2`, and so on for PostgreSQL). The following example uses a MySQL query: + +```ts +const userName = 'Sarah' +const email = 'sarah@prisma.io' +const result = await prisma.$queryRawUnsafe( + 'SELECT * FROM User WHERE (name = ? OR email = ?)', + userName, + email +) +``` + +> **Note**: MySQL variables are represented by `?` + +The following example uses a PostgreSQL query: + +```ts +const userName = 'Sarah' +const email = 'sarah@prisma.io' +const result = await prisma.$queryRawUnsafe( + 'SELECT * FROM User WHERE (name = $1 OR email = $2)', + userName, + email +) +``` + +> **Note**: PostgreSQL variables are represented by `$1` and `$2` + +As with tagged templates, Prisma Client escapes all variables. + +> **Note**: You cannot pass a table or column name as a variable into a parameterized query. For example, you cannot `SELECT ?` and pass in `*` or `id, name` based on some condition. + +##### Parameterized PostgreSQL `ILIKE` query + +When you use `ILIKE`, the `%` wildcard character(s) should be included in the variable itself, not the query (`string`): + +```ts +const userName = 'Sarah' +const emailFragment = 'prisma.io' +const result = await prisma.$queryRawUnsafe( + 'SELECT * FROM "User" WHERE (name = $1 OR email ILIKE $2)', + userName, + `%${emailFragment}` +) +``` + +> **Note**: Using `%$2` as an argument would not work + +### `$executeRaw` + +`$executeRaw` returns the _number of rows affected by a database operation_, such as `UPDATE` or `DELETE`. This function does **not** return database records. The following query updates records in the database and returns a count of the number of records that were updated: + +```ts +const result: number = + await prisma.$executeRaw`UPDATE User SET active = true WHERE emailValidated = true` +``` + +The method is implemented as a [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates), which allows you to pass a template literal where you can easily insert your [variables](#using-variables). In turn, Prisma creates prepared statements that are safe from SQL injections: + +```ts +const emailValidated = true +const active = true + +const result: number = + await prisma.$executeRaw`UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated};` +``` + +Be aware that: + +- `$executeRaw` does not support multiple queries in a single string (for example, `ALTER TABLE` and `CREATE TABLE` together). +- Prisma Client submits prepared statements, and prepared statements only allow a subset of SQL statements. For example, `START TRANSACTION` is not permitted. You can learn more about [the syntax that MySQL allows in Prepared Statements here](https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html). +- [`PREPARE` does not support `ALTER`](https://www.postgresql.org/docs/current/sql-prepare.html) - see the [workaround](#alter-limitation-postgresql). +- Template variables cannot be used inside SQL string literals. For example, the following query would **not** work: + + ```ts no-lines + const name = 'Bob' + await prisma.$queryRaw`UPDATE user SET greeting = 'My name is ${name}';` + ``` + + Instead, you can either pass the whole string as a variable, or use string concatenation: + + ```ts no-lines + const name = 'My name is Bob' + await prisma.$queryRaw`UPDATE user SET greeting = ${name};` + ``` + + ```ts no-lines + const name = 'Bob' + await prisma.$queryRaw`UPDATE user SET greeting = 'My name is ' || ${name};` + ``` + +- Template variables can only be used for data values (such as `email` in the example above). Variables cannot be used for identifiers such as column names, table names or database names, or for SQL keywords. For example, the following two queries would **not** work: + + ```ts no-lines + const myTable = 'user' + await prisma.$queryRaw`UPDATE ${myTable} SET active = true;` + ``` + + ```ts no-lines + const ordering = 'desc' + await prisma.$queryRaw`UPDATE User SET active = true ORDER BY ${desc};` + ``` + +#### Return type + +`$executeRaw` returns a `number`. + +#### Signature + +```ts +$executeRaw(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise; +``` + +### `$executeRawUnsafe` + +The `$executeRawUnsafe` method allows you to pass a raw string (or template string) to the database. Like `$executeRaw`, it does **not** return database records, but returns the number of rows affected. + +> **Note**: `$executeRawUnsafe` can only run **one** query at a time. You cannot append a second query - for example, adding `DROP bobby_tables` to the end of an `ALTER`. + + + +If you use this method with user inputs (in other words, `SELECT * FROM table WHERE columnx = ${userInput}`), then you open up the possibility for SQL injection attacks. SQL injection attacks can expose your data to modification or deletion.

+ +We strongly advise that you use the `$executeRaw` query instead. For more information on SQL injection attacks, see the [OWASP SQL Injection guide](https://www.owasp.org/index.php/SQL_Injection). + +
+ +The following example uses a template string to update records in the database. It then returns a count of the number of records that were updated: + +```ts +const emailValidated = true +const active = true + +const result = await prisma.$executeRawUnsafe( + `UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated}` +) +``` + +The same can be written as a parameterized query: + +```ts +const result = prisma.$executeRawUnsafe( + 'UPDATE User SET active = $1 WHERE emailValidated = $2', + 'yin@prisma.io', + true +) +``` + +#### Signature + +```ts no-lines +$executeRawUnsafe(query: string, ...values: any[]): PrismaPromise; +``` + +### Raw query type mapping + +Prisma maps any database values returned by `$queryRaw` and `$queryRawUnsafe`to their corresponding [JavaScript types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures). This behavior is the same as for regular Prisma query methods like `findMany`. + + + +**Feature availability:** + +- In v3.14.x and v3.15.x, raw query type mapping was available with the preview feature `improvedQueryRaw`. We made raw query type mapping [Generally Available](/orm/more/releases#generally-available-ga) in version 4.0.0, so you do not need to use `improvedQueryRaw` in version 4.0.0 or later. +- Before version 4.0.0, raw query type mapping was not available for SQLite. + + + +As an example, take a raw query that selects columns with `BigInt`, `Bytes`, `Decimal` and `Date` types from a table: + + + + + +```ts +const result = + await prisma.$queryRaw`SELECT bigint, bytes, decimal, date FROM "Table";` + +console.log(result) +``` + + + + + +```terminal no-copy wrap +{ bigint: BigInt("123"), bytes: Buffer.from([1, 2]), decimal: Decimal("12.34"), date: Date("") } +``` + + + + + +In the `result` object, the database values have been mapped to the corresponding JavaScript types. + +The following table shows the conversion between types used in the database and the JavaScript type returned by the raw query: + +| Database type | JavaScript type | +| ----------------------- | --------------- | +| Text | `String` | +| 32-bit integer | `Number` | +| Floating point number | `Number` | +| Double precision number | `Number` | +| 64-bit integer | `BigInt` | +| Decimal / numeric | `Decimal` | +| Bytes | `Buffer` | +| Json | `Object` | +| DateTime | `Date` | +| Date | `Date` | +| Time | `Date` | +| Uuid | `String` | +| Xml | `String` | + +Note that the exact name for each database type will vary between databases – for example, the boolean type is known as `boolean` in PostgreSQL and `STRING` in CockroachDB. See the [Scalar types reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) for full details of type names for each database. + +### PostgreSQL typecasting fixes + +Prisma resolves a number of issues with typecasting in PostgreSQL. + + + +**Feature availability:** In v3.14.x and v3.15.x, these PostgreSQL fixes were available with the preview feature `improvedQueryRaw`. We made these fixes [Generally Available](/orm/more/releases#generally-available-ga) in version 4.0.0, so you do not need to use `improvedQueryRaw` in version 4.0.0 or later. + + + +For example, the following raw query now works correctly, returning an integer result: + +```ts +await prisma.$queryRaw`SELECT ${1.5}::int as int` + +// Before: db error: ERROR: incorrect binary data format in bind parameter 1 +// After: [{ int: 2 }] +``` + +A consequence of this fix is that some subtle implicit casts are now handled more strictly, so some queries that previously were allowed will now fail. As an example, take the following query using PostgreSQL's `LENGTH` function, which only accepts the `text` type as an input: + +```ts +await prisma.$queryRaw`SELECT LENGTH(${42});` +``` + +Before version 4.0.0, Prisma silently coerces `42` to `text`. From version 4.0.0, the query returns an error: + +```terminal wrap +// ERROR: function length(integer) does not exist +// HINT: No function matches the given name and argument types. You might need to add explicit type casts. +``` + +The fix in this case is to explicitly cast `42` to the `text` type: + +```ts +await prisma.$queryRaw`SELECT LENGTH(${42}::text);` +``` + +### Transactions + +In 2.10.0 and later, you can use `.$executeRaw()` and `.$queryRaw()` inside a [transaction](/orm/prisma-client/queries/transactions). + +### Using variables + +`$executeRaw` and `$queryRaw` are implemented as [**tagged templates**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates). Tagged templates are the recommended way to use variables with raw SQL in the Prisma Client. + +The following example includes a placeholder named `${userId}`: + +```ts +const userId = 42 +const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${userId};` +``` + +✔ Benefits of using the tagged template versions of `$queryRaw` and `$executeRaw` include: + +- Prisma Client escapes all variables. +- Tagged templates are database-agnostic - you do not need to remember if variables should be written as `$1` (PostgreSQL) or `?` (MySQL). +- [SQL Template Tag](https://github.com/blakeembrey/sql-template-tag) give you access to [useful helpers](#tagged-template-helpers). +- Embedded, named variables are easier to read. + +> **Note**: You cannot pass a table or column name into a tagged template placeholder. For example, you cannot `SELECT ?` and pass in `*` or `id, name` based on some condition. + +#### Tagged template helpers + +Prisma Client specifically uses [SQL Template Tag](https://github.com/blakeembrey/sql-template-tag), which exposes a number of helpers. For example, the following query uses `join()` to pass in a list of IDs: + +```ts +import { Prisma } from '@prisma/client' + +const ids = [1, 3, 5, 10, 20] +const result = + await prisma.$queryRaw`SELECT * FROM User WHERE id IN (${Prisma.join(ids)})` +``` + +The following example uses the `empty` and `sql` helpers to change the query depending on whether `userName` is empty: + +```ts +import { Prisma } from '@prisma/client' + +const userName = '' +const result = await prisma.$queryRaw`SELECT * FROM User ${ + userName ? Prisma.sql`WHERE name = ${userName}` : Prisma.empty // Cannot use "" or NULL here! +}` +``` + +#### `ALTER` limitation (PostgreSQL) + +PostgreSQL [does not support using `ALTER` in a prepared statement](https://www.postgresql.org/docs/current/sql-prepare.html), which means that the following queries **will not work**: + +```ts +await prisma.$executeRaw`ALTER USER prisma WITH PASSWORD "${password}"` +await prisma.$executeRaw( + Prisma.sql`ALTER USER prisma WITH PASSWORD "${password}"` +) +``` + +You can use the following query, but be aware that this is potentially **unsafe** as `${password}` is not escaped: + +```ts +await prisma.$executeRawUnsafe('ALTER USER prisma WITH PASSWORD "$1"', password}) +``` + +### Unsupported types + +[`Unsupported` types](/orm/reference/prisma-schema-reference#unsupported) need to be cast to Prisma supported types before using them in `$queryRaw` or `$queryRawUnsafe`. For example, take the following model, which has a `location` field with an `Unsupported` type: + +```tsx +model Country { + location Unsupported("point")? +} +``` + +The following query on the unsupported field will **not** work: + +```tsx +await prisma.$queryRaw`SELECT location FROM Country;` +``` + +Instead, cast `Unsupported` fields to any supported Prisma type, **if your `Unsupported` column supports the cast**. + +The most common type you may want to cast your `Unsupported` column to is `String`. For example, on PostgreSQL, this would map to the `text` type: + +```tsx +await prisma.$queryRaw`SELECT location::text FROM Country;` +``` + +The database will thus provide a `String` representation of your data which Prisma supports. + +For details of supported Prisma types, see the [Prisma data connector](/orm/overview) for the relevant database. + +### SQL injection + +Prisma Client mitigates the risk of SQL injection in the following ways: + +- Prisma Client escapes all variables when you use tagged templates and sends all queries as prepared statements. + + ```ts + $queryRaw`...` // Tagged template + $executeRaw`...` // Tagged template + ``` + +- `$executeRaw` can only run **one** query at a time. You cannot append a second query - for example, adding `DROP bobby_tables` to the end of an `ALTER`. + +If you cannot use tagged templates, you can instead use [`$queryRawUnsafe`](/orm/prisma-client/queries/raw-database-access/raw-queries#queryrawunsafe) or [`$executeRawUnsafe`](/orm/prisma-client/queries/raw-database-access/raw-queries#executerawunsafe) but **be aware that your code may be vulnerable to SQL injection**. + +#### ⚠️ String concatenation + +The following example concatenates `query` and `inputString`. Prisma Client ❌ **cannot** escape `inputString` in this example, which makes it vulnerable to SQL injection: + +```ts +const inputString = '"Sarah" UNION SELECT id, title, content FROM Post' // SQL Injection +const query = 'SELECT id, name, email FROM User WHERE name = ' + inputString +const result = await prisma.$queryRawUnsafe(query) + +console.log(result) +``` + +## Raw queries with MongoDB + +For MongoDB in versions `3.9.0` and later, Prisma Client exposes three methods that allow you to send raw queries. You can use: + +- `$runCommandRaw` to run a command against the database +- `.findRaw` to find zero or more documents that match the filter. +- `.aggregateRaw` to perform aggregation operations on a collection. + +### `$runCommandRaw` + +`$runCommandRaw` runs a raw MongoDB command against the database. As input, it accepts all [MongoDB database commands](https://www.mongodb.com/docs/manual/reference/command/), with the following exceptions: + +- `find` (use [`findRaw`](#findraw) instead) +- `aggregate` (use [`aggregateRaw`](#aggregateraw) instead) + +When you use `$runCommandRaw` to run a MongoDB database command, note the following: + +- The object that you pass when you invoke `$runCommandRaw` must follow the syntax of the MongoDB database command. +- You must connect to the database with an appropriate role for the MongoDB database command. + +In the following example, a query inserts two records with the same `_id`. This bypasses normal document validation. + +```ts no-lines +prisma.$runCommandRaw({ + insert: 'Pets', + bypassDocumentValidation: true, + documents: [ + { + _id: 1, + name: 'Felinecitas', + type: 'Cat', + breed: 'Russian Blue', + age: 12, + }, + { + _id: 1, + name: 'Nao Nao', + type: 'Dog', + breed: 'Chow Chow', + age: 2, + }, + ], +}) +``` + + + +Do not use `$runCommandRaw` for queries which contain the `"find"` or `"aggregate"` commands, because you might be unable to fetch all data. This is because MongoDB returns a [cursor](https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/) that is attached to your MongoDB session, and you might not hit the same MongoDB session every time. For these queries, you should use the specialised [`findRaw`](#findraw) and [`aggregateRaw`](#aggregateraw) methods instead. + + + +#### Return type + +`$runCommandRaw` returns a `JSON` object whose shape depends on the inputs. + +#### Signature + +```ts no-lines +$runCommandRaw(command: InputJsonObject): PrismaPromise; +``` + +### `findRaw` + +`.findRaw` returns actual database records. It will find zero or more documents that match the filter on the `User` collection: + +```ts no-lines +const result = await prisma.user.findRaw({ + filter: { age: { $gt: 25 } }, + options: { projection: { _id: false } }, +}) +``` + +#### Return type + +`.findRaw` returns a `JSON` object whose shape depends on the inputs. + +#### Signature + +```ts no-lines +.findRaw(args?: {filter?: InputJsonObject, options?: InputJsonObject}): PrismaPromise; +``` + +- `filter`: The query predicate filter. If unspecified, then all documents in the collection will match the [predicate](https://docs.mongodb.com/manual/reference/operator/query). + +- `options`: Additional options to pass to the [`find` command](https://docs.mongodb.com/manual/reference/command/find/#command-fields). + +### `aggregateRaw` + +`.aggregateRaw` returns aggregated database records. It will perform aggregation operations on the `User` collection: + +```ts no-lines +const result = await prisma.user.aggregateRaw({ + pipeline: [ + { $match: { status: 'registered' } }, + { $group: { _id: '$country', total: { $sum: 1 } } }, + ], +}) +``` + +#### Return type + +`.aggregateRaw` returns a `JSON` object whose shape depends on the inputs. + +#### Signature + +```ts no-lines +.aggregateRaw(args?: {pipeline?: InputJsonObject[], options?: InputJsonObject}): PrismaPromise; +``` + +- `pipeline`: An array of aggregation stages to process and transform the document stream via the [aggregation pipeline](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline). + +- `options`: Additional options to pass to the [`aggregate` command](https://docs.mongodb.com/manual/reference/command/aggregate/#command-fields). diff --git a/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/100-custom-and-type-safe-queries.mdx b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/100-custom-and-type-safe-queries.mdx new file mode 100644 index 0000000000..053436ef72 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/100-custom-and-type-safe-queries.mdx @@ -0,0 +1,369 @@ +--- +title: 'Custom & type-safe queries' +metaTitle: 'Custom & type-safe queries' +metaDescription: 'Learn how to use SafeQL and Prisma Client extensions to work around features not natively supported by Prisma, such as PostGIS.' +--- + +## Overview + +This page explains how to improve the experience of writing raw SQL in Prisma ORM. It uses [Prisma Client extensions](/orm/prisma-client/client-extensions) and [SafeQL](https://safeql.dev) to create custom, type-safe Prisma Client queries which abstract custom SQL that your app might need (using `$queryRaw`). + +The example will be using [PostGIS](https://postgis.net/) and PostgreSQL, but is applicable to any raw SQL queries that you might need in your application. + +## What is SafeQL? + +[SafeQL](https://safeql.dev/) allows for advanced linting and type safety within raw SQL queries. After setup, SafeQL works with Prisma `$queryRaw` and `$executeRaw` to provide type safety when raw queries are required. + +SafeQL runs as an [ESLint](https://eslint.org/) plugin and is configured using ESLint rules. This guide doesn't cover setting up ESLint and we will assume that you already having it running in your project. + +## Prerequisites + +To follow along, you will be expected to have: + +- A [PostgreSQL](https://www.postgresql.org/) database with PostGIS installed +- Prisma set up in your project +- ESLint set up in your project + +## Geographic data support in Prisma + +At the time of writing, Prisma does not support working with geographic data, specifically using [PostGIS](https://github.com/prisma/prisma/issues/2789). + +A model that has geographic data columns will be stored using the [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) data type. Fields with `Unsupported` types are present in the generated Prisma Client and will be typed as `any`. A model with a required `Unsupported` type does not expose write operations such as `create`, and `update`. + +Prisma supports write operations on models with a required `Unsupported` field using `$queryRaw` and `$executeRaw`. You can use Prisma Client extensions and SafeQL to improve the type-safety when working with geographical data in raw queries. + +## 1. Set up Prisma for use with PostGIS + +If you haven't already, enable the `postgresqlExtensions` Preview feature and add the `postgis` PostgreSQL extension in your Prisma schema: + +```prisma highlight=3,9;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [postgis] +} +``` + + + +If you are not using a hosted database provider, you will likely need to install the `postgis` extension. Refer to [PostGIS's docs](http://postgis.net/documentation/getting_started/#installing-postgis) to learn more about how to get started with PostGIS. If you're using Docker Compose, you can use the following snippet to set up a PostgreSQL database that has PostGIS installed: + +```yaml +version: '3.6' +services: + pgDB: + image: postgis/postgis:13-3.1-alpine + restart: always + ports: + - '5432:5432' + volumes: + - db_data:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: geoexample +volumes: + db_data: +``` + + + +Next, create a migration and execute a migration to enable the extension: + +```terminal +npx prisma migrate dev --name add-postgis +``` + +For reference, the output of the migration file should look like the following: + +```sql file=migrations/TIMESTAMP_add_postgis/migration.sql +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "postgis"; +``` + +You can double-check that the migration has been applied by running `prisma migrate status`. + +## 2. Create a new model that uses a geographic data column + +Add a new model with a column with a `geography` data type once the migration is applied. For this guide, we'll use a model called `PointOfInterest`. + +```prisma +model PointOfInterest { + id Int @id @default(autoincrement()) + name String + location Unsupported("geography(Point, 4326)") +} +``` + +You'll notice that the `location` field uses an [`Unsupported`](/orm/reference/prisma-schema-reference#unsupported) type. This means that we lose a lot of the benefits of Prisma when working with `PointOfInterest`. We'll be using [SafeQL](https://safeql.dev/) to fix this. + +Like before, create and execute a migration using the `prisma migrate dev` command to create the `PointOfInterest` table in your database: + +```terminal +npx prisma migrate dev --name add-poi +``` + +For reference, here is the output of the SQL migration file generated by Prisma Migrate: + +```sql file=migrations/TIMESTAMP_add_poi/migration.sql +-- CreateTable +CREATE TABLE "PointOfInterest" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "location" geography(Point, 4326) NOT NULL, + + CONSTRAINT "PointOfInterest_pkey" PRIMARY KEY ("id") +); +``` + +## 3. Integrate SafeQL + +SafeQL is easily integrated with Prisma in order to lint `$queryRaw` and `$executeRaw` Prisma operations. You can reference [SafeQL's integration guide](https://safeql.dev/compatibility/prisma.html) or follow the steps below. + +### 3.1. Install the `@ts-safeql/eslint-plugin` npm package + +```terminal +npm install -D @ts-safeql/eslint-plugin +``` + +This ESLint plugin is what will allow for queries to be linted. + +### 3.2. Add `@ts-safeql/eslint-plugin` to your ESLint plugins + +Next, add `@ts-safeql/eslint-plugin` to your list of ESLint plugins. In our example we are using an `.eslintrc.js` file, but this can be applied to any way that you [configure ESLint](https://eslint.org/docs/latest/use/configure/). + +```js file=.eslintrc.js highlight=3 +/** @type {import('eslint').Linter.Config} */ +module.exports = { + "plugins": [..., "@ts-safeql/eslint-plugin"], + ... +} +``` + +### 3.3 Add `@ts-safeql/check-sql` rules + +Now, setup the rules that will enable SafeQL to mark invalid SQL queries as ESLint errors. + +```js file=.eslintrc.js highlight=4-22;add +/** @type {import('eslint').Linter.Config} */ +module.exports = { + plugins: [..., '@ts-safeql/eslint-plugin'], + rules: { + '@ts-safeql/check-sql': [ + 'error', + { + connections: [ + { + // The migrations path: + migrationsDir: './prisma/migrations', + targets: [ + // This makes `prisma.$queryRaw` and `prisma.$executeRaw` commands linted + { tag: 'prisma.+($queryRaw|$executeRaw)', transform: '{type}[]' }, + ], + }, + ], + }, + ], + }, +} +``` + +> **Note**: If your `PrismaClient` instance is called something different than `prisma`, you need to adjust the value for `tag` accordingly. For example, if it is called `db`, the value for `tag` should be `'db.+($queryRaw|$executeRaw)'`. + +### 3.4. Connect to your database + +Finally, set up a `connectionUrl` for SafeQL so that it can introspect your database and retrieve the table and column names you use in your schema. SafeQL then uses this information for linting and highlighting problems in your raw SQL statements. + +Our example relies on the [`dotenv`](https://github.com/motdotla/dotenv) package to get the same connection string that is used by Prisma. We recommend this in order to keep your database URL out of version control. + +If you haven't installed `dotenv` yet, you can install it as follows: + +```terminal +npm install dotenv +``` + +Then update your ESLint config as follows: + +```js file=.eslintrc.js highlight=1,6-9,16;add +require('dotenv').config() + +/** @type {import('eslint').Linter.Config} */ +module.exports = { + plugins: ['@ts-safeql/eslint-plugin'], + // exclude `parserOptions` if you are not using TypeScript + parserOptions: { + project: './tsconfig.json', + }, + rules: { + '@ts-safeql/check-sql': [ + 'error', + { + connections: [ + { + connectionUrl: process.env.DATABASE_URL, + // The migrations path: + migrationsDir: './prisma/migrations', + targets: [ + // what you would like SafeQL to lint. This makes `prisma.$queryRaw` and `prisma.$executeRaw` + // commands linted + { tag: 'prisma.+($queryRaw|$executeRaw)', transform: '{type}[]' }, + ], + }, + ], + }, + ], + }, +} +``` + +SafeQL is now fully configured to help you write better raw SQL using Prisma Client. + +## 4. Creating extensions to make raw SQL queries type-safe + +In this section, we'll create two [`model`](/orm/prisma-client/client-extensions/model) extensions with custom queries to be able to work conveniently with the `PointOfInterest` model: + +1. A `create` query that allows us to create new `PointOfInterest` records in the database +1. A `findClosestPoints` query that returns the `PointOfInterest` records that are closest to a given coordinate + +### 4.1. Adding an extension to create `PointOfInterest` records + +The `PointOfInterest` model in the Prisma schema uses an `Unsupported` type. As a consequence, the generated `PointOfInterest` type in Prisma Client can't be used to carry values for latitude and longitude. + +We will resolve this by defining two custom types that better represent our model in TypeScript: + +```ts +type MyPoint = { + latitude: number + longitude: number +} + +type MyPointOfInterest = { + name: string + location: MyPoint +} +``` + +Next, you can add a `create` query to the `pointOfInterest` property of your Prisma Client: + +```ts highlight=19;normal +const prisma = new PrismaClient().$extends({ + model: { + pointOfInterest: { + async create(data: { + name: string + latitude: number + longitude: number + }) { + // Create an object using the custom types from above + const poi: MyPointOfInterest = { + name: data.name, + location: { + latitude: data.latitude, + longitude: data.longitude, + }, + } + + // Insert the object into the database + const point = `POINT(${poi.location.longitude} ${poi.location.latitude})` + await prisma.$queryRaw` + INSERT INTO "PointOfInterest" (name, location) VALUES (${poi.name}, ST_GeomFromText(${point}, 4326)); + ` + + // Return the object + return poi + }, + }, + }, +}) +``` + +Notice that the SQL in the line that's highlighted in the code snippet gets checked by SafeQL! For example, if you change the name of the table from `"PointOfInterest"` to `"PointOfInterest2"`, the following error appears: + +``` +error Invalid Query: relation "PointOfInterest2" does not exist @ts-safeql/check-sql +``` + +This also works with the column names `name` and `location`. + +You can now create new `PointOfInterest` records in your code as follows: + +```ts +const poi = await prisma.pointOfInterest.create({ + name: 'Berlin', + latitude: 52.52, + longitude: 13.405, +}) +``` + +### 4.2. Adding an extension to query for closest to `PointOfInterest` records + +Now let's make a Prisma Client extension in order to query this model. We will be making an extension that finds the closest points of interest to a given longitude and latitude. + +```ts +const prisma = new PrismaClient().$extends({ + model: { + pointOfInterest: { + async create(data: { + name: string + latitude: number + longitude: number + }) { + // ... same code as before + }, + + async findClosestPoints(latitude: number, longitude: number) { + // Query for clostest points of interests + const result = await prisma.$queryRaw< + { + id: number | null + name: string | null + st_x: number | null + st_y: number | null + }[] + >`SELECT id, name, ST_X(location::geometry), ST_Y(location::geometry) + FROM "PointOfInterest" + ORDER BY ST_DistanceSphere(location::geometry, ST_MakePoint(${latitude}, ${longitude})) DESC` + + // Transform to our custom type + const pois: MyPointOfInterest[] = result.map((data) => { + return { + name: data.name, + location: { + latitude: data.st_x || 0, + longitude: data.st_y || 0, + }, + } + }) + + // Return data + return pois + }, + }, + }, +}) +``` + +Now, you can use our Prisma Client as normal to find close points of interest to a given longitude and latitude using the custom method created on the `PointOfInterest` model. + +```ts +const closestPointOfInterest = await prisma.pointOfInterest.findClosestPoints( + 53.5488, + 9.9872 +) +``` + +Similar to before, we again have the benefit of SafeQL to add extra type safety to our raw queries. For example, if we removed the cast to `geometry` for `location` by changing `location::geometry` to just `location`, we would get linting errors in the `ST_X`, `ST_Y` or `ST_DistanceSphere` functions respectively. + +```terminal +error Invalid Query: function st_distancesphere(geography, geometry) does not exist @ts-safeql/check-sql +``` + +## Conclusion + +While you may sometimes need to drop down to raw SQL when using Prisma, you can use various techniques to make the experience of writing raw SQL queries with Prisma better. + +In this article, you have used SafeQL and Prisma Client extensions to create custom, type-safe Prisma Client queries to abstract PostGIS operations which are currently not natively supported in Prisma ORM. diff --git a/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/index.mdx b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/index.mdx new file mode 100644 index 0000000000..ca494a4828 --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/090-raw-database-access/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Raw database access' +metaTitle: 'Raw database access' +metaDescription: 'Raw database access with Prisma Client.' +--- + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx b/docs/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx new file mode 100644 index 0000000000..5b30fa534a --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx @@ -0,0 +1,343 @@ +--- +title: 'Query optimization' +metaTitle: 'Query optimization' +metaDescription: 'How Prisma optimizes queries under the hood' +tocDepth: 3 +--- + + + +This guide describes ways to optimize query performance, debug performance issues, and how to tackle common performance issues such as the [n+1 problem](#solving-the-n1-problem). + + + +## Debugging performance issues + +To help you debug and diagnose performance issues, you can [log query events at client level](/orm/prisma-client/observability-and-logging/logging#event-based-logging), which allows you to see the generated queries, parameters, and durations. + +Alternatively, if you are only interested in the time taken to run a query, you can implement [logging middleware](/orm/prisma-client/client-extensions/middleware/logging-middleware). + +## Solving the n+1 problem + +The n+1 problem occurs when you loop through the results of a query and perform one additional query **per result**, resulting in `n` number of queries plus the original (n+1). This is a common problem with ORMs, particularly in combination with GraphQL, because it is not always immediately obvious that your code is generating inefficient queries. + +### Solving n+1 in GraphQL with `findUnique` and Prisma's dataloader + +
+ + + +
+ +The Prisma Client dataloader automatically **batches** `findUnique` queries that ✔ occur in the same tick and ✔ have the same `where` and `include` parameters. + +Automatic batching of `findUnique` is particularly useful in a **GraphQL context**. GraphQL runs a separate resolver function for every field, which can make it difficult to optimize a nested query. + +For example - the following GraphQL runs the `allUsers` resolver to get all users, and the `posts` resolver **once per user** to get each user's posts (n+1): + +```js +query { + allUsers { + id, + posts { + id + } + } +} +``` + +The `allUsers` query uses `user.findMany(..)` to return all users: + +```ts highlight=7;normal +const Query = objectType({ + name: 'Query', + definition(t) { + t.nonNull.list.nonNull.field('allUsers', { + type: 'User', + resolve: (_parent, _args, context) => { + return context.prisma.user.findMany() + }, + }) + }, +}) +``` + +This results in a single SQL query: + +```js +{ + timestamp: 2021-02-19T09:43:06.332Z, + query: 'SELECT `dev`.`User`.`id`, `dev`.`User`.`email`, `dev`.`User`.`name` FROM `dev`.`User` WHERE 1=1 LIMIT ? OFFSET ?', + params: '[-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +``` + +However, the resolver function for `posts` is then invoked **once per user**. This results in a `findMany` query **✘ per user** rather than a single `findMany` to return all posts by all users (expand CLI output to see queries). + + + + +```ts highlight=10-13;normal; +const User = objectType({ + name: 'User', + definition(t) { + t.nonNull.int('id') + t.string('name') + t.nonNull.string('email') + t.nonNull.list.nonNull.field('posts', { + type: 'Post', + resolve: (parent, _, context) => { + return context.prisma.post.findMany({ + where: { authorId: parent.id || undefined }, + }) + }, + }) + }, +}) +``` + + + + +```js no-copy +{ + timestamp: 2021-02-19T09:43:06.343Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` = ? LIMIT ? OFFSET ?', + params: '[1,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:43:06.347Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` = ? LIMIT ? OFFSET ?', + params: '[3,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:43:06.348Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` = ? LIMIT ? OFFSET ?', + params: '[2,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:43:06.348Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` = ? LIMIT ? OFFSET ?', + params: '[4,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:43:06.348Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` = ? LIMIT ? OFFSET ?', + params: '[5,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +// And so on +``` + + + + +Instead, use `findUnique` in combination with [the fluent API](/orm/prisma-client/queries/relation-queries#fluent-api) (`.posts()`) as shown to return a user's posts. Even though the resolver is called once per user, the Prisma dataloader **✔ batches the `findUnique` queries**. + + + + +```ts highlight=13-18;add|10-12;delete +const User = objectType({ + name: 'User', + definition(t) { + t.nonNull.int('id') + t.string('name') + t.nonNull.string('email') + t.nonNull.list.nonNull.field('posts', { + type: 'Post', + resolve: (parent, _, context) => { + return context.prisma.post.findMany({ + where: { authorId: parent.id || undefined }, + }) + return context.prisma.user + .findUnique({ + where: { id: parent.id || undefined }, + }) + .posts() + }, + }) + }, +}) +``` + + + + +```js no-copy +{ + timestamp: 2021-02-19T09:59:46.340Z, + query: 'SELECT `dev`.`User`.`id`, `dev`.`User`.`email`, `dev`.`User`.`name` FROM `dev`.`User` WHERE 1=1 LIMIT ? OFFSET ?', + params: '[-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:59:46.350Z, + query: 'SELECT `dev`.`User`.`id` FROM `dev`.`User` WHERE `dev`.`User`.`id` IN (?,?,?) LIMIT ? OFFSET ?', + params: '[1,2,3,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +{ + timestamp: 2021-02-19T09:59:46.350Z, + query: 'SELECT `dev`.`Post`.`id`, `dev`.`Post`.`createdAt`, `dev`.`Post`.`updatedAt`, `dev`.`Post`.`title`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`viewCount`, `dev`.`Post`.`authorId` FROM `dev`.`Post` WHERE `dev`.`Post`.`authorId` IN (?,?,?) LIMIT ? OFFSET ?', + params: '[1,2,3,-1,0]', + duration: 0, + target: 'quaint::connector::metrics' +} +``` + + + + +If the `posts` resolver is invoked once per user, Prisma's dataloader groups `findUnique` queries with the same parameters and selection set. Each group is optimized into a single `findMany`. + +#### Do I have to use the fluent API to enable batching of queries? + +It may seem counterintitive to use a `prisma.user.findUnique(...).posts()` query to return posts instead of `prisma.posts.findMany()` - particularly as the former results in two queries rather than one. + +The **only** reason you need to use the fluent API (`user.findUnique(...).posts()`) to return posts is that Prisma's dataloader batches `findUnique` queries and does not currently [batch `findMany` queries](https://github.com/prisma/prisma/issues/1477). + +When the dataloader batches `findMany` queries, you no longer need to use `findUnique` with the fluent API in this way. + +### n+1 in other contexts + +The n+1 problem is most commonly seen in a GraphQL context because you have to find a way to optimize a single query across multiple resolvers. However, you can just as easily introduce the n+1 problem by looping through results with `forEach` in your own code. + +The following code results in n+1 queries - one `findMany` to get all users, and one `findMany` **per user** to get each user's posts: + + + + +```ts +// One query to get all users +const users = await prisma.user.findMany({}) + +// One query PER USER to get all posts +users.forEach(async (usr) => { + const posts = await prisma.post.findMany({ + where: { + authorId: usr.id, + }, + }) + + // Do something with each users' posts +}) +``` + + + + +```sql no-copy +SELECT "public"."User"."id", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1 +SELECT "public"."Post"."id", "public"."Post"."title" FROM "public"."Post" WHERE "public"."Post"."authorId" = $1 OFFSET $2 +SELECT "public"."Post"."id", "public"."Post"."title" FROM "public"."Post" WHERE "public"."Post"."authorId" = $1 OFFSET $2 +SELECT "public"."Post"."id", "public"."Post"."title" FROM "public"."Post" WHERE "public"."Post"."authorId" = $1 OFFSET $2 +SELECT "public"."Post"."id", "public"."Post"."title" FROM "public"."Post" WHERE "public"."Post"."authorId" = $1 OFFSET $2 +/* ..and so on .. */ +``` + + + + +This is not an efficient way to query. Instead, you can: + +- Use nested reads ([`include`](/orm/reference/prisma-client-reference#include) ) to return users and related posts +- Use the [`in`](/orm/reference/prisma-client-reference#in) filter + +#### Solving n+1 with `include` + +You can use `include` to return each user's posts. This only results in **two** SQL queries - one to get users, and one to get posts. This is known as a [nested read](/orm/prisma-client/queries/relation-queries#nested-reads). + + + + +```ts +const usersWithPosts = await prisma.user.findMany({ + include: { + posts: true, + }, +}) +``` + + + + +```sql no-copy +SELECT "public"."User"."id", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1 +SELECT "public"."Post"."id", "public"."Post"."title", "public"."Post"."authorId" FROM "public"."Post" WHERE "public"."Post"."authorId" IN ($1,$2,$3,$4) OFFSET $5 +``` + + + + +#### Solving n+1 with `in` + +If you have a list of user IDs, you can use the `in` filter to return all posts where the `authorId` is `in` that list of IDs: + + + + +```ts +const users = await prisma.user.findMany({}) + +const userIds = users.map((x) => x.id) + +const posts = await prisma.post.findMany({ + where: { + authorId: { + in: userIds, + }, + }, +}) +``` + + + + +```sql no-copy +SELECT "public"."User"."id", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1 +SELECT "public"."Post"."id", "public"."Post"."createdAt", "public"."Post"."updatedAt", "public"."Post"."title", "public"."Post"."content", "public"."Post"."published", "public"."Post"."authorId" FROM "public"."Post" WHERE "public"."Post"."authorId" IN ($1,$2,$3,$4) OFFSET $5 +``` + + + + +## Using bulk queries + +It is generally more performant to read and write large amounts of data in bulk - for example, inserting 50,000 records in batches of 1000 rather than as 50,000 separate inserts. Prisma Client supports the following bulk queries: + +- [`createMany`](/orm/reference/prisma-client-reference#createmany) +- [`deleteMany`](/orm/reference/prisma-client-reference#deletemany) +- [`updateMany`](/orm/reference/prisma-client-reference#updatemany) +- [`findMany`](/orm/reference/prisma-client-reference#findmany) + +## Using `select` to limit number of columns returned + +Using `select` to limit the number of columns that are returned is **unlikely to have an effect on performance** unless you have identified this as a performance bottleneck through testing. For example, reading all fields may negatively affect performance if you have: + +- Tables with a large number of columns +- Large columns that are stored in a separate location on disk rather than a row, which results in an additional disk read + +Furthermore, if you have a mature product with well established query patterns and finely tuned indexes, selecting a specific subset of fields may be beneficial as it avoids reading data from disk. However, in most cases, this level of performance tuning is only necessary at a certain scale. + + diff --git a/docs/200-orm/200-prisma-client/100-queries/index.mdx b/docs/200-orm/200-prisma-client/100-queries/index.mdx new file mode 100644 index 0000000000..5f98e3590e --- /dev/null +++ b/docs/200-orm/200-prisma-client/100-queries/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Queries' +metaTitle: 'Prisma Client Queries' +metaDescription: 'Learn about the database queries you can send with Prisma Client.' +--- + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/057-composite-types.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/057-composite-types.mdx new file mode 100644 index 0000000000..3f1891dc09 --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/057-composite-types.mdx @@ -0,0 +1,798 @@ +--- +title: 'Composite types' +metaTitle: 'Composite types' +metaDescription: 'Composite types' +tocDepth: 3 +--- + + + + + +Composite types are only available with MongoDB. + + + +[Composite types](/orm/prisma-schema/data-model/models#defining-composite-types), known as [embedded documents](https://docs.mongodb.com/manual/core/data-model-design/#std-label-data-modeling-embedding) in MongoDB, allow you to embed records within other records. + +We made composite types [Generally Available](/orm/more/releases#generally-available-ga) in v3.12.0. They were previously available in [Preview](/orm/reference/preview-features) from v3.10.0. + +This page explains how to: + +- [find](#finding-records-that-contain-composite-types-with-find-and-findmany) records that contain composite types using `findFirst` and `findMany` +- [create](#creating-records-with-composite-types-using-create-and-createmany) new records with composite types using `create` and `createMany` +- [update](#changing-composite-types-within-update-and-updatemany) composite types within existing records using `update` and `updateMany` +- [delete](#deleting-records-that-contain-composite-types-with-delete-and-deletemany) records with composite types using `delete` and `deleteMany` + + + +## Example schema + +We’ll use this schema for the examples that follow: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +model Product { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String @unique + price Float + colors Color[] + sizes Size[] + photos Photo[] + orders Order[] +} + +model Order { + id String @id @default(auto()) @map("_id") @db.ObjectId + product Product @relation(fields: [productId], references: [id]) + color Color + size Size + shippingAddress Address + billingAddress Address? + productId String @db.ObjectId +} + +enum Color { + Red + Green + Blue +} + +enum Size { + Small + Medium + Large + XLarge +} + +type Photo { + height Int @default(200) + width Int @default(100) + url String +} + +type Address { + street String + city String + zip String +} +``` + +In this schema, the `Product` model has a `Photo[]` composite type, and the `Order` model has two composite `Address` types. The `shippingAddress` is required, but the `billingAddress` is optional. + +## Considerations when using composite types + +There are currently some limitations when using composite types in Prisma Client: + +- [`findUnique`](/orm/reference/prisma-client-reference#findunique) can't filter on composite types +- [`aggregate`](/orm/prisma-client/queries/aggregation-grouping-summarizing#aggregate), [`groupBy`](/orm/prisma-client/queries/aggregation-grouping-summarizing#group-by), [`count`](/orm/prisma-client/queries/aggregation-grouping-summarizing#count) don’t support composite operations + +## Default values for required fields on composite types + +From version 4.0.0, if you carry out a database read on a composite type when all of the following conditions are true, then Prisma Client inserts the default value into the result. + +Conditions: + +- A field on the composite type is [required](/orm/prisma-schema/data-model/models#optional-and-mandatory-fields), and +- this field has a [default value](/orm/prisma-schema/data-model/models#defining-a-default-value), and +- this field is not present in the returned document or documents. + +Note: + +- This is the same behavior as with [model fields](/orm/reference/prisma-schema-reference#model-field-scalar-types). +- On read operations, Prisma Client inserts the default value into the result, but does not insert the default value into the database. + +In our example schema, suppose that you add a required field to `photo`. This field, `bitDepth`, has a default value: + +```prisma file=schema.prisma highlight=4;add +... +type Photo { + ... + bitDepth Int @default(8) +} + +... +``` + +Suppose that you then run `npx prisma migrate deploy` to [deploy your database changes](/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate) and regenerate your Prisma Client with `npx prisma generate`. Then, you run the following application code: + +```ts +console.dir(await prisma.product.findMany({}), { depth: Infinity }) +``` + +The `bitDepth` field has no content because you have only just added this field, so the query returns the default value of `8`. + +** Earlier versions ** + +Before version 4.0.0, Prisma threw a P2032 error as follows: + +``` +Error converting field "bitDepth" of expected non-nullable +type "int", found incompatible value of "null". +``` + +## Finding records that contain composite types with `find` and `findMany` + +Records can be filtered by a composite type within the `where` operation. + +The following section describes the operations available for filtering by a single type or multiple types, and gives examples of each. + +### Filtering for one composite type + +Use the `is`, `equals`, `isNot` and `isSet` operations to change a single composite type: + +- `is`: Filter results by matching composite types. Requires one or more fields to be present _(e.g. Filter orders by the street name on the shipping address)_ +- `equals`: Filter results by matching composite types. Requires all fields to be present. _(e.g. Filter orders by the full shipping address)_ +- `isNot`: Filter results by non-matching composite types +- `isSet` : Filter optional fields to include only results that have been set (either set to a value, or explicitly set to `null`). Setting this filter to `true` will exclude `undefined` results that are not set at all. + +For example, use `is` to filter for orders with a street name of `'555 Candy Cane Lane'`: + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + is: { + street: '555 Candy Cane Lane', + }, + }, + }, +}) +``` + +Use `equals` to filter for orders which match on all fields in the shipping address: + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + equals: { + street: '555 Candy Cane Lane', + city: 'Wonderland', + zip: '52337', + }, + }, + }, +}) +``` + +You can also use a shorthand notation for this query, where you leave out the `equals`: + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + street: '555 Candy Cane Lane', + city: 'Wonderland', + zip: '52337', + }, + }, +}) +``` + +Use `isNot` to filter for orders that do not have a `zip` code of `'52337'`: + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + isNot: { + zip: '52337', + }, + }, + }, +}) +``` + +Use `isSet` to filter for orders where the optional `billingAddress` has been set (either to a value or to `null`): + +```ts +const orders = await prisma.order.findMany({ + where: { + billingAddress: { + isSet: true, + }, + }, +}) +``` + +### Filtering for many composite types + +Use the `equals`, `isEmpty`, `every`, `some` and `none` operations to filter for multiple composite types: + +- `equals`: Checks exact equality of the list +- `isEmpty`: Checks if the list is empty +- `every`: Every item in the list must match the condition +- `some`: One or more of the items in the list must match the condition +- `none`: None of the items in the list can match the condition +- `isSet` : Filter optional fields to include only results that have been set (either set to a value, or explicitly set to `null`). Setting this filter to `true` will exclude `undefined` results that are not set at all. + +For example, you can use `equals` to find products with a specific list of photos (all `url`, `height` and `width` fields must match): + +```ts +const product = prisma.product.findMany({ + where: { + photos: { + equals: [ + { + url: '1.jpg', + height: 200, + width: 100, + }, + { + url: '2.jpg', + height: 200, + width: 100, + }, + ], + }, + }, +}) +``` + +You can also use a shorthand notation for this query, where you leave out the `equals` and specify just the fields that you want to filter for: + +```ts +const product = prisma.product.findMany({ + where: { + photos: [ + { + url: '1.jpg', + height: 200, + width: 100, + }, + { + url: '2.jpg', + height: 200, + width: 100, + }, + ], + }, +}) +``` + +Use `isEmpty` to filter for products with no photos: + +```ts +const product = prisma.product.findMany({ + where: { + photos: { + isEmpty: true, + }, + }, +}) +``` + +Use `some` to filter for products where one or more photos has a `url` of `"2.jpg"`: + +```ts +const product = prisma.product.findFirst({ + where: { + photos: { + some: { + url: '2.jpg', + }, + }, + }, +}) +``` + +Use `none` to filter for products where no photos have a `url` of `"2.jpg"`: + +```ts +const product = prisma.product.findFirst({ + where: { + photos: { + none: { + url: '2.jpg', + }, + }, + }, +}) +``` + +## Creating records with composite types using `create` and `createMany` + + + +When you create a record with a composite type that has a unique restraint, note that MongoDB does not enforce unique values inside a record. [Learn more](#duplicate-values-in-unique-fields-of-composite-types). + + + +Composite types can be created within a `create` or `createMany` method using the `set` operation. For example, you can use `set` within `create` to create an `Address` composite type inside an `Order`: + +```ts +const order = await prisma.order.create({ + data: { + // Normal relation + product: { connect: { id: 'some-object-id' } }, + color: 'Red', + size: 'Large', + // Composite type + shippingAddress: { + set: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + }, + }, +}) +``` + +You can also use a shorthand notation where you leave out the `set` and specify just the fields that you want to create: + +```ts +const order = await prisma.order.create({ + data: { + // Normal relation + product: { connect: { id: 'some-object-id' } }, + color: 'Red', + size: 'Large', + // Composite type + shippingAddress: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + }, +}) +``` + +For an optional type, like the `billingAddress`, you can also set the value to `null`: + +```ts +const order = await prisma.order.create({ + data: { + // Normal relation + product: { connect: { id: 'some-object-id' } }, + color: 'Red', + size: 'Large', + // Composite type + shippingAddress: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + // Embedded optional type, set to null + billingAddress: { + set: null, + }, + }, +}) +``` + +To model the case where an `product` contains a list of multiple `photos`, you can `set` multiple composite types at once: + +```ts +const product = await prisma.product.create({ + data: { + name: 'Forest Runners', + price: 59.99, + colors: ['Red', 'Green'], + sizes: ['Small', 'Medium', 'Large'], + // New composite type + photos: { + set: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 100, width: 200, url: '2.jpg' }, + ], + }, + }, +}) +``` + +You can also use a shorthand notation where you leave out the `set` and specify just the fields that you want to create: + +```ts +const product = await prisma.product.create({ + data: { + name: 'Forest Runners', + price: 59.99, + // Scalar lists that we already support + colors: ['Red', 'Green'], + sizes: ['Small', 'Medium', 'Large'], + // New composite type + photos: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 100, width: 200, url: '2.jpg' }, + ], + }, +}) +``` + +These operations also work within the `createMany` method. For example, you can create multiple `product`s which each contain a list of `photos`: + +```ts +const product = await prisma.product.createMany({ + data: [ + { + name: 'Forest Runners', + price: 59.99, + colors: ['Red', 'Green'], + sizes: ['Small', 'Medium', 'Large'], + photos: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 100, width: 200, url: '2.jpg' }, + ], + }, + { + name: 'Alpine Blazers', + price: 85.99, + colors: ['Blue', 'Red'], + sizes: ['Large', 'XLarge'], + photos: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 150, width: 200, url: '4.jpg' }, + { height: 200, width: 200, url: '5.jpg' }, + ], + }, + ], +}) +``` + +## Changing composite types within `update` and `updateMany` + + + +When you update a record with a composite type that has a unique restraint, note that MongoDB does not enforce unique values inside a record. [Learn more](#duplicate-values-in-unique-fields-of-composite-types). + + + +Composite types can be set, updated or removed within an `update` or `updateMany` method. The following section describes the operations available for updating a single type or multiple types at once, and gives examples of each. + +### Changing a single composite type + +Use the `set`, `unset` `update` and `upsert` operations to change a single composite type: + +- Use `set` to set a composite type, overriding any existing value +- Use `unset` to unset a composite type. Unlike `set: null`, `unset` removes the field entirely +- Use `update` to update a composite type +- Use `upsert` to `update` an existing composite type if it exists, and otherwise `set` the composite type + +For example, use `update` to update a required `shippingAddress` with an `Address` composite type inside an `Order`: + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + shippingAddress: { + // Update just the zip field + update: { + zip: '41232', + }, + }, + }, +}) +``` + +For an optional embedded type, like the `billingAddress`, use `upsert` to create a new record if it does not exist, and update the record if it does: + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + billingAddress: { + // Create the address if it doesn't exist, + // otherwise update it + upsert: { + set: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + update: { + zip: '84323', + }, + }, + }, + }, +}) +``` + +You can also use the `unset` operation to remove an optional embedded type. The following example uses `unset` to remove the `billingAddress` from an `Order`: + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + billingAddress: { + // Unset the billing address + // Removes "billingAddress" field from order + unset: true, + }, + }, +}) +``` + +You can use [filters](/orm/prisma-client/special-fields-and-types/composite-types#finding-records-that-contain-composite-types-with-find-and-findmany) within `updateMany` to update all records that match a composite type. The following example uses the `is` filter to match the street name from a shipping address on a list of orders: + +```ts +const orders = await prisma.order.updateMany({ + where: { + shippingAddress: { + is: { + street: '555 Candy Cane Lane', + }, + }, + }, + data: { + shippingAddress: { + update: { + street: '111 Candy Cane Drive', + }, + }, + }, +}) +``` + +### Changing multiple composite types + +Use the `set`, `push`, `updateMany` and `deleteMany` operations to change a list of composite types: + +- `set`: Set an embedded list of composite types, overriding any existing list +- `push`: Push values to the end of an embedded list of composite types +- `updateMany`: Update many composite types at once +- `deleteMany`: Delete many composite types at once + +For example, use `push` to add a new photo to the `photos` list: + +```ts +const product = prisma.product.update({ + where: { + id: '62de6d328a65d8fffdae2c18', + }, + data: { + photos: { + // Push a photo to the end of the photos list + push: [{ height: 100, width: 200, url: '1.jpg' }], + }, + }, +}) +``` + +Use `updateMany` to update photos with a `url` of `1.jpg` or `2.png`: + +```ts +const product = prisma.product.update({ + where: { + id: '62de6d328a65d8fffdae2c18', + }, + data: { + photos: { + updateMany: { + where: { + url: '1.jpg', + }, + data: { + url: '2.png', + }, + }, + }, + }, +}) +``` + +The following example uses `deleteMany` to delete all photos with a `height` of 100: + +```ts +const product = prisma.product.update({ + where: { + id: '62de6d328a65d8fffdae2c18', + }, + data: { + photos: { + deleteMany: { + where: { + height: 100, + }, + }, + }, + }, +}) +``` + +## Upserting composite types with `upsert` + + + +When you create or update the values in a composite type that has a unique restraint, note that MongoDB does not enforce unique values inside a record. [Learn more](#duplicate-values-in-unique-fields-of-composite-types). + + + +To create or update a composite type, use the `upsert` method. You can use the same composite operations as the `create` and `update` methods above. + +For example, use `upsert` to either create a new product or add a photo to an existing product: + +```ts +const product = await prisma.product.upsert({ + where: { + name: 'Forest Runners', + }, + create: { + name: 'Forest Runners', + price: 59.99, + colors: ['Red', 'Green'], + sizes: ['Small', 'Medium', 'Large'], + photos: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 100, width: 200, url: '2.jpg' }, + ], + }, + update: { + photos: { + push: { height: 300, width: 400, url: '3.jpg' }, + }, + }, +}) +``` + +## Deleting records that contain composite types with `delete` and `deleteMany` + +To remove records which embed a composite type, use the `delete` or `deleteMany` methods. This will also remove the embedded composite type. + +For example, use `deleteMany` to delete all products with a `size` of `"Small"`. This will also delete any embedded `photos`. + +```ts +const deleteProduct = await prisma.product.deleteMany({ + where: { + sizes: { + equals: 'Small', + }, + }, +}) +``` + +You can also use [filters](/orm/prisma-client/special-fields-and-types/composite-types#finding-records-that-contain-composite-types-with-find-and-findmany) to delete records that match a composite type. The example below uses the `some` filter to delete products that contain a certain photo: + +```ts +const product = await prisma.product.deleteMany({ + where: { + photos: { + some: { + url: '2.jpg', + }, + }, + }, +}) +``` + +## Ordering composite types + +You can use the `orderBy` operation to sort results in ascending or descending order. + +For example, the following command finds all orders and orders them by the city name in the shipping address, in ascending order: + +```ts +const orders = await prisma.order.findMany({ + orderBy: { + shippingAddress: { + city: 'asc', + }, + }, +}) +``` + +## Duplicate values in unique fields of composite types + +Be careful when you carry out any of the following operations on a record with a composite type that has a unique constraint. In this situation, MongoDB does not enforce unique values inside a record. + +- When you create the record +- When you add data to the record +- When you update data in the record + +If your schema has a composite type with a `@@unique` constraint, MongoDB prevents you from storing the same value for the constrained value in two or more of the records that contain this composite type. However, MongoDB does does not prevent you from storing multiple copies of the same field value in a single record. + +Note that you can [use Prisma relations to work around this issue](#use-prisma-relations-to-enforce-unique-values-in-a-record). + +For example, in the following schema, `MailBox` has a composite type, `addresses`, which has a `@@unique` constraint on the `email` field. + +```prisma +type Address { + email String +} + +model MailBox { + name String + addresses Address[] + + @@unique([addresses.email]) +} +``` + +The following code creates a record with two identical values in `address`. MongoDB does not throw an error in this situation, and it stores `alice@prisma.io` in `addresses` twice. + +```ts +await prisma.MailBox.createMany({ + data: [ + { + name: 'Alice', + addresses: { + set: [ + { + address: 'alice@prisma.io', // Not unique + }, + { + address: 'alice@prisma.io', // Not unique + }, + ], + }, + }, + ], +}) +``` + +Note: MongoDB throws an error if you try to store the same value in two separate records. In our example above, if you try to store the email address `alice@prisma.io` for the user Alice and for the user Bob, MongoDB does not store the data and throws an error. + +### Use Prisma relations to enforce unique values in a record + +In the example above, MongoDB did not enforce the unique constraint on a nested address name. However, you can model your data differently to enforce unique values in a record. To do so, use Prisma [relations](/orm/prisma-schema/data-model/relations) to turn the composite type into a collection. Set a relationship to this collection and place a unique constraint on the field that you want to be unique. + +In the following example, MongoDB enforces unique values in a record. There is a relation between `Mailbox` and the `Address` model. Also, the `name` field in the `Address` model has a unique constraint. + +```prisma +model Address { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + mailbox Mailbox? @relation(fields: [mailboxId], references: [id]) + mailboxId String? @db.ObjectId + + @@unique([name]) +} + +model Mailbox { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + addresses Address[] @relation +} +``` + +```ts +await prisma.MailBox.create({ + data: { + name: 'Alice', + addresses: { + create: [ + { name: 'alice@prisma.io' }, // Not unique + { name: 'alice@prisma.io' }, // Not unique + ], + }, + }, +}) +``` + +If you run the above code, MongoDB enforces the unique constraint. It does not allow your application to add two addresses with the name `alice@prisma.io`. diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/080-null-and-undefined.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/080-null-and-undefined.mdx new file mode 100644 index 0000000000..e98f66fd42 --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/080-null-and-undefined.mdx @@ -0,0 +1,367 @@ +--- +title: 'Null and undefined' +metaTitle: 'Null and undefined (Reference)' +metaDescription: 'How Prisma Client handles null and undefined, including a GraphQL use case.' +preview: false +--- + + + +Prisma Client differentiates between `null` and `undefined`: + +- `null` is a **value** +- `undefined` means **do nothing** + + + +This is particularly important to account for in [a **Prisma with GraphQL context**, where `null` and `undefined` are interchangeable](#null-and-undefined-in-a-graphql-resolver). + + + +The data below represents a `User` table. This set of data will be used in all of the examples below: + +| id | name | email | +| --- | ------- | ----------------- | +| 1 | Nikolas | nikolas@gmail.com | +| 2 | Martin | martin@gmail.com | +| 3 | _empty_ | sabin@gmail.com | +| 4 | Tyler | tyler@gmail.com | + + + + + +## `null` and `undefined` in queries that affect _many_ records + +This section will cover how `undefined` and `null` values affect the behavior of queries that interact with or create multiple records in a database. + +### Null + +Consider the following Prisma Client query which searches for all users whose `name` value matches the provided `null` value: + + + + + +```ts +const users = await prisma.user.findMany({ + where: { + name: null, + }, +}) +``` + + + + + +```json +[ + { + "id": 3, + "name": null, + "email": "sabin@gmail.com" + } +] +``` + + + + + +Because `null` was provided as the filter for the `name` column, Prisma Client will generate a query that searches for all records in the `User` table whose `name` column is _empty_. + +### Undefined + +Now consider the scenario where you run the same query with `undefined` as the filter value on the `name` column: + + + + + +```ts +const users = await prisma.user.findMany({ + where: { + name: undefined, + }, +}) +``` + + + + +```json +[ + { + "id": 1, + "name": "Nikolas", + "email": "nikolas@gmail.com" + }, + { + "id": 2, + "name": "Martin", + "email": "martin@gmail.com" + }, + { + "id": 3, + "name": null, + "email": "sabin@gmail.com" + }, + { + "id": 4, + "name": "Tyler", + "email": "tyler@gmail.com" + } +] +``` + + + + +Using `undefined` as a value in a filter essentially tells Prisma Client you have decided _not to define a filter_ for that column. + +An equivalent way to write the above query would be: + +```ts +const users = await prisma.user.findMany() +``` + +This query will select every row from the `User` table. + + + +**Note**: Using `undefined` as the value of any key in a Prisma Client query's parameter object will cause Prisma to act as if that key was not provided at all. + + + +Although this section's examples focused on the `findMany` function, the same concepts apply to any function that can affect multiple records, such as `updateMany` and `deleteMany`. + +## `null` and `undefined` in queries that affect _one_ record + +This section will cover how `undefined` and `null` values affect the behavior of queries that interact with or create a single record in a database. + + + +**Note**: `null` is not a valid filter value in a `findUnique` query. + + + +The query behavior when using `null` and `undefined` in the filter criteria of a query that affects a single record is very similar to the behaviors described in the previous section. + +### Null + +Consider the following query where `null` is used to filter the `name` column: + + + + + +```ts +const user = await prisma.user.findFirst({ + where: { + name: null, + }, +}) +``` + + + + + +```json +[ + { + "id": 3, + "name": null, + "email": "sabin@gmail.com" + } +] +``` + + + + +Because `null` was used as the filter on the `name` column, Prisma Client will generate a query that searches for the first record in the `User` table whose `name` value is _empty_. + +### Undefined + +If `undefined` is used as the filter value on the `name` column instead, _the query will act as if no filter criteria was passed to that column at all_. + +Consider the query below: + + + + + +```ts +const user = await prisma.user.findFirst({ + where: { + name: undefined, + }, +}) +``` + + + + + +```json +[ + { + "id": 1, + "name": "Nikolas", + "email": "nikolas@gmail.com" + } +] +``` + + + + +In this scenario, the query will return the very first record in the database. + +Another way to represent the above query is: + +```ts +const user = await prisma.user.findFirst() +``` + +Although this section's examples focused on the `findFirst` function, the same concepts apply to any function that affects a single record. + +## `null` and `undefined` in a GraphQL resolver + +For this example, consider a database based on the following Prisma schema: + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? +} +``` + +In the following GraphQL mutation that updates a user, both `authorEmail` and `name` accept `null`. From a GraphQL perspective, this means that fields are **optional**: + +```ts +type Mutation { + // Update author's email or name, or both - or neither! + updateUser(id: Int!, authorEmail: String, authorName: String): User! +} +``` + +However, if you pass `null` values for `authorEmail` or `authorName` on to Prisma, the following will happen: + +- If `args.authorEmail` is `null`, the query will **fail**. `email` does not accept `null`. +- If `args.authorName` is `null`, Prisma changes the value of `name` to `null`. This is probably not how you want an update to work. + +```ts +updateUser: (parent, args, ctx: Context) => { + return ctx.prisma.user.update({ + where: { id: Number(args.id) }, + data: { +| email: args.authorEmail, // email cannot be null +| name: args.authorName // name set to null - potentially unwanted behavior + }, + }) +}, +``` + +Instead, set the value of `email` and `name` to `undefined` if the input value is `null`. Doing this is the same as not updating the field at all: + +```ts +updateUser: (parent, args, ctx: Context) => { + return ctx.prisma.user.update({ + where: { id: Number(args.id) }, + data: { +| email: args.authorEmail != null ? args.authorEmail : undefined, // If null, do nothing +| name: args.authorName != null ? args.authorName : undefined // If null, do nothing + }, + }) +}, +``` + +## The effect of `null` and `undefined` on conditionals + +There are some caveats to filtering with conditionals which might produce unexpected results. When filtering with conditionals you might expect one result but receive another given how Prisma treats nullable values. + +The following table provides a high-level overview of how the different operators handle 0, 1 and `n` filters. + +| Operator | 0 filters | 1 filter | n filters | +| -------- | ----------------- | ---------------------- | -------------------- | +| `OR` | return empty list | validate single filter | validate all filters | +| `AND` | return all items | validate single filter | validate all filters | +| `NOT` | return all items | validate single filter | validate all filters | + +This example shows how an `undefined` parameter impacts the results returned by a query that uses the [`OR`](/orm/reference/prisma-client-reference#or) operator. + +```ts +interface FormData { + name: string + email?: string +} + +const formData: FormData = { + name: 'Emelie', +} + +const users = await prisma.user.findMany({ + where: { + OR: [ + { + email: { + contains: formData.email, + }, + }, + ], + }, +}) + +// returns: [] +``` + +The query receives filters from a formData object, which includes an optional email property. In this instance, the value of the email property is `undefined`. When this query is run no data is returned. + +This is in contrast to the [`AND`](/orm/reference/prisma-client-reference#and) and [`NOT`](/orm/reference/prisma-client-reference#not-1) operators, which will both return all the users +if you pass in an `undefined` value. + +> This is because passing an `undefined` value to an `AND` or `NOT` operator is the same +> as passing nothing at all, meaning the `findMany` query in the example will run without any filters and return all the users. + +```ts +interface FormData { + name: string + email?: string +} + +const formData: FormData = { + name: 'Emelie', +} + +const users = await prisma.user.findMany({ + where: { + AND: [ + { + email: { + contains: formData.email, + }, + }, + ], + }, +}) + +// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' } + +const users = await prisma.user.findMany({ + where: { + NOT: [ + { + email: { + contains: formData.email, + }, + }, + ], + }, +}) + +// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' } +``` diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/100-working-with-json-fields.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/100-working-with-json-fields.mdx new file mode 100644 index 0000000000..84d40d3f6f --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/100-working-with-json-fields.mdx @@ -0,0 +1,992 @@ +--- +title: 'Working with Json fields' +metaTitle: 'Working with Json fields (Concepts)' +metaDescription: 'How to read, write, and filter by Json fields.' +tocDepth: 3 +--- + + + +Use the [`Json`](/orm/reference/prisma-schema-reference#json) Prisma field type to read, write, and perform basic filtering on JSON types in the underlying database. In the following example, the `User` model has an optional `Json` field named `extendedPetsData`: + +```prisma highlight=6;normal +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] + extendedPetsData Json? +} +``` + +Example field value: + +```json +{ + "pet1": { + "petName": "Claudine", + "petType": "House cat" + }, + "pet2": { + "petName": "Sunny", + "petType": "Gerbil" + } +} +``` + +> **Note**: The `Json` field is only supported if the [underlying database](/orm/overview) has a corresponding JSON data type. + +The `Json` field supports a few additional types, such as `string` and `boolean`. These additional types exist to match the types supported by [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse): + +```ts +export declare type JsonValue = + | string + | number + | boolean + | null + | JsonObject + | JsonArray +``` + + + +## Use cases for JSON fields + +Reasons to store data as JSON rather than representing data as related models include: + +- You need to store data that does not have a consistent structure +- You are importing data from another system and do not want to map that data to Prisma models + +## Reading a `Json` field + +You can use the `Prisma.JsonArray` and `Prisma.JsonObject` utility classes to work with the contents of a `Json` field: + +```ts +const { PrismaClient, Prisma } = require('@prisma/client') + +const user = await prisma.user.findFirst({ + where: { + id: 9, + }, +}) + +// Example extendedPetsData data: +// [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }] + +if ( + user?.extendedPetsData && + typeof user?.extendedPetsData === 'object' && + Array.isArray(user?.extendedPetsData) +) { + const petsObject = user?.extendedPetsData as Prisma.JsonArray + + const firstPet = petsObject[0] +} +``` + +See also: [Advanced example: Update a nested JSON key value](#advanced-example-update-a-nested-json-key-value) + +## Writing to a `Json` field + +The following example writes a JSON object to the `extendedPetsData` field: + +```ts +var json = [ + { name: 'Bob the dog' }, + { name: 'Claudine the cat' }, +] as Prisma.JsonArray + +const createUser = await prisma.user.create({ + data: { + email: 'birgitte@prisma.io', + extendedPetsData: json, + }, +}) +``` + +> **Note**: JavaScript objects (for example, `{ extendedPetsData: "none"}`) are automatically converted to JSON. + +See also: [Advanced example: Update a nested JSON key value](#advanced-example-update-a-nested-json-key-value) + +## Filter on a `Json` field + +From v2.23.0, you can filter rows by the data inside a `Json` type. We call this **advanced `Json` filtering**. + +The availability of advanced `Json` filtering depends on your Prisma version: + +- V4.0.0 or later: advanced `Json` filtering is [generally available](/orm/more/releases#generally-available-ga). +- From v2.23.0, but before v4.0.0: advanced `Json` filtering is a [preview feature](/orm/reference/preview-features/client-preview-features). Add `previewFeatures = ["filterJson"]` to your schema. [Learn more](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature). +- Before v2.23.0: you can [filter on the exact `Json` field value](#filter-on-exact-field-value), but you cannot use the other features described in this section. + + + +Advanced `Json` filtering is supported by [PostgreSQL](/orm/overview/databases/postgresql) and [MySQL](/orm/overview/databases/mysql) only with different syntaxes for the `path` option. PostgreSQL does not support [filtering on object key values in arrays](#filtering-on-object-key-value-inside-array). + + + +### Database connector implementation differences + +The implementation of `Json` filtering differs between connectors: + +- The [MySQL connector](/orm/overview/databases/mysql) uses [MySQL's implementation of JSON path](https://dev.mysql.com/doc/refman/8.0/en/json.html#json-path-syntax) +- The [PostgreSQL connector](/orm/overview/databases/postgresql) uses the custom JSON functions and operators [supported in version 12 _and earlier_](https://www.postgresql.org/docs/11/functions-json.html) + +This means that `path` option syntax differs between database connectors - for example, the following is a valid MySQL `path` value: + +``` +$petFeatures.petName +``` + +The following is a valid PostgreSQL `path` value: + +``` +["petFeatures", "petName"] +``` + +### Filter on exact field value + +The following query returns all users where the value of `extendedPetsData` matches the `json` variable exactly: + +```ts +var json = { [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }] } + +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + equals: json, + }, + }, +}) +``` + +The following query returns all users where the value of `extendedPetsData` does **not** match the `json` variable exactly: + +```ts +var json = { + extendedPetsData: [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }], +} + +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + not: json, + }, + }, +}) +``` + +### Filter on object property + +In [2.23.0](https://github.com/prisma/prisma/releases/tag/2.23.0) and later, you can filter on a specific property inside a block of JSON. In the following examples, the value of `extendedPetsData` is a one-dimensional, unnested JSON object: + +```json highlight=11;normal +{ + "petName": "Claudine", + "petType": "House cat" +} +``` + +The following query returns all users where the value of `petName` is `"Claudine"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['petName'], + equals: 'Claudine', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.petName', + equals: 'Claudine', + }, + }, +}) +``` + + + + + +The following query returns all users where the value of `petType` _contains_ `"cat"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['petType'], + string_contains: 'cat', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.petType', + string_contains: 'cat', + }, + }, +}) +``` + + + + +The following string filters are available: + +- [`string_contains`](/orm/reference/prisma-client-reference#string_contains) +- [`string_starts_with`](/orm/reference/prisma-client-reference#string_starts_with) +- [`string_ends_with`](/orm/reference/prisma-client-reference#string_ends_with) . + +### Filter on nested object property + +You can filter on nested JSON properties. In the following examples, the value of `extendedPetsData` is a JSON object with several levels of nesting. + +```json +{ + "pet1": { + "petName": "Claudine", + "petType": "House cat" + }, + "pet2": { + "petName": "Sunny", + "petType": "Gerbil", + "features": { + "eyeColor": "Brown", + "furColor": "White and black" + } + } +} +``` + +The following query returns all users where `"pet2"` → `"petName"` is `"Sunny"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['pet2', 'petName'], + equals: 'Sunny', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.pet2.petName', + equals: 'Sunny', + }, + }, +}) +``` + + + + +The following query returns all users where: + +- `"pet2"` → `"petName"` is `"Sunny"` +- `"pet2"` → `"features"` → `"furColor"` contains `"black"` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + AND: [ + { + extendedPetsData: { + path: ['pet2', 'petName'], + equals: 'Sunny', + }, + }, + { + extendedPetsData: { + path: ['pet2', 'features', 'furColor'], + string_contains: 'black', + }, + }, + ], + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + AND: [ + { + extendedPetsData: { + path: '$.pet2.petName', + equals: 'Sunny', + }, + }, + { + extendedPetsData: { + path: '$.pet2.features.furColor', + string_contains: 'black', + }, + }, + ], + }, +}) +``` + + + + + +### Filtering on an array value + +You can filter on the presence of a specific value in a scalar array (strings, integers). In the following example, the value of `extendedPetsData` is an array of strings: + +```json +["Claudine", "Sunny"] +``` + +The following query returns all users with a pet named `"Claudine"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + array_contains: ['Claudine'], + }, + }, +}) +``` + + + +**Note**: In PostgreSQL, the value of `array_contains` must be an array and not a string, even if the array only contains a single value. + + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + array_contains: 'Claudine', + }, + }, +}) +``` + + + + + +The following array filters are available: + +- [`array_contains`](/orm/reference/prisma-client-reference#array_contains) +- [`array_starts_with`](/orm/reference/prisma-client-reference#array_starts_with) +- [`array_ends_with`](/orm/reference/prisma-client-reference#array_ends_with) + +### Filtering on nested array value + +You can filter on the presence of a specific value in a scalar array (strings, integers). In the following examples, the value of `extendedPetsData` includes nested scalar arrays of names: + +```json +{ + "cats": { "owned": ["Bob", "Sunny"], "fostering": ["Fido"] }, + "dogs": { "owned": ["Ella"], "fostering": ["Prince", "Empress"] } +} +``` + +#### Scalar value arrays + +The following query returns all users that foster a cat named `"Fido"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['cats', 'fostering'], + array_contains: ['Fido'], + }, + }, +}) +``` + + + +**Note**: In PostgreSQL, the value of `array_contains` must be an array and not a string, even if the array only contains a single value. + + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.cats.fostering', + array_contains: 'Fido', + }, + }, +}) +``` + + + + +The following query returns all users that foster cats named `"Fido"` _and_ `"Bob"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['cats', 'fostering'], + array_contains: ['Fido', 'Bob'], + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.cats.fostering', + array_contains: ['Fido', 'Bob'], + }, + }, +}) +``` + + + + +#### JSON object arrays + + + + +```ts +const json = [{ status: 'expired', insuranceID: 92 }] + +const checkJson = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['insurances'], + array_contains: json, + }, + }, +}) +``` + + + + +```ts +const json = { status: 'expired', insuranceID: 92 } + +const checkJson = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.insurances', + array_contains: json, + }, + }, +}) +``` + + + + +- If you are using PostgreSQL, you must pass in an array of objects to match, even if that array only contains one object: + + ```json5 + [{ status: 'expired', insuranceID: 92 }] + // PostgreSQL + ``` + + If you are using MySQL, you must pass in a single object to match: + + ```json5 + { status: 'expired', insuranceID: 92 } + // MySQL + ``` + +- If your filter array contains multiple objects, PostgreSQL will only return results if _all_ objects are present - not if at least one object is present. + +- You must set `array_contains` to a JSON object, not a string. If you use a string, Prisma escapes the quotation marks and the query will not return results. For example: + + ```ts + array_contains: '[{"status": "expired", "insuranceID": 92}]' + ``` + + is sent to the database as: + + ``` + [{\"status\": \"expired\", \"insuranceID\": 92}] + ``` + +### Targeting an array element by index + +You can filter on the value of an element in a specific position. + +```json +{ "owned": ["Bob", "Sunny"], "fostering": ["Fido"] } +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + comments: { + path: ['owned', '1'], + string_contains: 'Bob', + }, + }, +}) +``` + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + comments: { + path: '$.owned[1]', + string_contains: 'Bob', + }, + }, +}) +``` + + + + + +### Filtering on object key value inside array + +Depending on your provider, you can filter on the key value of an object inside an array. + + + +Filtering on object key values within an array is **only** supported by the [MySQL database connector](/orm/overview/databases/mysql). However, you can still [filter on the presence of entire JSON objects](#json-object-arrays). + + + +In the following example, the value of `extendedPetsData` is an array of objects with a nested `insurances` array, which contains two objects: + +```json +[ + { + "petName": "Claudine", + "petType": "House cat", + "insurances": [ + { "insuranceID": 92, "status": "expired" }, + { "insuranceID": 12, "status": "active" } + ] + }, + { + "petName": "Sunny", + "petType": "Gerbil" + }, + { + "petName": "Gerald", + "petType": "Corn snake" + }, + { + "petName": "Nanna", + "petType": "Moose" + } +] +``` + +The following query returns all users where at least one pet is a moose: + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$[*].petType', + array_contains: 'Moose', + }, + }, +}) +``` + +- `$[*]` is the root array of pet objects +- `petType` matches the `petType` key in any pet object + +The following query returns all users where at least one pet has an expired insurance: + +```ts +const getUsers = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$[*].insurances[*].status', + array_contains: 'expired', + }, + }, +}) +``` + +- `$[*]` is the root array of pet objects +- `insurances[*]` matches any `insurances` array inside any pet object +- `status` matches any `status` key in any insurance object + +## Advanced example: Update a nested JSON key value + +The following example assumes that the value of `extendedPetsData` is some variation of the following: + +```json +{ + "petName": "Claudine", + "petType": "House cat", + "insurances": [ + { "insuranceID": 92, "status": "expired" }, + { "insuranceID": 12, "status": "active" } + ] +} +``` + +The following example: + +1. Gets all users +1. Change the `"status"` of each insurance object to `"expired"` +1. Get all users that have an expired insurance where the ID is `92` + + + + +```ts +const userQueries: string | any[] = [] + +getUsers.forEach((user) => { + if ( + user.extendedPetsData && + typeof user.extendedPetsData === 'object' && + !Array.isArray(user.extendedPetsData) + ) { + const petsObject = user.extendedPetsData as Prisma.JsonObject + + const i = petsObject['insurances'] + + if (i && typeof i === 'object' && Array.isArray(i)) { + const insurancesArray = i as Prisma.JsonArray + + insurancesArray.forEach((i) => { + if (i && typeof i === 'object' && !Array.isArray(i)) { + const insuranceObject = i as Prisma.JsonObject + + insuranceObject['status'] = 'expired' + } + }) + + const whereClause = Prisma.validator()({ + id: user.id, + }) + + const dataClause = Prisma.validator()({ + extendedPetsData: petsObject, + }) + + userQueries.push( + prisma.user.update({ + where: whereClause, + data: dataClause, + }) + ) + } + } +}) + +if (userQueries.length > 0) { + console.log(userQueries.length + ' queries to run!') + await prisma.$transaction(userQueries) +} + +const json = [{ status: 'expired', insuranceID: 92 }] + +const checkJson = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: ['insurances'], + array_contains: json, + }, + }, +}) + +console.log(checkJson.length) +``` + + + + + +```ts +const userQueries: string | any[] = [] + +getUsers.forEach((user) => { + if ( + user.extendedPetsData && + typeof user.extendedPetsData === 'object' && + !Array.isArray(user.extendedPetsData) + ) { + const petsObject = user.extendedPetsData as Prisma.JsonObject + + const insuranceList = petsObject['insurances'] // is a Prisma.JsonArray + + if (Array.isArray(insuranceList)) { + insuranceList.forEach((insuranceItem) => { + if ( + insuranceItem && + typeof insuranceItem === 'object' && + !Array.isArray(insuranceItem) + ) { + insuranceItem['status'] = 'expired' // is a Prisma.JsonObject + } + }) + + const whereClause = Prisma.validator()({ + id: user.id, + }) + + const dataClause = Prisma.validator()({ + extendedPetsData: petsObject, + }) + + userQueries.push( + prisma.user.update({ + where: whereClause, + data: dataClause, + }) + ) + } + } +}) + +if (userQueries.length > 0) { + console.log(userQueries.length + ' queries to run!') + await prisma.$transaction(userQueries) +} + +const json = { status: 'expired', insuranceID: 92 } + +const checkJson = await prisma.user.findMany({ + where: { + extendedPetsData: { + path: '$.insurances', + array_contains: json, + }, + }, +}) + +console.log(checkJson.length) +``` + + + + +## Using `null` Values + +There are two types of `null` values possible for a `JSON` field in an SQL database. + +- Database `NULL`: The value in the database is a `NULL`. +- JSON `null`: The value in the database contains a JSON value that is `null`. + +To differentiate between these possibilities, we've introduced three _null enums_ you can use: + +- `JsonNull`: Represents the `null` value in JSON. +- `DbNull`: Represents the `NULL` value in the database. +- `AnyNull`: Represents both `null` JSON values and `NULL` database values. (Only when filtering) + + + +From v4.0.0, `JsonNull`, `DbNull`, and `AnyNull` are objects. Before v4.0.0, they were strings. + + + + + +- When filtering using any of the _null enums_ you can not use a shorthand and leave the `equals` operator off. +- These _null enums_ do not apply to MongoDB because there the difference between a JSON `null` and a database `NULL` does not exist. +- The _null enums_ do not apply to the `array_contains` operator in all databases because there can only be a JSON `null` within a JSON array. Since there cannot be a database `NULL` within a JSON array, `{ array_contains: null }` is not ambiguous. + + + +For example: + +```prisma +model Log { + id Int @id + meta Json +} +``` + +Here is an example of using `AnyNull`: + +```ts highlight=7;normal +import { Prisma } from '@prisma/client' + +prisma.log.findMany({ + where: { + data: { + meta: { + equals: Prisma.AnyNull, + }, + }, + }, +}) +``` + +### Inserting `null` Values + +This also applies to `create`, `update` and `upsert`. To insert a `null` value +into a `Json` field, you would write: + +```ts highlight=5;normal +import { Prisma } from '@prisma/client' + +prisma.log.create({ + data: { + meta: Prisma.JsonNull, + }, +}) +``` + +And to insert a database `NULL` into a `Json` field, you would write: + +```ts highlight=5;normal +import { Prisma } from '@prisma/client' + +prisma.log.create({ + data: { + meta: Prisma.DbNull, + }, +}) +``` + +### Filtering by `null` Values + +To filter by `JsonNull` or `DbNull`, you would write: + +```ts highlight=6;normal +import { Prisma } from '@prisma/client' + +prisma.log.findMany({ + where: { + meta: { + equals: Prisma.AnyNull, + }, + }, +}) +``` + + + +These _null enums_ do not apply to MongoDB because MongoDB does not differentiate between a JSON `null` and a database `NULL`. They also do not apply to the `array_contains` operator in all databases because there can only be a JSON `null` within a JSON array. Since there cannot be a database `NULL` within a JSON array, `{ array_contains: null }` is not ambiguous. + + + +## Typed `Json` + +By default, `Json` fields are not typed in Prisma models. To accomplish strong typing inside of these fields, you will need to use an external package like [prisma-json-types-generator](https://www.npmjs.com/package/prisma-json-types-generator) to accomplish this. + +### Using `prisma-json-types-generator` + +First, install and configure `prisma-json-types-generator` [according to the package's instructions](https://www.npmjs.com/package/prisma-json-types-generator#using-it). + +Then, assuming you have a model like the following: + +```prisma no-copy +model Log { + id Int @id + meta Json +} +``` + +You can update it and type it by using [abstract syntax tree comments](https://www.prisma.io/docs/orm/prisma-schema/overview#comments) + +```prisma highlight=4;normal file=schema.prisma +model Log { + id Int @id + + /// [LogMetaType] + meta Json +} +``` + +Then, make sure you define the above type in a type declaration file included in your `tsconfig.json` + +```ts file=types.ts +declare global { + namespace PrismaJson { + type LogMetaType = { timestamp: number; host: string } + } +} +``` + +Now, when working with `Log.meta` it will be strongly typed! + +## `Json` FAQs + +### Can you select a subset of JSON key/values to return? + +No - it is not yet possible to [select which JSON elements to return](https://github.com/prisma/prisma/issues/2431). Prisma Client returns the entire JSON object. + +### Can you filter on the presence of a specific key? + +No - it is not yet possible to filter on the presence of a specific key. + +### Is case insensitive filtering supported? + +No - [case insensitive filtering](https://github.com/prisma/prisma/issues/7390) is not yet supported. diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/200-working-with-scalar-lists-arrays.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/200-working-with-scalar-lists-arrays.mdx new file mode 100644 index 0000000000..ab258de11f --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/200-working-with-scalar-lists-arrays.mdx @@ -0,0 +1,219 @@ +--- +title: 'Working with scalar lists' +metaTitle: 'Working with scalar lists/arrays (Concepts)' +metaDescription: 'How to read, write, and filter by scalar lists / arrays.' +tocDepth: 3 +--- + + + +[Scalar lists](/orm/reference/prisma-schema-reference#-modifier) are represented by the `[]` modifier and are only available if the underlying database supports scalar lists. The following example has one scalar `String` list named `pets`: + + + + +```prisma highlight=4;normal +model User { + id Int @id @default(autoincrement()) + name String + pets String[] +} +``` + + + + +```prisma highlight=4;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + pets String[] +} +``` + + + + +Example field value: + +```json5 +['Fido', 'Snoopy', 'Brian'] +``` + + + +## Setting the value of a scalar list + +The following example demonstrates how to [`set`](/orm/reference/prisma-client-reference#set-1) the value of a scalar list (`coinflips`) when you create a model: + +```ts +const createdUser = await prisma.user.create({ + data: { + email: 'eloise@prisma.io', + coinflips: [true, true, true, false, true], + }, +}) +``` + +## Unsetting the value of a scalar list + + + +This method is available on MongoDB only in versions +[3.11.1](https://github.com/prisma/prisma/releases/tag/3.11.1) and later. + + + +The following example demonstrates how to [`unset`](/orm/reference/prisma-client-reference#unset) the value of a scalar list (`coinflips`): + +```ts +const createdUser = await prisma.user.create({ + data: { + email: 'eloise@prisma.io', + coinflips: { + unset: true, + }, + }, +}) +``` + +Unlike `set: null`, `unset` removes the list entirely. + +## Adding items to a scalar list + + + +Available for: + +- PostgreSQL in versions [2.15.0](https://github.com/prisma/prisma/releases/tag/2.15.0) and later +- CockroachDB in versions [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) and later +- MongoDB in versions [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0) and later + + + +Use the [`push`](/orm/reference/prisma-client-reference#push) method to add a single value to a scalar list: + +```ts +const userUpdate = await prisma.user.update({ + where: { + id: 9, + }, + data: { + coinflips: { + push: true, + }, + }, +}) +``` + +In earlier versions, you have to overwrite the entire value. The following example retrieves user, uses `push()` to add three new coin flips, and overwrites the `coinflips` field in an `update`: + +```ts +const user = await prisma.user.findUnique({ + where: { + email: 'eloise@prisma.io', + }, +}) + +if (user) { + console.log(user.coinflips) + + user.coinflips.push(true, true, false) + + const updatedUser = await prisma.user.update({ + where: { + email: 'eloise@prisma.io', + }, + data: { + coinflips: user.coinflips, + }, + }) + + console.log(updatedUser.coinflips) +} +``` + +## Filtering scalar lists + + + +Available for: + +- PostgreSQL in versions [2.15.0](https://github.com/prisma/prisma/releases/tag/2.15.0) and later +- CockroachDB in versions [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) and later +- MongoDB in versions [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0) and later + + + +Use [scalar list filters](/orm/reference/prisma-client-reference#scalar-list-filters) to filter for records with scalar lists that match a specific condition. The following example returns all posts where the tags list includes `databases` _and_ `typescript`: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + hasEvery: ['databases', 'typescript'], + }, + }, +}) +``` + +### `NULL` values in arrays + + + +This section applies to: + +- PostgreSQL in versions [2.15.0](https://github.com/prisma/prisma/releases/tag/2.15.0) and later +- CockroachDB in versions [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) and later + + + +When using scalar list filters with a relational database connector, array fields with a `NULL` value are not considered by the following conditions: + +- `NOT` (array does not contain X) +- `isEmpty` (array is empty) + +This means that records you might expect to see are not returned. Consider the following examples: + +- The following query returns all posts where the `tags` **do not** include `databases`: + + ```ts + const posts = await prisma.post.findMany({ + where: { + NOT: { + tags: { + has: 'databases', + }, + }, + }, + }) + ``` + + - ✔ Arrays that do not contain `"databases"`, such as `{"typescript", "graphql"}` + - ✔ Empty arrays, such as `[]` + + The query does not return: + + - ✘ `NULL` arrays, even though they do not contain `"databases"` + +The following query returns all posts where `tags` is empty: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + isEmpty: true, + }, + }, +}) +``` + +The query returns: + +- ✔ Empty arrays, such as `[]` + +The query does not return: + +- ✘ `NULL` arrays, even though they could be considered empty + +To work around this issue, you can set the default value of array fields to `[]`. diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/300-working-with-composite-ids-and-constraints.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/300-working-with-composite-ids-and-constraints.mdx new file mode 100644 index 0000000000..127a4d0cf8 --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/300-working-with-composite-ids-and-constraints.mdx @@ -0,0 +1,206 @@ +--- +title: 'Working with compound IDs and unique constraints' +metaTitle: 'Working with compound IDs and unique constraints (Concepts)' +metaDescription: 'How to read, write, and filter by compound IDs and unique constraints.' +tocDepth: 2 +--- + + + +Composite IDs and compound unique constraints can be defined in your Prisma schema using the [`@@id`](/orm/reference/prisma-schema-reference#id-1) and [`@@unique`](/orm/reference/prisma-schema-reference#unique-1) attributes. + + + +**MongoDB does not support `@@id`**
+MongoDB does not support composite IDs, which means you cannot identify a model with a `@@id` attribute. + +
+ +A composite ID or compound unique constraint uses the combined values of two fields as a primary key or identifier in your database table. In the following example, the `postId` field and `userId` field are used as a composite ID for a `Like` table: + +```prisma highlight=22;normal +model User { + id Int @id @default(autoincrement()) + name String + post Post[] + likes Like[] +} + +model Post { + id Int @id @default(autoincrement()) + content String + User User? @relation(fields: [userId], references: [id]) + userId Int? + likes Like[] +} + +model Like { + postId Int + userId Int + User User @relation(fields: [userId], references: [id]) + Post Post @relation(fields: [postId], references: [id]) + + @@id([postId, userId]) +} +``` + +Querying for records from the `Like` table (e.g. using `prisma.like.findMany()`) would return objects that look as follows: + +```json +{ + "postId": 1, + "userId": 1 +} +``` + +Although there are only two fields in the response, those two fields make up a compound ID named `postId_userId`. + +You can also create a named compound ID or compound unique constraint by using the `@@id` or `@@unique` attributes' `name` field. For example: + +```prisma highlight=7;normal +model Like { + postId Int + userId Int + User User @relation(fields: [userId], references: [id]) + Post Post @relation(fields: [postId], references: [id]) + + @@id(name: "likeId", [postId, userId]) +} +``` + +
+ +## Where you can use compound IDs and unique constraints + +Compound IDs and compound unique constraints can be used when working with _unique_ data. + +Below is a list of Prisma Client functions that accept a compound ID or compound unique constraint in the `where` filter of the query: + +- `findUnique` +- `findUniqueOrThrow` +- `delete` +- `update` +- `upsert` + +A composite ID and a composite unique constraint is also usable when creating relational data with `connect` and `connectOrCreate`. + +## Filtering records by a compound ID or unique constraint + +Although your query results will not display a compound ID or unique constraint as a field, you can use these compound values to filter your queries for unique records: + +```ts highlight=3-6;normal +const like = await prisma.like.findUnique({ + where: { + likeId: { + userId: 1, + postId: 1, + }, + }, +}) +``` + + + +Note composite ID and compound unique constraint keys are only available as filter options for _unique_ queries such as `findUnique` and `findUniqueOrThrow`. See the [section](/orm/prisma-client/special-fields-and-types/working-with-composite-ids-and-constraints#where-you-can-use-compound-ids-and-unique-constraints) above for a list of places these fields may be used. + + + +## Deleting records by a compound ID or unique constraint + +A compound ID or compound unique constraint may be used in the `where` filter of a `delete` query: + +```ts highlight=3-6;normal +const like = await prisma.like.delete({ + where: { + likeId: { + userId: 1, + postId: 1, + }, + }, +}) +``` + +## Updating and upserting records by a compound ID or unique constraint + +A compound ID or compound unique constraint may be used in the `where` filter of an `update` query: + +```ts highlight=3-6;normal +const like = await prisma.like.update({ + where: { + likeId: { + userId: 1, + postId: 1, + }, + }, + data: { + postId: 2, + }, +}) +``` + +They may also be used in the `where` filter of an `upsert` query: + +```ts highlight=3-6;normal +await prisma.like.upsert({ + where: { + likeId: { + userId: 1, + postId: 1, + }, + }, + update: { + userId: 2, + }, + create: { + userId: 2, + postId: 1, + }, +}) +``` + +## Filtering relation queries by a compound ID or unique constraint + +Compound IDs and compound unique constraint can also be used in the `connect` and `connectOrCreate` keys used when connecting records to create a relationship. + +For example, consider this query: + +```ts highlight=6-9;normal +await prisma.user.create({ + data: { + name: 'Alice', + likes: { + connect: { + likeId: { + postId: 1, + userId: 2, + }, + }, + }, + }, +}) +``` + +The `likeId` compound ID is used as the identifier in the `connect` object that is used to locate the `Like` table's record that will be linked to the new user: `"Alice"`. + +Similarly, the `likeId` can be used in `connectOrCreate`'s `where` filter to attempt to locate an existing record in the `Like` table: + +```ts highlight=10-13;normal +await prisma.user.create({ + data: { + name: 'Alice', + likes: { + connectOrCreate: { + create: { + postId: 1, + }, + where: { + likeId: { + postId: 1, + userId: 1, + }, + }, + }, + }, + }, +}) +``` diff --git a/docs/200-orm/200-prisma-client/200-special-fields-and-types/index.mdx b/docs/200-orm/200-prisma-client/200-special-fields-and-types/index.mdx new file mode 100644 index 0000000000..4c786346c6 --- /dev/null +++ b/docs/200-orm/200-prisma-client/200-special-fields-and-types/index.mdx @@ -0,0 +1,89 @@ +--- +title: 'Fields & types' +metaTitle: 'Fields & types' +metaDescription: 'Learn how to use about special fields and types with Prisma Client.' +tocDepth: 3 +--- + + + +This section covers various special fields and types you can use with Prisma Client. + + + +## Working with `Decimal` + +`Decimal` fields are represented by the [`Decimal.js` library](https://mikemcl.github.io/decimal.js/). The following example demonstrates how to import and use `Prisma.Decimal`: + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const newTypes = await prisma.sample.create({ + data: { + cost: new Prisma.Decimal(24.454545), + }, +}) +``` + + + +The use of the `Decimal` field [is not currently supported in MongoDB](https://github.com/prisma/prisma/issues/12637). + + + +## Working with `BigInt` + +`BigInt` fields are represented by the [`BigInt` type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) (Node.js 10.4.0+ required). The following example demonstrates how to use the `BigInt` type: + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const newTypes = await prisma.sample.create({ + data: { + revenue: BigInt(534543543534), + }, +}) +``` + +### Serializing BigInt + +Prisma returns records as plain JavaScript objects. If you attempt to use `JSON.stringify` on an object that includes a `BigInt` field, you will see the following error: + +``` +Do not know how to serialize a BigInt +``` + +To work around this issue, use a customized implementation of `JSON.stringify`: + +```js +JSON.stringify( + this, + (key, value) => (typeof value === 'bigint' ? value.toString() : value) // return everything else unchanged +) +``` + +## Working with `Bytes` + +`Bytes` fields are represented by the [`Buffer`](https://nodejs.org/api/buffer.html) type. The following example demonstrates how to use the `Buffer` type: + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const newTypes = await prisma.sample.create({ + data: { + myField: Buffer.from([1, 2, 3, 4]), + }, +}) +``` + +## Working with `Json` + +See: [Working with `Json` fields](working-with-json-fields) + +## Working with scalar lists / scalar arrays + +See: [Working with scalar lists / arrays](working-with-scalar-lists-arrays) + +## Working with composite IDs and compound unique constraints + +See: [Working with composite IDs and compound unique constraints](working-with-composite-ids-and-constraints) diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/100-model.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/100-model.mdx new file mode 100644 index 0000000000..a74d1a62c2 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/100-model.mdx @@ -0,0 +1,164 @@ +--- +title: '`model`: Add custom methods to your models' +metaTitle: 'Prisma Client extensions: model component' +metaDescription: 'Extend the functionality of Prisma Client, model component' +tocDepth: 4 +--- + + + + + +Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the `clientExtensions` Preview feature flag if you are running on a version earlier than 4.16.0. + + + +You can use the `model` [Prisma Client extensions](/orm/prisma-client/client-extensions) component type to add custom methods to your models. + +Possible uses for the `model` component include the following: + +- New operations to operate alongside existing Prisma Client operations, such as `findMany` +- Encapsulated business logic +- Repetitive operations +- Model-specific utilities + + + +## Add a custom method + +Use the `$extends` [client-level method](/orm/reference/prisma-client-reference#client-methods) to create an _extended client_. An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions. Use the `model` extension component to add methods to models in your schema. + +### Add a custom method to a specific model + +To extend a specific model in your schema, use the following structure. This example adds a method to the `user` model. + +```ts +const prisma = new PrismaClient().$extends({ + name?: '', // (optional) names the extension for error logs + model?: { + user: { ... } // in this case, we extend the `user` model + }, +}); +``` + +#### Example + +The following example adds a method called `signUp` to the `user` model. This method creates a new user with the specified email address. + +```ts +const prisma = new PrismaClient().$extends({ + model: { + user: { + async signUp(email: string) { + await prisma.user.create({ data: { email } }) + }, + }, + }, +}) +``` + +You would call `signUp` in your application as follows: + +```ts +const user = await prisma.user.signUp('john@prisma.io') +``` + +When you call a method in an extension, use the constant name from your `$extends` statement, not `prisma`. In the above example, `prisma.user.signUp` works, but `prisma.user.signUp` does not, because the original `prisma` is not modified. + +### Add a custom method to all models in your schema + +To extend _all_ models in your schema, use the following structure: + +```ts +const prisma = new PrismaClient().$extends({ + name?: '', // `name` is an optional field that you can use to name the extension for error logs + model?: { + $allModels: { ... } + }, +}) +``` + +#### Example + +The following example adds an `exists` method to all models. + +```ts +const prisma = new PrismaClient().$extends({ + model: { + $allModels: { + async exists( + this: T, + where: Prisma.Args['where'] + ): Promise { + // Get the current model at runtime + const context = Prisma.getExtensionContext(this) + + const result = await (context as any).findFirst({ where }) + return result !== null + }, + }, + }, +}) +``` + +You would call `exists` in your application as follows: + +```ts +// `exists` method available on all models +await prisma.user.exists({ name: 'Alice' }) +await prisma.post.exists({ + OR: [{ title: { contains: 'Prisma' } }, { content: { contains: 'Prisma' } }], +}) +``` + +## Call a custom method from another custom method + +You can call a custom method from another custom method, if the two methods are declared on the same model. For example, you can call a custom method on the `user` model from another custom method on the `user` model. It does not matter if the two methods are declared in the same extension or in different extensions. + +To do so, use `Prisma.getExtensionContext(this).methodName`. Note that you cannot use `prisma.user.methodName`. This is because `prisma` is not extended yet, and therefore does not contain the new method. + +For example: + +```ts +const prisma = new PrismaClient().$extends({ + model: { + user: { + firstMethod() { + ... + }, + secondMethod() { + Prisma.getExtensionContext(this).firstMethod() + } + } + } +}) +``` + +## Get the current model name at runtime + + + +This feature is available from version 4.9.0. + + + +You can get the name of the current model at runtime with `Prisma.getExtensionContext(this).name`. You might use this to write out the model name to a log, to send the name to another service, or to branch your code based on the model. + +For example: + +```ts +// `context` refers to the current model +const context = Prisma.getExtensionContext(this) + +// `context.name` returns the name of the current model +console.log(context.name) + +// Usage +await(context as any).findFirst({ args }) +``` + +Refer to [Add a custom method to all models in your schema](#example-1) for a concrete example for retrieving the current model name at runtime. + +## Advanced type safety: type utilities for defining generic extensions + +You can improve the type-safety of `model` components in your shared extensions with [type utilities](/orm/prisma-client/client-extensions/type-utilities). diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/110-client.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/110-client.mdx new file mode 100644 index 0000000000..8475af3926 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/110-client.mdx @@ -0,0 +1,67 @@ +--- +title: '`client`: Add methods to Prisma Client' +metaTitle: 'Prisma Client extensions: client component' +metaDescription: 'Extend the functionality of Prisma Client, client component' +tocDepth: 4 +--- + + + + + +Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the `clientExtensions` Preview feature flag if you are running on a version earlier than 4.16.0. + + + +You can use the `client` [Prisma Client extensions](/orm/prisma-client/client-extensions) component to add top-level methods to Prisma Client. + + + +## Extend Prisma Client + +Use the `$extends` [client-level method](/orm/reference/prisma-client-reference#client-methods) to create an _extended client_. An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions. Use the `client` extension component to add top-level methods to Prisma Client. + +To add a top-level method to Prisma Client, use the following structure: + +```ts +const prisma = new PrismaClient().$extends({ + client?: { ... } +}) +``` + +### Example + +The following example uses the `client` component to add two methods to Prisma Client: + +- `$log` outputs a message. +- `$totalQueries` returns the number of queries executed by the current client instance. It uses the [metrics](/orm/prisma-client/observability-and-logging/metrics) feature to collect this information. + + + +To use metrics in your project, you must enable the `metrics` feature flag in the `generator` block of your `schema.prisma` file. [Learn more](/orm/prisma-client/observability-and-logging/metrics#step-2-enable-the-feature-flag-in-the-prisma-schema-file). + + + +```ts +const prisma = new PrismaClient().$extends({ + client: { + $log: (s: string) => console.log(s), + async $totalQueries() { + const index_prisma_client_queries_total = 0 + // Prisma.getExtensionContext(this) in the following block + // returns the current client instance + const metricsCounters = await ( + await Prisma.getExtensionContext(this).$metrics.json() + ).counters + + return metricsCounters[index_prisma_client_queries_total].value + }, + }, +}) + +async function main() { + prisma.$log('Hello world') + const totalQueries = await prisma.$totalQueries() + console.log(totalQueries) +} +``` diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/120-query.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/120-query.mdx new file mode 100644 index 0000000000..68a12b52ce --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/120-query.mdx @@ -0,0 +1,299 @@ +--- +title: '`query`: Create custom Prisma Client queries' +metaTitle: 'Prisma Client extensions: query component' +metaDescription: 'Extend the functionality of Prisma Client, query component' +tocDepth: 4 +--- + + + + + +Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the `clientExtensions` Preview feature flag if you are running on a version earlier than 4.16.0. + + + +You can use the `query` [Prisma Client extensions](/orm/prisma-client/client-extensions) component type to hook into the query life-cycle and modify an incoming query or its result. + +You can use Prisma Client extensions `query` component to create independent clients. This provides an alternative to [middlewares](/orm/prisma-client/client-extensions/middleware). You can bind one client to a specific filter or user, and another client to another filter or user. For example, you might do this to get [user isolation](/orm/prisma-client/client-extensions#extended-clients) in a row-level security (RLS) extension. In addition, unlike middlewares the `query` extension component gives you end-to-end type safety. [Learn more about `query` extensions versus middlewares](#query-extensions-versus-middlewares). + + + +## Extend Prisma Client query operations + +Use the `$extends` [client-level method](/orm/reference/prisma-client-reference#client-methods) to create an [extended client](/orm/prisma-client/client-extensions#about-prisma-client-extensions). An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions. + +Use the `query` extension component to modify queries. You can modify a custom query in the following: + +- [A specific operation in a specific model](#modify-a-specific-operation-in-a-specific-model) +- [A specific operation in all models of your schema](#modify-a-specific-operation-in-all-models-of-your-schema) +- [All Prisma Client operations](#modify-all-prisma-client-operations) +- [All operations in a specific model](#modify-all-operations-in-a-specific-model) +- [All operations in all models of your schema](#modify-all-operations-in-all-models-of-your-schema) +- [A specific top-level raw query operation](#modify-a-top-level-raw-query-operation) + +To create a custom query, use the following structure: + +```ts +const prisma = new PrismaClient().$extends({ + name?: 'name', + query?: { + user: { ... } // in this case, we add a query to the `user` model + }, +}); +``` + +The properties are as follows: + +- `name`: (optional) specifies a name for the extension that appears in error logs. +- `query`: defines a custom query. + +### Modify a specific operation in a specific model + +The `query` object can contain functions that map to the names of the [Prisma Client operations](/orm/reference/prisma-client-reference#model-queries), such as `findUnique`, `findFirst`, `findMany`, `count`, and `create`. The following example modifies `user.findMany` to a use a customized query that finds only users who are older than 18 years: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + user: { + async findMany({ model, operation, args, query }) { + // take incoming `where` and set `age` + args.where = { ...args.where, age: { gt: 18 } } + + return query(args) + }, + }, + }, +}) + +await prisma.user.findMany() // returns users whose age is greater than 18 +``` + +In the above example, a call to `prisma.user.findMany` triggers `query.user.findMany`. Each callback receives a type-safe `{ model, operation, args, query }` object that describes the query. This object has the following properties: + +- `model`: the name of the containing model for the query that we want to extend. + + In the above example, the `model` is a string of type `"User"`. + +- `operation`: the name of the operation being extended and executed. + + In the above example, the `operation` is a string of type `"findMany"`. + +- `args`: the specific query input information to be extended. + + This is a type-safe object that you can mutate before the query happens. You can mutate any of the properties in `args`. Exception: you cannot mutate `include` or `select` because that would change the expected output type and break type safety. + +- `query`: a promise for the result of the query. + + - You can use `await` and then mutate the result of this promise, because its value is type-safe. TypeScript catches any unsafe mutations on the object. + +### Modify a specific operation in all models of your schema + +To extend the queries in all the models of your schema, use `$allModels` instead of a specific model name. For example: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + $allModels: { + async findMany({ model, operation, args, query }) { + // set `take` and fill with the rest of `args` + args = { take: 100, ...args } + + return query(args) + }, + }, + }, +}) +``` + +### Modify all operations in a specific model + +Use `$allOperations` to extend all operations in a specific model. + +For example, the following code applies a custom query to all operations on the `user` model: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + user: { + $allOperations({ model, operation, args, query }) { + /* your custom logic here */ + return query(args) + }, + }, + }, +}) +``` + +### Modify all Prisma Client operations + +Use the `$allOperations` method to modify all query methods present in Prisma Client. The `$allOperations` can be used on both model operations and raw queries. + +You can modify all methods as follows: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + $allOperations({ model, operation, args, query }) { + /* your custom logic for modifying all Prisma Client operations here */ + return query(args) + }, + }, +}) +``` + +In the event a [raw query](/orm/prisma-client/queries/raw-database-access/raw-queries) is invoked, the `model` argument passed to the callback will be `undefined`. + +For example, you can use the `$allOperations` method to log queries as follows: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + async $allOperations({ operation, model, args, query }) { + const start = performance.now() + const result = await query(args) + const end = performance.now() + const time = end - start + console.log( + util.inspect( + { model, operation, args, time }, + { showHidden: false, depth: null, colors: true } + ) + ) + return result + }, + }, +}) +``` + +### Modify all operations in all models of your schema + +Use `$allModels` and `$allOperations` to extend all operations in all models of your schema. + +To apply a custom query to all operations on all models of your schema: + +```ts +const prisma = new PrismaClient().$extends({ + query: { + $allModels: { + $allOperations({ model, operation, args, query }) { + /* your custom logic for modifying all operations on all models here */ + return query(args) + }, + }, + }, +}) +``` + +### Modify a top-level raw query operation + +To apply custom behavior to a specific top-level raw query operation, use the name of a top-level raw query function instead of a model name: + + + + +```ts copy +const prisma = new PrismaClient().$extends({ + query: { + $queryRaw({ args, query, operation }) { + // handle $queryRaw operation + return query(args) + }, + $executeRaw({ args, query, operation }) { + // handle $executeRaw operation + return query(args) + }, + $queryRawUnsafe({ args, query, operation }) { + // handle $queryRawUnsafe operation + return query(args) + }, + $executeRawUnsafe({ args, query, operation }) { + // handle $executeRawUnsafe operation + return query(args) + }, + }, +}) +``` + + + + +```ts copy +const prisma = new PrismaClient().$extends({ + query: { + $runCommandRaw({ args, query, operation }) { + // handle $runCommandRaw operation + return query(args) + }, + }, +}) +``` + + + + +### Mutate the result of a query + +You can use `await` and then mutate the result of the `query` promise. + +```ts +const prisma = new PrismaClient().$extends({ + query: { + user: { + async findFirst({ model, operation, args, query }) { + const user = await query(args) + + if (user.password !== undefined) { + user.password = '******' + } + + return user + }, + }, + }, +}) +``` + + + +We include the above example to show that this is possible. However, for performance reasons we recommend that you use the [`result` component type](/orm/prisma-client/client-extensions/result) to override existing fields. The `result` component type usually gives better performance in this situation because it computes only on access. The `query` component type computes after query execution. + + + +## Wrap a query into a batch transaction + +You can wrap your extended queries into a [batch transaction](/orm/prisma-client/queries/transactions). For example, you can use this to enact row-level security (RLS). + +The following example extends `findFirst` so that it runs in a batch transaction. + +```ts +const prisma = new PrismaClient().$extends({ + query: { + user: { + // Get the input `args` and a callback to `query` + async findFirst({ args, query, operation }) { + const [result] = await prisma.$transaction([query(args)]) // wrap the query in a batch transaction, and destructure the result to return an array + return result // return the first result found in the array + }, + }, + }, +}) +``` + +## Query extensions versus middlewares + +You can use query extensions or [middlewares](/orm/prisma-client/client-extensions/middleware) to hook into the query life-cycle and modify an incoming query or its result. Client extensions and middlewares differ in the following ways: + +- Middlewares always apply globally to the same client. Client extensions are isolated, unless you deliberately combine them. [Learn more about client extensions](/orm/prisma-client/client-extensions#about-prisma-client-extensions). + - For example, in a row-level security (RLS) scenario, you can keep each user in an entirely separate client. With middlewares, all users are active in the same client. +- During application execution, with extensions you can choose from one or more extended clients, or the standard Prisma Client. With middlewares, you cannot choose which client to use, because there is only one global client. +- Extensions benefit from end-to-end type safety and inference, but middlewares don't. + +You can use Prisma Client extensions in all scenarios where middlewares can be used. + +### If you use the `query` extension component and middlewares + +If you use the `query` extension component and middlewares in your project, then the following rules and priorities apply: + +- In your application code, you must declare all your middlewares on the main Prisma Client instance. You cannot declare them on an extended client. +- In situations where middlewares and extensions with a `query` component execute, Prisma Client executes the middlewares before it executes the extensions with the `query` component. Prisma Client executes the individual middlewares and extensions in the order in which you instantiated them with `$use` or `$extends`. diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/130-result.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/130-result.mdx new file mode 100644 index 0000000000..14c7d6e9bc --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/130-result.mdx @@ -0,0 +1,146 @@ +--- +title: '`result`: Add custom fields and methods to query results' +metaTitle: 'Prisma Client extensions: result component' +metaDescription: 'Extend the functionality of Prisma Client, result component' +tocDepth: 4 +--- + + + + + +Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the `clientExtensions` Preview feature flag if you are running on a version earlier than 4.16.0. + + + +You can use the `result` [Prisma Client extensions](/orm/prisma-client/client-extensions) component type to add custom fields and methods to query results. + + + +## Add custom fields or methods to query results + +Use the `$extends` [client-level method](/orm/reference/prisma-client-reference#client-methods) to create an _extended client_. An extended client is a variant of the standard Prisma Client that is wrapped by one or more extensions. + +Use the `result` extension component to add custom fields and methods to query results. + +To add a custom [field](#add-a-custom-field-to-query-results) or [method](#add-a-custom-method-to-the-result-object) to query results, use the following structure. In this example, we add the custom field `myComputedField` to the result of a `user` model query. + +```ts +const prisma = new PrismaClient().$extends({ + name?: 'name', + result?: { + user: { // in this case, we extend the `user` model + myComputedField: { // the name of the new computed field + needs: { ... }, + compute() { ... } + }, + }, + }, +}); +``` + +The parameters are as follows: + +- `name`: (optional) specifies a name for the extension that appears in error logs. +- `result`: defines new fields and methods to the query results. +- `needs`: an object which describes the dependencies of the result field. +- `compute`: a method that defines how the virtual field is computed when it is accessed. + +### Add a custom field to query results + +You can use the `result` extension component to add fields to query results. These fields are computed at runtime and are type-safe. + +In the following example, we add a new virtual field called `fullName` to the `user` model. + +```ts +const prisma = new PrismaClient().$extends({ + result: { + user: { + fullName: { + // the dependencies + needs: { firstName: true, lastName: true }, + compute(user) { + // the computation logic + return `${user.firstName} ${user.lastName}` + }, + }, + }, + }, +}) + +const user = await prisma.user.findFirst() + +// return the user's full name, such as "John Doe" +console.log(user.fullName) +``` + +In above example, the input `user` of `compute` is automatically typed according to the object defined in `needs`. `firstName` and `lastName` are of type `string`, because they are specified in `needs`. If they are not specified in `needs`, then they cannot be accessed. + +### Re-use a computed field in another computed field + +The following example computes a user's title and full name in a type-safe way. `titleFullName` is a computed field that reuses the `fullName` computed field. + +```ts +const prisma = new PrismaClient() + .$extends({ + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute(user) { + return `${user.firstName} ${user.lastName}` + }, + }, + }, + }, + }) + .$extends({ + result: { + user: { + titleFullName: { + needs: { title: true, fullName: true }, + compute(user) { + return `${user.title} (${user.fullName})` + }, + }, + }, + }, + }) +``` + +#### Considerations for fields + +- For performance reasons, Prisma Client computes results on access, not on retrieval. +- You can only create computed fields that are based on scalar fields. +- You can only use computed fields with `select` and you cannot aggregate them. For example: + + ```ts + const user = await prisma.user.findFirst({ + select: { email: true }, + }) + console.log(user.fullName) // undefined + ``` + +### Add a custom method to the result object + +You can use the `result` component to add methods to query results. The following example adds a new method, `save` to the result object. + +```ts +const prisma = new PrismaClient().$extends({ + result: { + user: { + save: { + needs: { id: true }, + compute(user) { + return () => + prisma.user.update({ where: { id: user.id }, data: user }) + }, + }, + }, + }, +}) + +const user = await prisma.user.findUniqueOrThrow({ where: { id: someId } }) +user.email = 'mynewmail@mailservice.com' +await user.save() +``` diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/140-shared-extensions.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/140-shared-extensions.mdx new file mode 100644 index 0000000000..cd27e58d20 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/140-shared-extensions.mdx @@ -0,0 +1,143 @@ +--- +title: 'Shared Prisma Client extensions' +metaTitle: 'Shared Prisma Client extensions' +metaDescription: 'Share extensions or import shared extensions into your Prisma project' +tocDepth: 4 +--- + + + +You can share your [Prisma Client extensions](/orm/prisma-client/client-extensions) with other users, either as packages or as modules, and import extensions that other users create into your project. + +If you would like to build a shareable extension, we also recommend using the [`prisma-client-extension-starter`](https://github.com/prisma/prisma-client-extension-starter) template. + + + +## Install a shared, packaged extension + +In your project, you can install any Prisma Client extension that another user has published to `npm`. To do so, run the following command: + +```terminal +npm install prisma-extension- +``` + +For example, if the package name for an available extension is `prisma-extension-find-or-create`, you could install it as follows: + +```terminal +npm install prisma-extension-find-or-create +``` + +To import the `find-or-create` extension from the example above, and wrap your client instance with it, you could use the following code. This example assumes that the extension name is `findOrCreate`. + +```ts +import findOrCreate from 'prisma-extension-find-or-create' + +const prisma = new PrismaClient().$extends(findOrCreate) +const user = await prisma.user.findOrCreate() +``` + +When you call a method in an extension, use the constant name from your `$extends` statement, not `prisma`. In the above example,`xprisma.user.findOrCreate` works, but `prisma.user.findOrCreate` does not, because the original `prisma` is not modified. + +## Create a shareable extension + +When you want to create extensions other users can use, and that are not tailored just for your schema, Prisma provides utilities to allow you to create shareable extensions. + +To create a shareable extension: + +1. Define the extension as a module using `Prisma.defineExtension` +2. Use one of the methods that begin with the `$all` prefix such as [`$allModels`](/orm/prisma-client/client-extensions/model#add-a-custom-method-to-all-models-in-your-schema) or [`$allOperations`](/orm/prisma-client/client-extensions/query#modify-all-prisma-client-operations) + +### Define an extension + +Use the `Prisma.defineExtension` method to make your extension shareable. You can use it to package the extension to either separate your extensions into a separate file or share it with other users as an npm package. + +The benefit of `Prisma.defineExtension` is that it provides strict type checks and auto completion for authors of extension in development and users of shared extensions. + +### Use a generic method + +Extensions that contain methods under `$allModels` apply to every model instead of a specific one. Similarly, methods under `$allOperations` apply to a client instance as a whole and not to a named component, e.g. `result` or `query`. + +You do not need to use the `$all` prefix with the [`client`](/orm/prisma-client/client-extensions/client) component, because the `client` component always applies to the client instance. + +For example, a generic extension might take the following form: + +```ts +export default Prisma.defineExtension({ + name: 'prisma-extension-find-or-create', //Extension name + model: { + $allModels: { + // new method + findOrCreate(/* args */) { + /* code for the new method */ + return query(args) + }, + }, + }, +}) +``` + +Refer to the following pages to learn the different ways you can modify Prisma Client operations: + +- [Modify all Prisma Client operations](/orm/prisma-client/client-extensions/query#modify-all-prisma-client-operations) +- [Modify a specific operation in all models of your schema](/orm/prisma-client/client-extensions/query#modify-a-specific-operation-in-all-models-of-your-schema) +- [Modify all operations in all models of your schema](/orm/prisma-client/client-extensions/query#modify-all-operations-in-all-models-of-your-schema) + +
+ For versions earlier than 4.16.0 + +The `Prisma` import is available from a different path shown in the snippet below: + +```ts +import { Prisma } from '@prisma/client/scripts/default-index' + +export default Prisma.defineExtension({ + name: 'prisma-extension-', +}) +``` + +
+ +### Publishing the shareable extension to npm + +You can then share the extension on `npm`. When you choose a package name, we recommend that you use the `prisma-extension-` convention, to make it easier to find and install. + +### Call a client-level method from your packaged extension + +In the following situations, you need to refer to a Prisma Client instance that your extension wraps: + +- When you want to use a [client-level method](/orm/reference/prisma-client-reference#client-methods), such as `$queryRaw`, in your packaged extension. +- When you want to chain multiple `$extends` calls in your packaged extension. + +However, when someone includes your packaged extension in their project, your code cannot know the details of the Prisma Client instance. + +You can refer to this client instance as follows: + +```ts +Prisma.defineExtension((client) => { + // The Prisma Client instance that the extension user applies the extension to + return client.$extends({ + name: 'prisma-extension-', + }) +}) +``` + +For example: + +```ts +export default Prisma.defineExtension((client) => { + return client.$extends({ + name: 'prisma-extension-find-or-create', + query: { + $allModels: { + async findOrCreate({ args, query, operation }) { + return (await client.$transaction([query(args)]))[0] + }, + }, + }, + }) +}) +``` + +### Advanced type safety: type utilities for defining generic extensions + +You can improve the type-safety of your shared extensions using [type utilities](/orm/prisma-client/client-extensions/type-utilities). diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/150-type-utilities.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/150-type-utilities.mdx new file mode 100644 index 0000000000..dbc0c53cef --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/150-type-utilities.mdx @@ -0,0 +1,101 @@ +--- +title: 'Type utilities' +metaTitle: 'Prisma Client Extensions: Type utilities' +metaDescription: 'Advanced type safety: improve type safety in your custom model methods' +--- + + + +Several type utilities exist within Prisma Client that can assist in the creation of highly type-safe extensions. + + + +## Type Utilities + +[Prisma Client type utilities](/orm/prisma-client/type-safety) are utilities available within your application and Prisma Client extensions and provide useful ways of constructing safe and extendable types for your extension. + +The type utilities available are: + +- `Exact`: Enforces strict type safety on `Input`. `Exact` makes sure that a generic type `Input` strictly complies with the type that you specify in `Shape`. It [narrows](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) `Input` down to the most precise types. +- `Args`: Retrieves the input arguments for any given model and operation. This is particularly useful for extension authors who want to do the following: + - Re-use existing types to extend or modify them. + - Benefit from the same auto-completion experience as on existing operations. +- `Result`: Takes the input arguments and provides the result for a given model and operation. You would usually use this in conjunction with `Args`. As with `Args`, `Result` helps you to re-use existing types to extend or modify them. +- `Payload`: Retrieves the entire structure of the result, as scalars and relations objects for a given model and operation. For example, you can use this to determine which keys are scalars or objects at a type level. + +The following example creates a new operation, `exists`, based on `findFirst`. It has all of the arguments that `findFirst`. + +```ts +const prisma = new PrismaClient().$extends({ + model: { + $allModels: { + // Define a new `exists` operation on all models + // T is a generic type that corresponds to the current model + async exists( + // `this` refers to the current type, e.g. `prisma.user` at runtime + this: T, + + // The `exists` function will use the `where` arguments from the current model, `T`, and the `findFirst` operation + where: Prisma.Args['where'] + ): Promise { + // Retrieve the current model at runtime + const context = Prisma.getExtensionContext(this) + + // Prisma Client query that retrieves data based + const result = await (context as any).findFirst({ where }) + return result !== null + }, + }, + }, +}) + +async function main() { + const user = await prisma.user.exists({ name: 'Alice' }) + const post = await prisma.post.exists({ + OR: [ + { title: { contains: 'Prisma' } }, + { content: { contains: 'Prisma' } }, + ], + }) +} +``` + +## Add a custom property to a method + +The following example illustrates how you can add custom arguments, to a method in an extension: + +```ts highlight=16 +type CacheStrategy = { + swr: number + ttl: number +} + +const prisma = new PrismaClient().$extends({ + model: { + $allModels: { + findMany( + this: T, + args: Prisma.Exact< + A, + // For the `findMany` method, use the arguments from model `T` and the `findMany` method + // and intersect it with `CacheStrategy` as part of `findMany` arguments + Prisma.Args & CacheStrategy + > + ): Prisma.Result { + // method implementation with the cache strategy + }, + }, + }, +}) + +async function main() { + await prisma.post.findMany({ + cacheStrategy: { + ttl: 360, + swr: 60, + }, + }) +} +``` + +The example here is only conceptual. For the actual caching to work, you will have to implement the logic. If you're interested in a caching extension/ service, we recommend taking a look at [Prisma Accelerate](https://www.prisma.io/data-platform/accelerate). diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/200-extension-examples.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/200-extension-examples.mdx new file mode 100644 index 0000000000..2312713f0b --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/200-extension-examples.mdx @@ -0,0 +1,64 @@ +--- +title: 'Shared packages & examples' +metaTitle: 'Prisma Client extensions | Shared packages & examples' +metaDescription: 'Explore the Prisma Client extensions that have been built by Prisma and its community' +--- + +## Extensions made by Prisma + +The following is a list of extensions we've built at Prisma: + +| Extension | Description | +| :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`@prisma/extension-accelerate`](https://www.npmjs.com/package/@prisma/extension-accelerate) | Enables [Accelerate](https://www.prisma.io/accelerate), a global database cache available in 300+ locations with built-in connection pooling | +| [`@prisma/extension-pulse`](https://npmjs.com/package/@prisma/extension-pulse) | Enables [Pulse](https://www.prisma.io/pulse), a service that captures change events from your database and delivers them instantly to your applications. | +| [`@prisma/extension-read-replicas`](https://github.com/prisma/extension-read-replicas) | Adds read replica support to Prisma Client | + +## Extensions made by Prisma's community + +The following is a list of extensions created by the community. If you want to create your own package, refer to the [Shared Prisma Client extensions](/orm/prisma-client/client-extensions/shared-extensions) documentation. + +| Extension | Description | +| :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- | +| [`prisma-extension-supabase-rls`](https://github.com/dthyresson/prisma-extension-supabase-rls) | Adds support for Supabase Row Level Security with Prisma | +| [`prisma-extension-bark`](https://github.com/adamjkb/bark) | Implements the Materialized Path pattern that allows you to easily create and interact with tree structures in Prisma | +| [`prisma-cursorstream`](https://github.com/etabits/prisma-cursorstream) | Adds cursor-based streaming | +| [`prisma-gpt`](https://github.com/aliyeysides/prisma-gpt) | Lets you query your database using natural language | +| [`prisma-extension-caching`](https://github.com/isaev-the-poetry/prisma-extension-caching) | Transforms SQL data from queries in streams to improve performance in larger datasets | +| [`prisma-extension-cache-manager`](https://github.com/random42/prisma-extension-cache-manager) | Caches model queries with any [cache-manager](https://www.npmjs.com/package/cache-manager) compatible cache | +| [`prisma-extension-random`](https://github.com/nkeil/prisma-extension-random) | Lets you query for random rows in your database | +| [`prisma-paginate`](https://github.com/sandrewTx08/prisma-paginate) | Adds support for paginating read queries | + +If you have built an extension and would like to see it featured, feel free to add it to the list by opening a pull request. + +## Examples + + + +The following example extensions are provided as examples only, and without warranty. They are supposed to show how Prisma Client extensions can be created using approaches documented here. We recommend using these examples as a source of inspiration for building your own extensions. + + + +| Example | Description | +| :------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | +| [`audit-log-context`](https://github.com/prisma/prisma-client-extensions/tree/main/audit-log-context) | Provides the current user's ID as context to Postgres audit log triggers | +| [`callback-free-itx`](https://github.com/prisma/prisma-client-extensions/tree/main/callback-free-itx) | Adds a method to start interactive transactions without callbacks | +| [`computed-fields`](https://github.com/prisma/prisma-client-extensions/tree/main/computed-fields) | Adds virtual / computed fields to result objects | +| [`input-transformation`](https://github.com/prisma/prisma-client-extensions/tree/main/input-transformation) | Transforms the input arguments passed to Prisma Client queries to filter the result set | +| [`input-validation`](https://github.com/prisma/prisma-client-extensions/tree/main/input-validation) | Runs custom validation logic on input arguments passed to mutation methods | +| [`instance-methods`](https://github.com/prisma/prisma-client-extensions/tree/main/instance-methods) | Adds Active Record-like methods like `save()` and `delete()` to result objects | +| [`json-field-types`](https://github.com/prisma/prisma-client-extensions/tree/main/json-field-types) | Uses strongly-typed runtime parsing for data stored in JSON columns | +| [`model-filters`](https://github.com/prisma/prisma-client-extensions/tree/main/model-filters) | Adds reusable filters that can composed into complex `where` conditions for a model | +| [`obfuscated-fields`](https://github.com/prisma/prisma-client-extensions/tree/main/obfuscated-fields) | Prevents sensitive data (e.g. `password` fields) from being included in results | +| [`query-logging`](https://github.com/prisma/prisma-client-extensions/tree/main/query-logging) | Wraps Prisma Client queries with simple query timing and logging | +| [`readonly-client`](https://github.com/prisma/prisma-client-extensions/tree/main/readonly-client) | Creates a client that only allows read operations | +| [`retry-transactions`](https://github.com/prisma/prisma-client-extensions/tree/main/retry-transactions) | Adds a retry mechanism to transactions with exponential backoff and jitter | +| [`row-level-security`](https://github.com/prisma/prisma-client-extensions/tree/main/row-level-security) | Uses Postgres row-level security policies to isolate data a multi-tenant application | +| [`static-methods`](https://github.com/prisma/prisma-client-extensions/tree/main/static-methods) | Adds custom query methods to Prisma Client models | +| [`transformed-fields`](https://github.com/prisma/prisma-client-extensions/tree/main/transformed-fields) | Demonstrates how to use result extensions to transform query results and add i18n to an app | +| [`exists-method`](https://github.com/prisma/prisma-client-extensions/tree/main/exists-fn) | Demonstrates how to add an `exists` method to all your models | +| [`update-delete-ignore-not-found `](https://github.com/prisma/prisma-client-extensions/tree/main/update-delete-ignore-not-found) | Demonstrates how to add the `updateIgnoreOnNotFound` and `deleteIgnoreOnNotFound` methods to all your models. | + +## Going further + +- Learn more about [Prisma Client extensions](/orm/prisma-client/client-extensions). diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx new file mode 100644 index 0000000000..583441ea93 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx @@ -0,0 +1,669 @@ +--- +title: 'Middleware sample: soft delete' +metaTitle: 'Middleware sample: soft delete (Reference)' +metaDescription: 'How to use middleware to intercept deletes and set a field value instead of deleting the record.' +tocDepth: 4 +--- + + + +The following sample uses [middleware](/orm/prisma-client/client-extensions/middleware) to perform a **soft delete**. Soft delete means that a record is **marked as deleted** by changing a field like `deleted` to `true` rather than actually being removed from the database. Reasons to use a soft delete include: + +- Regulatory requirements that mean you have to keep data for a certain amount of time +- 'Trash' / 'bin' functionality that allows users to restore content that was deleted + + + +**Note:** This page demonstrates a sample use of middleware. We do not intend the sample to be a fully functional soft delete feature. + + + +This sample uses the following schema - note the `deleted` field on the `Post` model: + +```prisma highlight=28;normal +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] + followers User[] @relation("UserToUser") + user User? @relation("UserToUser", fields: [userId], references: [id]) + userId Int? +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + user User? @relation(fields: [userId], references: [id]) + userId Int? + tags Tag[] + views Int @default(0) + deleted Boolean @default(false) +} + +model Category { + id Int @id @default(autoincrement()) + parentCategory Category? @relation("CategoryToCategory", fields: [categoryId], references: [id]) + category Category[] @relation("CategoryToCategory") + categoryId Int? +} + +model Tag { + tagName String @id // Must be unique + posts Post[] +} +``` + + + +## Step 1: Store status of record + +Add a field named `deleted` to the `Post` model. You can choose between two field types depending on your requirements: + +- `Boolean` with a default value of `false`: + + ```prisma highlight=4;normal + model Post { + id Int @id @default(autoincrement()) + ... + deleted Boolean @default(false) + } + ``` + +- Create a nullable `DateTime` field so that you know exactly _when_ a record was marked as deleted - `NULL` indicates that a record has not been deleted. In some cases, storing when a record was removed may be a regulatory requirement: + + ```prisma highlight=4;normal + model Post { + id Int @id @default(autoincrement()) + ... + deleted DateTime? + } + ``` + +> **Note**: Using two separate fields (`isDeleted` and `deletedDate`) may result in these two fields becoming out of sync - for example, a record may be marked as deleted but have no associated date.) + +This sample uses a `Boolean` field type for simplicity. + +## Step 2: Soft delete middleware + +Add a middleware that performs the following tasks: + +- Intercepts `delete` and `deleteMany` queries for the `Post` model +- Changes the `params.action` to `update` and `updateMany` respectively +- Introduces a `data` argument and sets `{ deleted: true }`, preserving other filter arguments if they exist + +Run the following sample to test the soft delete middleware: + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({}) + +async function main() { + /***********************************/ + /* SOFT DELETE MIDDLEWARE */ + /***********************************/ + + prisma.$use(async (params, next) => { + // Check incoming query type + if (params.model == 'Post') { + if (params.action == 'delete') { + // Delete queries + // Change action to an update + params.action = 'update' + params.args['data'] = { deleted: true } + } + if (params.action == 'deleteMany') { + // Delete many queries + params.action = 'updateMany' + if (params.args.data != undefined) { + params.args.data['deleted'] = true + } else { + params.args['data'] = { deleted: true } + } + } + } + return next(params) + }) + + /***********************************/ + /* TEST */ + /***********************************/ + + const titles = [ + { title: 'How to create soft delete middleware' }, + { title: 'How to install Prisma' }, + { title: 'How to update a record' }, + ] + + console.log('\u001b[1;34mSTARTING SOFT DELETE TEST \u001b[0m') + console.log('\u001b[1;34m#################################### \u001b[0m') + + let i = 0 + let posts = new Array() + + // Create 3 new posts with a randomly assigned title each time + for (i == 0; i < 3; i++) { + const createPostOperation = prisma.post.create({ + data: titles[Math.floor(Math.random() * titles.length)], + }) + posts.push(createPostOperation) + } + + var postsCreated = await prisma.$transaction(posts) + + console.log( + 'Posts created with IDs: ' + + '\u001b[1;32m' + + postsCreated.map((x) => x.id) + + '\u001b[0m' + ) + + // Delete the first post from the array + const deletePost = await prisma.post.delete({ + where: { + id: postsCreated[0].id, // Random ID + }, + }) + + // Delete the 2nd two posts + const deleteManyPosts = await prisma.post.deleteMany({ + where: { + id: { + in: [postsCreated[1].id, postsCreated[2].id], + }, + }, + }) + + const getPosts = await prisma.post.findMany({ + where: { + id: { + in: postsCreated.map((x) => x.id), + }, + }, + }) + + console.log() + + console.log( + 'Deleted post with ID: ' + '\u001b[1;32m' + deletePost.id + '\u001b[0m' + ) + console.log( + 'Deleted posts with IDs: ' + + '\u001b[1;32m' + + [postsCreated[1].id + ',' + postsCreated[2].id] + + '\u001b[0m' + ) + console.log() + console.log( + 'Are the posts still available?: ' + + (getPosts.length == 3 + ? '\u001b[1;32m' + 'Yes!' + '\u001b[0m' + : '\u001b[1;31m' + 'No!' + '\u001b[0m') + ) + console.log() + console.log('\u001b[1;34m#################################### \u001b[0m') + // 4. Count ALL posts + const f = await prisma.post.findMany({}) + console.log('Number of posts: ' + '\u001b[1;32m' + f.length + '\u001b[0m') + + // 5. Count DELETED posts + const r = await prisma.post.findMany({ + where: { + deleted: true, + }, + }) + console.log( + 'Number of SOFT deleted posts: ' + '\u001b[1;32m' + r.length + '\u001b[0m' + ) +} + +main() +``` + +The sample outputs the following: + +```no-lines +STARTING SOFT DELETE TEST +#################################### +Posts created with IDs: 587,588,589 + +Deleted post with ID: 587 +Deleted posts with IDs: 588,589 + +Are the posts still available?: Yes! + +#################################### +``` + +:::tip + +Comment out the middleware to see the message change. + +::: + +✔ Pros of this approach to soft delete include: + +- Soft delete happens at data access level, which means that you cannot delete records unless you use raw SQL + +✘ Cons of this approach to soft delete include: + +- Content can still be read and updated unless you explicitly filter by `where: { deleted: false }` - in a large project with a lot of queries, there is a risk that soft deleted content will still be displayed +- You can still use raw SQL to delete records + +:::tip + +You can create rules or triggers ([MySQL](https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html) and [PostgreSQL](https://www.postgresql.org/docs/8.1/rules-update.html)) at a database level to prevent records from being deleted. + +::: + +## Step 3: Optionally prevent read/update of soft deleted records + +In step 2, we implemented middleware that prevents `Post` records from being deleted. However, you can still read and update deleted records. This step explores two ways to prevent the reading and updating of deleted records. + +> **Note**: These options are just ideas with pros and cons, you may choose to do something entirely different. + +### Option 1: Implement filters in your own application code + +In this option: + +- Prisma middleware is responsible for preventing records from being deleted +- Your own application code (which could be a GraphQL API, a REST API, a module) is responsible for filtering out deleted posts where necessary (`{ where: { deleted: false } }`) when reading and updating data - for example, the `getPost` GraphQL resolver never returns a deleted post + +✔ Pros of this approach to soft delete include: + +- No change to Prisma's create/update queries - you can easily request deleted records if you need them +- Modifying queries in middleware can have some unintended consequences, such as changing query return types (see option 2) + +✘ Cons of this approach to soft delete include: + +- Logic relating to soft delete maintained in two different places +- If your API surface is very large and maintained by multiple contributors, it may be difficult to enforce certain business rules (for example, never allow deleted records to be updated) + +### Option 2: Use middleware to determine the behavior of read/update queries for deleted records + +Option two uses Prisma middleware to prevent soft deleted records from being returned. The following table describes how the middleware affects each query: + +| **Query** | **Middleware logic** | **Changes to return type** | +| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------- | --- | +| `findUnique` | 🔧 Change query to `findFirst` (because you cannot apply `deleted: false` filters to `findUnique`)
🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts
🔧 From version 5.0.0, you can use `findUnique` to apply `delete: false` filters since [non unique fields are exposed](/orm/reference/prisma-client-reference#filter-on-non-unique-fields-with-userwhereuniqueinput). | No change | | +| `findMany` | 🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts by default
🔧 Allow developers to **explicitly request** soft deleted posts by specifying `deleted: true` | No change | +| `update` | 🔧 Change query to `updateMany` (because you cannot apply `deleted: false` filters to `update`)
🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts | `{ count: n }` instead of `Post` | +| `updateMany` | 🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts | No change | + +- **Is it not possible to utilize soft delete with `findFirstOrThrow` or `findUniqueOrThrow`?**
+ From version [5.1.0](https://github.com/prisma/prisma/releases/5.1.0), you can apply soft delete `findFirstOrThrow` or `findUniqueOrThrow` by using middleware. +- **Why are you making it possible to use `findMany` with a `{ where: { deleted: true } }` filter, but not `updateMany`?**
+ This particular sample was written to support the scenario where a user can _restore_ their deleted blog post (which requires a list of soft deleted posts) - but the user should not be able to edit a deleted post. +- **Can I still `connect` or `connectOrCreate` a deleted post?**
+ In this sample - yes. The middleware does not prevent you from connecting an existing, soft deleted post to a user. + +Run the following sample to see how middleware affects each query: + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const prisma = new PrismaClient({}) + +async function main() { + /***********************************/ + /* SOFT DELETE MIDDLEWARE */ + /***********************************/ + + prisma.$use(async (params, next) => { + if (params.model == 'Post') { + if (params.action === 'findUnique' || params.action === 'findFirst') { + // Change to findFirst - you cannot filter + // by anything except ID / unique with findUnique + params.action = 'findFirst' + // Add 'deleted' filter + // ID filter maintained + params.args.where['deleted'] = false + } + if ( + params.action === 'findFirstOrThrow' || + params.action === 'findUniqueOrThrow' + ) { + if (params.args.where) { + if (params.args.where.deleted == undefined) { + // Exclude deleted records if they have not been explicitly requested + params.args.where['deleted'] = false + } + } else { + params.args['where'] = { deleted: false } + } + } + if (params.action === 'findMany') { + // Find many queries + if (params.args.where) { + if (params.args.where.deleted == undefined) { + params.args.where['deleted'] = false + } + } else { + params.args['where'] = { deleted: false } + } + } + } + return next(params) + }) + + prisma.$use(async (params, next) => { + if (params.model == 'Post') { + if (params.action == 'update') { + // Change to updateMany - you cannot filter + // by anything except ID / unique with findUnique + params.action = 'updateMany' + // Add 'deleted' filter + // ID filter maintained + params.args.where['deleted'] = false + } + if (params.action == 'updateMany') { + if (params.args.where != undefined) { + params.args.where['deleted'] = false + } else { + params.args['where'] = { deleted: false } + } + } + } + return next(params) + }) + + prisma.$use(async (params, next) => { + // Check incoming query type + if (params.model == 'Post') { + if (params.action == 'delete') { + // Delete queries + // Change action to an update + params.action = 'update' + params.args['data'] = { deleted: true } + } + if (params.action == 'deleteMany') { + // Delete many queries + params.action = 'updateMany' + if (params.args.data != undefined) { + params.args.data['deleted'] = true + } else { + params.args['data'] = { deleted: true } + } + } + } + return next(params) + }) + + /***********************************/ + /* TEST */ + /***********************************/ + + const titles = [ + { title: 'How to create soft delete middleware' }, + { title: 'How to install Prisma' }, + { title: 'How to update a record' }, + ] + + console.log('\u001b[1;34mSTARTING SOFT DELETE TEST \u001b[0m') + console.log('\u001b[1;34m#################################### \u001b[0m') + + let i = 0 + let posts = new Array() + + // Create 3 new posts with a randomly assigned title each time + for (i == 0; i < 3; i++) { + const createPostOperation = prisma.post.create({ + data: titles[Math.floor(Math.random() * titles.length)], + }) + posts.push(createPostOperation) + } + + var postsCreated = await prisma.$transaction(posts) + + console.log( + 'Posts created with IDs: ' + + '\u001b[1;32m' + + postsCreated.map((x) => x.id) + + '\u001b[0m' + ) + + // Delete the first post from the array + const deletePost = await prisma.post.delete({ + where: { + id: postsCreated[0].id, // Random ID + }, + }) + + // Delete the 2nd two posts + const deleteManyPosts = await prisma.post.deleteMany({ + where: { + id: { + in: [postsCreated[1].id, postsCreated[2].id], + }, + }, + }) + + const getOnePost = await prisma.post.findUnique({ + where: { + id: postsCreated[0].id, + }, + }) + + const getOneUniquePostOrThrow = async () => + await prisma.post.findUniqueOrThrow({ + where: { + id: postsCreated[0].id, + }, + }) + + const getOneFirstPostOrThrow = async () => + await prisma.post.findFirstOrThrow({ + where: { + id: postsCreated[0].id, + }, + }) + + const getPosts = await prisma.post.findMany({ + where: { + id: { + in: postsCreated.map((x) => x.id), + }, + }, + }) + + const getPostsAnDeletedPosts = await prisma.post.findMany({ + where: { + id: { + in: postsCreated.map((x) => x.id), + }, + deleted: true, + }, + }) + + const updatePost = await prisma.post.update({ + where: { + id: postsCreated[1].id, + }, + data: { + title: 'This is an updated title (update)', + }, + }) + + const updateManyDeletedPosts = await prisma.post.updateMany({ + where: { + deleted: true, + id: { + in: postsCreated.map((x) => x.id), + }, + }, + data: { + title: 'This is an updated title (updateMany)', + }, + }) + + console.log() + + console.log( + 'Deleted post (delete) with ID: ' + + '\u001b[1;32m' + + deletePost.id + + '\u001b[0m' + ) + console.log( + 'Deleted posts (deleteMany) with IDs: ' + + '\u001b[1;32m' + + [postsCreated[1].id + ',' + postsCreated[2].id] + + '\u001b[0m' + ) + console.log() + console.log( + 'findUnique: ' + + (getOnePost?.id != undefined + ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' + : '\u001b[1;31m' + + 'Post not returned!' + + '(Value is: ' + + JSON.stringify(getOnePost) + + ')' + + '\u001b[0m') + ) + try { + console.log('findUniqueOrThrow: ') + await getOneUniquePostOrThrow() + } catch (error) { + if ( + error instanceof Prisma.PrismaClientKnownRequestError && + error.code == 'P2025' + ) + console.log( + '\u001b[1;31m' + + 'PrismaClientKnownRequestError is catched' + + '(Error name: ' + + error.name + + ')' + + '\u001b[0m' + ) + } + try { + console.log('findFirstOrThrow: ') + await getOneFirstPostOrThrow() + } catch (error) { + if ( + error instanceof Prisma.PrismaClientKnownRequestError && + error.code == 'P2025' + ) + console.log( + '\u001b[1;31m' + + 'PrismaClientKnownRequestError is catched' + + '(Error name: ' + + error.name + + ')' + + '\u001b[0m' + ) + } + console.log() + console.log( + 'findMany: ' + + (getPosts.length == 3 + ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' + : '\u001b[1;31m' + 'Posts not returned!' + '\u001b[0m') + ) + console.log( + 'findMany ( delete: true ): ' + + (getPostsAnDeletedPosts.length == 3 + ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' + : '\u001b[1;31m' + 'Posts not returned!' + '\u001b[0m') + ) + console.log() + console.log( + 'update: ' + + (updatePost.id != undefined + ? '\u001b[1;32m' + 'Post updated!' + '\u001b[0m' + : '\u001b[1;31m' + + 'Post not updated!' + + '(Value is: ' + + JSON.stringify(updatePost) + + ')' + + '\u001b[0m') + ) + console.log( + 'updateMany ( delete: true ): ' + + (updateManyDeletedPosts.count == 3 + ? '\u001b[1;32m' + 'Posts updated!' + '\u001b[0m' + : '\u001b[1;31m' + 'Posts not updated!' + '\u001b[0m') + ) + console.log() + console.log('\u001b[1;34m#################################### \u001b[0m') + // 4. Count ALL posts + const f = await prisma.post.findMany({}) + console.log( + 'Number of active posts: ' + '\u001b[1;32m' + f.length + '\u001b[0m' + ) + + // 5. Count DELETED posts + const r = await prisma.post.findMany({ + where: { + deleted: true, + }, + }) + console.log( + 'Number of SOFT deleted posts: ' + '\u001b[1;32m' + r.length + '\u001b[0m' + ) +} + +main() +``` + +The sample outputs the following: + +``` +STARTING SOFT DELETE TEST +#################################### +Posts created with IDs: 680,681,682 + +Deleted post (delete) with ID: 680 +Deleted posts (deleteMany) with IDs: 681,682 + +findUnique: Post not returned!(Value is: []) +findMany: Posts not returned! +findMany ( delete: true ): Posts returned! + +update: Post not updated!(Value is: {"count":0}) +updateMany ( delete: true ): Posts not updated! + +#################################### +Number of active posts: 0 +Number of SOFT deleted posts: 95 +``` + +✔ Pros of this approach: + +- A developer can make a conscious choice to include deleted records in `findMany` +- You cannot accidentally read or update a deleted record + +✖ Cons of this approach: + +- Not obvious from API that you aren't getting all records and that `{ where: { deleted: false } }` is part of the default query +- Return type `update` affected because middleware changes the query to `updateMany` +- Doesn't handle complex queries with `AND`, `OR`, `every`, etc... +- Doesn't handle filtering when using `include` from another model. + +## FAQ + +### Can I add a global `includeDeleted` to the `Post` model? + +You may be tempted to 'hack' your API by adding a `includeDeleted` property to the `Post` model and make the following query possible: + +```ts +prisma.post.findMany({ where: { includeDeleted: true } }) +``` + +> **Note**: You would still need to write middleware. + +We **✘ do not** recommend this approach as it pollutes the schema with fields that do not represent real data. diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx new file mode 100644 index 0000000000..fbf2da4f85 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx @@ -0,0 +1,90 @@ +--- +title: 'Middleware sample: logging' +metaTitle: 'Middleware sample: logging (Reference)' +metaDescription: 'How to use middleware to log the time taken to perform any query.' +--- + + + +The following example logs the time taken for a Prisma Query to run: + +```ts +const prisma = new PrismaClient() + +prisma.$use(async (params, next) => { + const before = Date.now() + + const result = await next(params) + + const after = Date.now() + + console.log(`Query ${params.model}.${params.action} took ${after - before}ms`) + + return result +}) + +const create = await prisma.post.create({ + data: { + title: 'Welcome to Prisma Day 2020', + }, +}) + +const createAgain = await prisma.post.create({ + data: { + title: 'All about database collation', + }, +}) +``` + +Example output: + +```no-lines +Query Post.create took 92ms +Query Post.create took 15ms +``` + +The example is based on the following sample schema: + +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model Post { + authorId Int? + content String? + id Int @id @default(autoincrement()) + published Boolean @default(false) + title String + user User? @relation(fields: [authorId], references: [id]) + language String? + + @@index([authorId], name: "authorId") +} + +model User { + email String @unique + id Int @id @default(autoincrement()) + name String? + posts Post[] + extendedProfile Json? + role Role @default(USER) +} + +enum Role { + ADMIN + USER + MODERATOR +} +``` + + + +## Going further + +You can also use [Prisma Client extensions](/orm/prisma-client/client-extensions) to log the time it takes to perform a query. A functional example can be found in [this GitHub repository](https://github.com/prisma/prisma-client-extensions/tree/main/query-logging). diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx new file mode 100644 index 0000000000..bd70ecdb2e --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx @@ -0,0 +1,71 @@ +--- +title: 'Middleware sample: session data' +metaTitle: 'Middleware sample: session data (Reference)' +metaDescription: 'How to use middleware to set the value taken from session state.' +--- + + + +The following example sets the `language` field of each `Post` to the context language (taken, for example, from session state): + +```ts +const prisma = new PrismaClient() + +const contextLanguage = 'en-us' // Session state + +prisma.$use(async (params, next) => { + if (params.model == 'Post' && params.action == 'create') { + params.args.data.language = contextLanguage + } + + return next(params) +}) + +const create = await prisma.post.create({ + data: { + title: 'My post in English', + }, +}) +``` + +The example is based on the following sample schema: + +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model Post { + authorId Int? + content String? + id Int @id @default(autoincrement()) + published Boolean @default(false) + title String + user User? @relation(fields: [authorId], references: [id]) + language String? + + @@index([authorId], name: "authorId") +} + +model User { + email String @unique + id Int @id @default(autoincrement()) + name String? + posts Post[] + extendedProfile Json? + role Role @default(USER) +} + +enum Role { + ADMIN + USER + MODERATOR +} +``` + + diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx new file mode 100644 index 0000000000..002d7b531e --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx @@ -0,0 +1,187 @@ +--- +title: 'Middleware' +metaTitle: 'Middleware (Reference)' +metaDescription: 'Prisma Client middleware allows you to perform actions before or after any query on any model with the prisma.$use method.' +--- + + + + + +**Deprecated**: Middleware is deprecated in version 4.16.0. + +We recommend using the [Prisma Client extensions `query` component type](/orm/prisma-client/client-extensions/query) as an alternative to middleware. Prisma Client extensions were first introduced into Preview in version 4.7.0 and made Generally Available in 4.16.0. + +Prisma Client extensions allow you to create independent Prisma Client instances and bind each client to a specific filter or user. For example, you could bind clients to specific users to provide user isolation. Prisma Client extensions also provide end-to-end type safety. + + + +Middlewares act as query-level lifecycle hooks, which allow you to perform an action before or after a query runs. Use the [`prisma.$use`](/orm/reference/prisma-client-reference#use) method to add middleware, as follows: + +```ts highlight=4-9,12-17;normal +const prisma = new PrismaClient() + +// Middleware 1 +prisma.$use(async (params, next) => { + // Manipulate params here + const result = await next(params) + // See results here + return result +}) + +// Middleware 2 +prisma.$use(async (params, next) => { + // Manipulate params here + const result = await next(params) + // See results here + return result +}) + +// Queries here +``` + + + +Do not invoke `next` multiple times within a middleware when using [batch transactions](/orm/prisma-client/queries/transactions#sequential-prisma-client-operations). This will cause you to break out of the transaction and lead to unexpected results. + + + +[`params`](/orm/reference/prisma-client-reference#params) represent parameters available in the middleware, such as the name of the query, and [`next`](/orm/reference/prisma-client-reference#next) represents [the next middleware in the stack _or_ the original Prisma Client query](#running-order-and-the-middleware-stack). + +Possible use cases for middleware include: + +- Setting or overwriting a field value - for example, [setting the context language of a blog post comment](session-data-middleware) +- Validating input data - for example, check user input for inappropriate language via an external service +- Intercept a `delete` query and change it to an `update` in order to perform a [soft delete](soft-delete-middleware) +- [Log the time taken to perform a query](logging-middleware) + +There are many more use cases for middleware - this list serves as inspiration for the types of problems that middleware is designed to address. + + + +## Samples + +The following sample scenarios show how to use middleware in practice: + + + +## Where to add middleware + +Add Prisma middleware **outside the context of the request handler**, otherwise each request adds a new _instance_ of the middleware to the stack. The following example demonstrates where to add Prisma middleware in the context of an Express app: + +```ts highlight=6-11;normal +import express from 'express' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +prisma.$use(async (params, next) => { + // Manipulate params here + const result = await next(params) + // See results here + return result +}) + +const app = express() +app.get('/feed', async (req, res) => { + // NO MIDDLEWARE HERE + const posts = await prisma.post.findMany({ + where: { published: true }, + include: { author: true }, + }) + res.json(posts) +}) +``` + +## Running order and the middleware stack + +If you have multiple middlewares, the running order for **each separate query** is: + +1. All logic **before** `await next(params)` in each middleware, in descending order +2. All logic **after** `await next(params)` in each middleware, in ascending order + +Depending on where you are in the stack, `await next(params)` either: + +- Runs the next middleware (in middlewares #1 and #2 in the example) _or_ +- Runs the original Prisma Client query (in middleware #3) + +```ts +const prisma = new PrismaClient() + +// Middleware 1 +prisma.$use(async (params, next) => { + console.log(params.args.data.title) + console.log('1') + const result = await next(params) + console.log('6') + return result +}) + +// Middleware 2 +prisma.$use(async (params, next) => { + console.log('2') + const result = await next(params) + console.log('5') + return result +}) + +// Middleware 3 +prisma.$use(async (params, next) => { + console.log('3') + const result = await next(params) + console.log('4') + return result +}) + +const create = await prisma.post.create({ + data: { + title: 'Welcome to Prisma Day 2020', + }, +}) + +const create2 = await prisma.post.create({ + data: { + title: 'How to Prisma!', + }, +}) +``` + +Output: + +```no-lines +Welcome to Prisma Day 2020 +1 +2 +3 +4 +5 +6 +How to Prisma! +1 +2 +3 +4 +5 +6 +``` + +## Performance and appropriate use cases + +Middleware executes for **every** query, which means that overuse has the potential to negatively impact performance. To avoid adding performance overheads: + +- Check the `params.model` and `params.action` properties early in your middleware to avoid running logic unnecessarily: + + ```ts + prisma.$use(async (params, next) => { + if (params.model == 'Post' && params.action == 'delete') { + // Logic only runs for delete action and Post model + } + return next(params) + }) + ``` + +- Consider whether middleware is the appropriate solution for your scenario. For example: + + - If you need to populate a field, can you use the [`@default`](/orm/reference/prisma-schema-reference#default) attribute? + - If you need to set the value of a `DateTime` field, can you use the `now()` function or the `@updatedAt` attribute? + - If you need to perform more complex validation, can you use a `CHECK` constraint in the database itself? diff --git a/docs/200-orm/200-prisma-client/300-client-extensions/index.mdx b/docs/200-orm/200-prisma-client/300-client-extensions/index.mdx new file mode 100644 index 0000000000..2b10696724 --- /dev/null +++ b/docs/200-orm/200-prisma-client/300-client-extensions/index.mdx @@ -0,0 +1,231 @@ +--- +title: 'Extensions' +metaTitle: 'Prisma Client extensions' +metaDescription: 'Extend the functionality of Prisma Client' +tocDepth: 4 +--- + + + + + +Prisma Client extensions are Generally Available from versions 4.16.0 and later. They were introduced in Preview in version 4.7.0. Make sure you enable the `clientExtensions` Preview feature flag if you are running on a version earlier than 4.16.0. + + + +You can use Prisma Client extensions to add functionality to your models, result objects, and queries, or to add client-level methods. + +You can create an extension with one or more of the following component types: + +- `model`: [add custom methods or fields to your models](/orm/prisma-client/client-extensions/model) +- `client`: [add client-level methods to Prisma Client](/orm/prisma-client/client-extensions/client) +- `query`: [create custom Prisma Client queries](/orm/prisma-client/client-extensions/query) +- `result`: [add custom fields to your query results](/orm/prisma-client/client-extensions/result) + +For example, you might create an extension that uses the `model` and `client` component types. + + + +## About Prisma Client extensions + +When you use a Prisma Client extension, you create an _extended client_. An extended client is a lightweight variant of the standard Prisma Client that is wrapped by one or more extensions. The standard client is not mutated. You can add as many extended clients as you want to your project. [Learn more about extended clients](#extended-clients). + +You can associate a single extension, or multiple extensions, with an extended client. [Learn more about multiple extensions](#multiple-extensions). + +You can [share your Prisma Client extensions](/orm/prisma-client/client-extensions/shared-extensions) with other Prisma users, and [import Prisma Client extensions developed by other users](/orm/prisma-client/client-extensions/shared-extensions#install-a-shared-packaged-extension) into your Prisma project. + +### Extended clients + +Extended clients interact with each other, and with the standard client, as follows: + +- Each extended client operates independently in an isolated instance. +- Extended clients cannot conflict with each other, or with the standard client. +- All extended clients and the standard client communicate with the same [Prisma query engine](/orm/more/under-the-hood/engines). +- All extended clients and the standard client share the same connection pool. + +> **Note**: The author of an extension can modify this behavior since they're able to run arbitrary code as part of an extension. For example, an extension might actually create an entirely new `PrismaClient` instance (including its own query engine and connection pool). Be sure to check the documentation of the extension you're using to learn about any specific behavior it might implement. + +### Example use cases for extended clients + +Because extended clients operate in isolated instances, they can be a good way to do the following, for example: + +- Implement row-level security (RLS), where each HTTP request has its own client with its own RLS extension, customized with session data. This can keep each user entirely separate, each in a separate client. +- Add a `user.current()` method for the `User` model to get the currently logged-in user. +- Enable more verbose logging for requests if a debug cookie is set. +- Attach a unique request id to all logs so that you can correlate them later, for example to help you analyze the operations that Prisma Client carries out. +- Remove a `delete` method from models unless the application calls the admin endpoint and the user has the necessary privileges. + +## Add an extension to Prisma Client + +You can create an extension using two primary ways: + +- Use the client-level [`$extends`](/orm/reference/prisma-client-reference#client-methods) method + + ```ts + const xprisma = prisma.$extends({ + name: 'signUp', // Optional: name appears in error logs + model: { // This is a `model` component + user: { ... } // The extension logic for the `user` model goes inside the curly braces + }, + }) + ``` + +- Use the `Prisma.defineExtension` method to define an extension and assign it to a variable, and then pass the extension to the client-level `$extends` method + + ```ts + import { Prisma } from '@prisma/client' + + // Define the extension + const myExtension = Prisma.defineExtension({ + name: 'signUp', // Optional: name appears in error logs + model: { // This is a `model` component + user: { ... } // The extension logic for the `user` model goes inside the curly braces + }, + }) + + // Pass the extension to a Prisma Client instance + const xprisma = prisma.$extends(myExtension) + ``` + + :::tip + + This pattern is useful for when you would like to separate extensions into multiple files or directories within a project. + + ::: + +The above examples use the [`model` extension component](/orm/prisma-client/client-extensions/model) to extend the `User` model. + +In your `$extends` method, use the appropriate extension component or components ([`model`](/orm/prisma-client/client-extensions/model), [`client`](/orm/prisma-client/client-extensions/client), [`result`](/orm/prisma-client/client-extensions/result) or [`query`](/orm/prisma-client/client-extensions/query)). + +## Name an extension for error logs + +You can name your extensions to help identify them in error logs. To do so, use the optional field `name`. For example: + +```ts +const prisma = new PrismaClient().$extends({ + name: `signUp`, // (Optional) Extension name + model: { + user: { ... } + }, +}) +``` + +## Multiple extensions + +You can associate an extension with an [extended client](#about-prisma-client-extensions) in one of two ways: + +- You can associate it with an extended client on its own, or +- You can combine the extension with other extensions and associate all of these extensions with an extended client. The functionality from these combined extensions applies to the same extended client. + Note: [Combined extensions can conflict](#conflicts-in-combined-extensions). + +You can combine the two approaches above. For example, you might associate one extension with its own extended client and associate two other extensions with another extended client. [Learn more about how client instances interact](#extended-clients). + +### Apply multiple extensions to an extended client + +In the following example, suppose that you have two extensions, `extensionA` and `extensionB`. There are two ways to combine these. + +#### Option 1: Declare the new client in one line + +With this option, you apply both extensions to a new client in one line of code. + +```ts +// First of all, store your original Prisma Client in a variable as usual +const prisma = new PrismaClient() + +// Declare an extended client that has an extensionA and extensionB +const prismaAB = prisma.$extends(extensionA).$extends(extensionB) +``` + +You can then refer to `prismaAB` in your code, for example `prismaAB.myExtensionMethod()`. + +#### Option 2: Declare multiple extended clients + +The advantage of this option is that you can call any of the extended clients separately. + +```ts +// First of all, store your original Prisma Client in a variable as usual +const prisma = new PrismaClient() + +// Declare an extended client that has extensionA applied +const prismaA = prisma.$extends(extensionA) + +// Declare an extended client that has extensionB applied +const prismaB = prisma.$extends(extensionB) + +// Declare an extended client that is a combination of clientA and clientB +const prismaAB = prismaA.$extends(extensionB) +``` + +In your code, you can call any of these clients separately, for example `prismaA.myExtensionMethod()`, `prismaB.myExtensionMethod()`, or `prismaAB.myExtensionMethod()`. + +### Conflicts in combined extensions + +When you combine two or more extensions into a single extended client, then the _last_ extension that you declare takes precedence in any conflict. In the example in option 1 above, suppose there is a method called `myExtensionMethod()` defined in `extensionA` and a method called `myExtensionMethod()` in `extensionB`. When you call `prismaAB.myExtensionMethod()`, then Prisma Client uses `myExtensionMethod()` as defined in `extensionB`. + +## Type of an extended client + +You can infer the type of an extended Prisma Client instance using the [`typeof`](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html) utility as follows: + +```ts +const extendedPrismaClient = new PrismaClient().$extends({ + /** extension */ +}) + +type ExtendedPrismaClient = typeof extendedPrismaClient +``` + +If you're using Prisma Client as a singleton, you can get the type of the extended Prisma Client instance using the `typeof` and [`ReturnType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype) utilities as follows: + +```ts +function getExtendedClient() { + return new PrismaClient().$extends({ + /* extension */ + }) +} + +type ExtendedPrismaClient = ReturnType +``` + +## Limitations + +### Usage of `$on` and `$use` with extended clients + +`$on` and `$use` are not available in extended clients. If you would like to continue using these [client-level methods](/orm/reference/prisma-client-reference#client-methods) with an extended client, you will need to hook them up before extending the client. + +```ts +const prisma = new PrismaClient() + +prisma.$use(async (params, next) => { + console.log('This is middleware!') + return next(params) +}) + +const xPrisma = prisma.$extends({ + name: 'myExtension', + model: { + user: { + async signUp(email: string) { + await prisma.user.create({ data: { email } }) + }, + }, + }, +}) +``` + +To learn more, see our documentation on [`$on`](/orm/reference/prisma-client-reference#on) and [`$use`](/orm/reference/prisma-client-reference#use) + +### Usage of client-level methods in extended clients + +[Client-level methods](/orm/reference/prisma-client-reference#client-methods) do not necessarily exist on extended clients. For these clients you will need to first check for existence before using. + +```ts +const xPrisma = prisma.$extends(...); + +if (xPrisma.$connect) { + xPrisma.$connect() +} +``` + +### Usage with nested operations + +The `query` extension type does not support nested read and write operations. diff --git a/docs/200-orm/200-prisma-client/400-type-safety/050-prisma-validator.mdx b/docs/200-orm/200-prisma-client/400-type-safety/050-prisma-validator.mdx new file mode 100644 index 0000000000..8a65adfd0d --- /dev/null +++ b/docs/200-orm/200-prisma-client/400-type-safety/050-prisma-validator.mdx @@ -0,0 +1,153 @@ +--- +title: 'Prisma validator' +metaTitle: 'Prisma validator' +metaDescription: 'The Prisma validator is a utility function that takes a generated type and returns a type-safe object which adheres to the generated types model fields.' +--- + + + +The [`Prisma.validator`](/orm/reference/prisma-client-reference#prismavalidator) is a utility function that takes a generated type and returns a type-safe object which adheres to the generated types model fields. + +This page introduces the `Prisma.validator` and offers some motivations behind why you might choose to use it. + + + +> **Note**: If you have a use case for `Prisma.validator`, be sure to check out this [blog post](https://www.prisma.io/blog/satisfies-operator-ur8ys8ccq7zb) about improving your Prisma workflows with the new TypeScript `satisfies` keyword. It's likely that you can solve your use case natively using `satisfies` instead of using `Prisma.validator`. + +## Creating a typed query statement + +Let's imagine that you created a new `userEmail` object that you wanted to re-use in different queries throughout your application. It's typed and can be safely used in queries. + +The below example asks `Prisma` to return the `email` of the user whose `id` is 3, if no user exists it will return `null`. + +```ts +import { Prisma } from '@prisma/client' + +const userEmail: Prisma.UserSelect = { + email: true, +} + +// Run inside async function +const user = await prisma.user.findUnique({ + where: { + id: 3, + }, + select: userEmail, +}) +``` + +This works well but there is a caveat to extracting query statements this way. + +You'll notice that if you hover your mouse over `userEmail` TypeScript won't infer the object's key or value (that is, `email: true`). + +The same applies if you use dot notation on `userEmail` within the `prisma.user.findUnique(...)` query, you will be able to access all of the properties available to a `select` object. + +If you are using this in one file that may be fine, but if you are going to export this object and use it in other queries, or if you are compiling an external library where you want to control how the user uses this object within their queries then this won't be type-safe. + +The object `userEmail` has been created to select only the user's `email`, and yet it still gives access to all the other properties available. **It is typed, but not type-safe**. + +`Prisma` has a way to validate generated types to make sure they are type-safe, a utility function available on the namespace called `validator`. + +## Using the `Prisma.validator` + +The following example passes the `UserSelect` generated type into the `Prisma.validator` utility function and defines the expected return type in much the same way as the previous example. + +```ts highlight=3,4,5;delete|7-9;add +import { Prisma } from '@prisma/client' + +const userEmail: Prisma.UserSelect = { + email: true, +} + +const userEmail = Prisma.validator()({ + email: true, +}) + +// Run inside async function +const user = await prisma.user.findUnique({ + where: { + id: 3, + }, + select: userEmail, +}) +``` + +Alternatively, you can use the following syntax that uses a "selector" pattern using an existing instance of Prisma Client: + +```ts +import { Prisma } from '@prisma/client' +import prisma from './lib/prisma' + +const userEmail = Prisma.validator( + prisma, + 'user', + 'findUnique', + 'select' +)({ + email: true, +}) +``` + +The big difference is that the `userEmail` object is now type-safe. If you hover your mouse over it TypeScript will tell you the object's key/value pair. If you use dot notation to access the object's properties you will only be able to access the `email` property of the object. + +This functionality is handy when combined with user defined input, like form data. + +## Combining `Prisma.validator` with form input + +The following example creates a type-safe function from the `Prisma.validator` which can be used when interacting with user created data, such as form inputs. + +> **Note**: Form input is determined at runtime so can't be verified by only using TypeScript. Be sure to validate your form input through other means too (such as an external validation library) before passing that data through to your database. + +```ts +import { Prisma, PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +// Create a new function and pass the parameters onto the validator +const createUserAndPost = ( + name: string, + email: string, + postTitle: string, + profileBio: string +) => { + return Prisma.validator()({ + name, + email, + posts: { + create: { + title: postTitle, + }, + }, + profile: { + create: { + bio: profileBio, + }, + }, + }) +} + +const findSpecificUser = (email: string) => { + return Prisma.validator()({ + email, + }) +} + +// Create the user in the database based on form input +// Run inside async function +await prisma.user.create({ + data: createUserAndPost( + 'Rich', + 'rich@boop.com', + 'Life of Pie', + 'Learning each day' + ), +}) + +// Find the specific user based on form input +// Run inside async function +const oneUser = await prisma.user.findUnique({ + where: findSpecificUser('rich@boop.com'), +}) +``` + +The `createUserAndPost` custom function is created using the `Prisma.validator` and passed a generated type, `UserCreateInput`. The `Prisma.validator` validates the functions input because the types assigned to the parameters must match those the generated type expects. diff --git a/docs/200-orm/200-prisma-client/400-type-safety/100-operating-against-partial-structures-of-model-types.mdx b/docs/200-orm/200-prisma-client/400-type-safety/100-operating-against-partial-structures-of-model-types.mdx new file mode 100644 index 0000000000..751a0ce83b --- /dev/null +++ b/docs/200-orm/200-prisma-client/400-type-safety/100-operating-against-partial-structures-of-model-types.mdx @@ -0,0 +1,137 @@ +--- +title: 'Operating against partial structures of your model types' +metaTitle: 'Operating against partial structures of your model types' +metaDescription: 'This page documents various scenarios for using the generated types from the Prisma namespace' +--- + + + +When using Prisma Client, every model from your [Prisma schema](/orm/prisma-schema) is translated into a dedicated TypeScript type. For example, assume you have the following `User` and `Post` models: + +```prisma +model User { + id Int @id + email String @unique + name String? + posts Post[] +} + +model Post { + id Int @id + author User @relation(fields: [userId], references: [id]) + title String + published Boolean @default(false) + userId Int +} +``` + +The Prisma Client code that's generated from this schema contains this representation of the `User` type: + +```ts +export declare type User = { + id: string + email: string + name: string | null +} +``` + + + +## Problem: Using variations of the generated model type + +### Description + +In some scenarios, you may need a _variation_ of the generated `User` type. For example, when you have a function that expects an instance of the `User` model that carries the `posts` relation. Or when you need a type to pass only the `User` model's `email` and `name` fields around in your application code. + +### Solution + +As a solution, you can customize the generated model type using Prisma Client's helper types. + +The `User` type only contains the model's [scalar](/orm/prisma-schema/data-model/models#scalar-fields) fields, but doesn't account for any relations. That's because [relations are not included by default](/orm/prisma-client/queries/select-fields#return-the-default-selection-set) in Prisma Client queries. + +However, sometimes it's useful to have a type available that **includes a relation** (i.e. a type that you'd get from an API call that uses [`include`](/orm/prisma-client/queries/select-fields#include-relations-and-select-relation-fields)). Similarly, another useful scenario could be to have a type available that **includes only a subset of the model's scalar fields** (i.e. a type that you'd get from an API call that uses [`select`](/orm/prisma-client/queries/select-fields#select-specific-fields)). + +One way of achieving this would be to define these types manually in your application code: + +```ts +// 1: Define a type that includes the relation to `Post` +type UserWithPosts = { + id: string + email: string + name: string | null + posts: Post[] +} + +// 2: Define a type that only contains a subset of the scalar fields +type UserPersonalData = { + email: string + name: string | null +} +``` + +While this is certainly feasible, this approach increases the maintenance burden upon changes to the Prisma schema as you need to manually maintain the types. A cleaner solution to this is to use the `UserGetPayload` type that is generated and exposed by Prisma Client under the `Prisma` namespace in combination with the [`validator`](prisma-validator). + +The following example uses the `Prisma.validator` to create two type-safe objects and then uses the `Prisma.UserGetPayload` utility function to create a type that can be used to return all users and their posts. + +```ts +import { Prisma } from '@prisma/client' + +// 1: Define a type that includes the relation to `Post` +const userWithPosts = Prisma.validator()({ + include: { posts: true }, +}) + +// 2: Define a type that only contains a subset of the scalar fields +const userPersonalData = Prisma.validator()({ + select: { email: true, name: true }, +}) + +// 3: This type will include a user and all their posts +type UserWithPosts = Prisma.UserGetPayload +``` + +The main benefits of the latter approach are: + +- Cleaner approach as it leverages Prisma Client's generated types +- Reduced maintenance burden and improved type safety when the schema changes + +## Problem: Getting access to the return type of a function + +### Description + +When doing [`select`](/orm/reference/prisma-client-reference#select) or [`include`](/orm/reference/prisma-client-reference#include) operations on your models and returning these variants from a function, it can be difficult to gain access to the return type, e.g: + +```ts +// Function definition that returns a partial structure +async function getUsersWithPosts() { + const users = await prisma.user.findMany({ include: { posts: true } }) + return users +} +``` + +Extracting the type that represents "users with posts" from the above code snippet requires some advanced TypeScript usage: + +```ts +// Function definition that returns a partial structure +async function getUsersWithPosts() { + const users = await prisma.user.findMany({ include: { posts: true } }) + return users +} + +// Extract `UsersWithPosts` type with +type ThenArg = T extends PromiseLike ? U : T +type UsersWithPosts = ThenArg> + +// run inside `async` function +const usersWithPosts: UsersWithPosts = await getUsersWithPosts() +``` + +### Solution + +With the `PromiseReturnType` that is exposed by the Prisma namespace, you can solve this more elegantly: + +```ts +import { Prisma } from '@prisma/client' + +type UsersWithPosts = Prisma.PromiseReturnType +``` diff --git a/docs/200-orm/200-prisma-client/400-type-safety/830-prisma-type-system.mdx b/docs/200-orm/200-prisma-client/400-type-safety/830-prisma-type-system.mdx new file mode 100644 index 0000000000..e00397c0f8 --- /dev/null +++ b/docs/200-orm/200-prisma-client/400-type-safety/830-prisma-type-system.mdx @@ -0,0 +1,164 @@ +--- +title: How to use Prisma's type system +metaDescription: How to use Prisma's type system +tocDepth: 3 +--- + + + +This guide introduces Prisma's type system and explains how to introspect existing native types in your database, and how to use types when you apply schema changes to your database with Prisma Migrate or `db push`. + + + +## How does Prisma's type system work? + +Prisma uses _types_ to define the kind of data that a field can hold. To make it easy to get started, Prisma provides a small number of core [scalar types](/orm/reference/prisma-schema-reference#model-field-scalar-types) that should cover most default use cases. For example, take the following blog post model: + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Post { + id Int @id + title String + createdAt DateTime +} +``` + +The `title` field of the `Post` model uses the `String` scalar type, while the `createdAt` field uses the `DateTime` scalar type. + +Databases also have their own type system, which defines the type of value that a column can hold. Most databases provide a large number of data types to allow fine-grained control over exactly what a column can store. For example, a database might provide inbuilt support for multiple sizes of integers, or for XML data. The names of these types vary between databases. For example, in PostgreSQL the column type for booleans is `boolean`, whereas in MySQL the `tinyint(1)` type is typically used. + +In the blog post example above, we are using the PostgreSQL connector. This is specified in the `datasource` block of the Prisma schema. + +### Default type mappings + +To allow you to get started with our core scalar types, Prisma provides _default type mappings_ that map each scalar type to a default type in the underlying database. For example: + +- by default Prisma's `String` type gets mapped to PostgreSQL's `text` type and MySQL's `varchar` type +- by default Prisma's `DateTime` type gets mapped to PostgreSQL's `timestamp(3)` type and SQL Server's `datetime2` type + +See Prisma's [database connector pages](/orm/overview) for the default type mappings for a given database. For example, [this table](/orm/overview/databases/postgresql#type-mapping-between-postgresql-and-prisma-schema) gives the default type mappings for PostgreSQL. +To see the default type mappings for all databases for a specific given Prisma type, see the [model field scalar types section](/orm/reference/prisma-schema-reference#model-field-scalar-types) of the Prisma schema reference. For example, [this table](/orm/reference/prisma-schema-reference#float) gives the default type mappings for the `Float` scalar type. + +### Native type mappings + +Sometimes you may need to use a more specific database type that is not one of the default type mappings for your Prisma type. For this purpose, Prisma provides [native type attributes](/orm/prisma-schema/data-model/models#native-types-mapping) to refine the core scalar types. For example, in the `createdAt` field of your `Post` model above you may want to use a date-only column in your underlying PostgreSQL database, by using the `date` type instead of the default type mapping of `timestamp(3)`. To do this, add a `@db.Date` native type attribute to the `createdAt` field: + +```prisma file=schema.prisma +model Post { + id Int @id + title String + createdAt DateTime @db.Date +} +``` + +Native type mappings allow you to express all the types in your database. However, you do not need to use them if the Prisma defaults satisfy your needs. This leads to a shorter, more readable Prisma schema for common use cases. + +## How to introspect database types + +When you [introspect](/orm/prisma-schema/introspection) an existing database, Prisma will take the database type of each table column and represent it in your Prisma schema using the correct Prisma type for the corresponding model field. If the database type is not the default database type for that Prisma scalar type, Prisma will also add a native type attribute. + +As an example, take a `User` table in a PostgreSQL database, with: + +- an `id` column with a data type of `serial` +- a `name` column with a data type of `text` +- an `isActive` column with a data type of `boolean` + +You can create this with the following SQL command: + +```sql +CREATE TABLE "public"."User" ( + id serial PRIMARY KEY NOT NULL, + name text NOT NULL, + "isActive" boolean NOT NULL +); +``` + +Introspect your database with the following command run from the root directory of your project: + +```terminal +npx prisma db pull +``` + +You will get the following Prisma schema: + +```prisma file=schema.prisma +model User { + id Int @id @default(autoincrement()) + name String + isActive Boolean +} +``` + +The `id`, `name` and `isActive` columns in the database are mapped respectively to the `Int`, `String` and `Boolean` Prisma types. The database types are the _default_ database types for these Prisma types, so Prisma does not add any native type attributes. + +Now add a `createdAt` column to your database with a data type of `date` by running the following SQL command: + +```sql +ALTER TABLE "public"."User" +ADD COLUMN "createdAt" date NOT NULL; +``` + +Introspect your database again: + +```terminal +npx prisma db pull +``` + +Your Prisma schema now includes the new `createdAt` field with a Prisma type of `DateTime`. The `createdAt` field also has a `@db.Date` native type attribute, because PostgreSQL's `date` is not the default type for the `DateTime` type: + +```prisma file=schema.prisma highlight=5;add +model User { + id Int @id @default(autoincrement()) + name String + isActive Boolean + createdAt DateTime @db.Date +} +``` + +## How to use types when you apply schema changes to your database + +When you apply schema changes to your database using Prisma Migrate or `db push`, Prisma will use both the Prisma scalar type of each field and any native attribute it has to determine the correct database type for the corresponding column in the database. + +As an example, create a Prisma schema with the following `Post` model: + +```prisma file=schema.prisma +model Post { + id Int @id + title String + createdAt DateTime + updatedAt DateTime @db.Date +} +``` + +This `Post` model has: + +- an `id` field with a Prisma type of `Int` +- a `title` field with a Prisma type of `String` +- a `createdAt` field with a Prisma type of `DateTime` +- an `updatedAt` field with a Prisma type of `DateTime` and a `@db.Date` native type attribute + +Now apply these changes to an empty PostgreSQL database with the following command, run from the root directory of your project: + +```terminal +npx prisma db push +``` + +You will see that the database has a newly created `Post` table, with: + +- an `id` column with a database type of `integer` +- a `title` column with a database type of `text` +- a `createdAt` column with a database type of `timestamp(3)` +- an `updatedAt` column with a database type of `date` + +Notice that the `@db.Date` native type attribute modifies the database type of the `updatedAt` column to `date`, rather than the default of `timestamp(3)`. + +## More on using Prisma's type system + +For further reference information on using Prisma's type system, see the following resources: + +- The [database connector](/orm/overview) page for each database provider has a type mapping section with a table of default type mappings between Prisma types and database types, and a table of database types with their corresponding native type attribute in Prisma. For example, the type mapping section for PostgreSQL is [here](/orm/overview/databases/postgresql#type-mapping-between-postgresql-and-prisma-schema). +- The [model field scalar types](/orm/reference/prisma-schema-reference#model-field-scalar-types) section of the Prisma schema reference has a subsection for each Prisma scalar type. This includes a table of default mappings for that Prisma type in each database, and a table for each database listing the corresponding database types and their native type attributes in Prisma. For example, the entry for the `String` Prisma type is [here](/orm/reference/prisma-schema-reference#string). diff --git a/docs/200-orm/200-prisma-client/400-type-safety/index.mdx b/docs/200-orm/200-prisma-client/400-type-safety/index.mdx new file mode 100644 index 0000000000..7b968206f5 --- /dev/null +++ b/docs/200-orm/200-prisma-client/400-type-safety/index.mdx @@ -0,0 +1,267 @@ +--- +title: 'Type safety' +metaTitle: 'Type safety' +metaDescription: 'Prisma Client provides full type safety for queries, even for partial queries or included relations. This page explains how to leverage the generated types and utilities.' +tocDepth: 3 +--- + + + +The generated code for Prisma Client contains several helpful types and utilities that you can use to make your application more type-safe. This page describes patterns for leveraging them. + +> **Note**: If you're interested in advanced type safety topics with Prisma, be sure to check out this [blog post](https://www.prisma.io/blog/satisfies-operator-ur8ys8ccq7zb) about improving your Prisma workflows with the new TypeScript `satisfies` keyword. + + + +## Importing generated types + +You can import the `Prisma` namespace and use dot notation to access types and utilities. The following example shows how to import the `Prisma` namespace and use it to access and use the `Prisma.UserSelect` [generated type](#what-are-generated-types): + +```ts +import { Prisma } from '@prisma/client' + +// Build 'select' object +const userEmail: Prisma.UserSelect = { + email: true, +} + +// Use select object +const createUser = await prisma.user.create({ + data: { + email: 'bob@prisma.io', + }, + select: userEmail, +}) +``` + +See also: [Using the `Prisma.UserCreateInput` generated type](/orm/prisma-client/queries/crud#create-a-single-record-using-generated-types) + +## What are generated types? + +Generated types are TypeScript types that are derived from your models. You can use them to create typed objects that you pass into top-level methods like `prisma.user.create(...)` or `prisma.user.update(...)`, or options such as `select` or `include`. + +For example, `select` accepts an object of type `UserSelect`. Its object properties match those that are supported by `select` statements according to the model. + +The first tab below shows the `UserSelect` generated type and how each property on the object has a type annotation. The second tab shows the resulting schema model. + + + + + +```ts +type Prisma.UserSelect = { + id?: boolean | undefined; + email?: boolean | undefined; + name?: boolean | undefined; + posts?: boolean | Prisma.PostFindManyArgs | undefined; + profile?: boolean | Prisma.ProfileArgs | undefined; +} +``` + + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] + profile Profile? +} +``` + + + + + +In TypeScript the concept of [type annotations](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-annotations-on-variables) is when you declare a variable and add a type annotation to describe the type of the variable. See the below example. + +```ts +const myAge: number = 37 +const myName: string = 'Rich' +``` + +Both of these variable declarations have been given a type annotation to specify what primitive type they are, `number` and `string` respectively. Most of the time this kind of annotation is not needed as TypeScript will infer the type of the variable based on how its initialized. In the above example `myAge` was initialized with a number so TypeScript guesses that it should be typed as a number. + +Going back to the `UserSelect` type, if you were to use dot notation on the created object `userEmail`, you would have access to all of the fields on the `User` model that can be interacted with using a `select` statement. + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] + profile Profile? +} +``` + +```ts +import { Prisma } from '@prisma/client' + +const userEmail: Prisma.UserSelect = { + email: true, +} + +// properties available on the typed object +userEmail.id +userEmail.email +userEmail.name +userEmail.posts +userEmail.profile +``` + +In the same mould, you can type an object with an `include` generated type then your object would have access to those properties on which you can use an `include` statement. + +```ts +import { Prisma } from '@prisma/client' + +const userPosts: Prisma.UserInclude = { + posts: true, +} + +// properties available on the typed object +userPosts.posts +userPosts.profile +``` + +> See the [model query options](/orm/reference/prisma-client-reference#model-query-options) reference for more information about the different types available. + +### Generated `UncheckedInput` types + +The `UncheckedInput` types are a special set of generated types that allow you to perform some operations that Prisma Client considers "unsafe", like directly writing [relation scalar fields](/orm/prisma-schema/data-model/relations). You can choose either the "safe" `Input` types or the "unsafe" `UncheckedInput` type when doing operations like `create`, `update`, or `upsert`. + +For example, this Prisma schema has a one-to-many relation between `User` and `Post`: + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String @db.VarChar(255) + content String? + author User @relation(fields: [authorId], references: [id]) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +The first tab shows the `PostUncheckedCreateInput` generated type. It contains the `authorId` property, which is a relation scalar field. The second tab shows an example query that uses the `PostUncheckedCreateInput` type. This query will result in an error if a user with an `id` of `1` does not exist. + + + + + +```ts +type PostUncheckedCreateInput = { + id?: number + title: string + content?: string | null + authorId: number +} +``` + + + + +```ts +prisma.post.create({ + data: { + title: 'First post', + content: 'Welcome to the first post in my blog...', + authorId: 1, + }, +}) +``` + + + + + +The same query can be rewritten using the "safer" `PostCreateInput` type. This type does not contain the `authorId` field but instead contains the `author` relation field. + + + + + +```ts +type PostCreateInput = { + title: string + content?: string | null + author: UserCreateNestedOneWithoutPostsInput +} + +type UserCreateNestedOneWithoutPostsInput = { + create?: XOR< + UserCreateWithoutPostsInput, + UserUncheckedCreateWithoutPostsInput + > + connectOrCreate?: UserCreateOrConnectWithoutPostsInput + connect?: UserWhereUniqueInput +} +``` + + + + +```ts +prisma.post.create({ + data: { + title: 'First post', + content: 'Welcome to the first post in my blog...', + author: { + connect: { + id: 1, + }, + }, + }, +}) +``` + + + + + +This query will also result in an error if an author with an `id` of `1` does not exist. In this case, Prisma Client will give a more descriptive error message. You can also use the [`connectOrCreate`](/orm/reference/prisma-client-reference#connectorcreate) API to safely create a new user if one does not already exist with the given `id`. + +We recommend using the "safe" `Input` types whenever possible. + +## Type utilities + + + +This feature is available from Prisma version 4.9.0 upwards. + + + +To help you create highly type-safe applications, Prisma Client provides a set of type utilities that tap into input and output types. These types are fully dynamic, which means that they adapt to any given model and schema. You can use them to improve the auto-completion and developer experience of your projects. + +This is especially useful in [validating inputs](/orm/prisma-client/type-safety/prisma-validator) and [shared Prisma Client extensions](/orm/prisma-client/client-extensions/shared-extensions). + +The following type utilities are available in Prisma Client: + +- `Exact`: Enforces strict type safety on `Input`. `Exact` makes sure that a generic type `Input` strictly complies with the type that you specify in `Shape`. It [narrows](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) `Input` down to the most precise types. +- `Args`: Retrieves the input arguments for any given model and operation. This is particularly useful for extension authors who want to do the following: + - Re-use existing types to extend or modify them. + - Benefit from the same auto-completion experience as on existing operations. +- `Result`: Takes the input arguments and provides the result for a given model and operation. You would usually use this in conjunction with `Args`. As with `Args`, `Result` helps you to re-use existing types to extend or modify them. +- `Payload`: Retrieves the entire structure of the result, as scalars and relations objects for a given model and operation. For example, you can use this to determine which keys are scalars or objects at a type level. + +As an example, here's a quick way you can enforce that the arguments to a function matches what you will pass to a `post.create`: + +```ts +type PostCreateBody = Prisma.Args['data'] + +const addPost = async (postBody: PostCreateBody) => { + const post = await prisma.post.create({ data: postBody }) + return post +} + +await addPost(myData) +// ^ guaranteed to match the input of `post.create` +``` diff --git a/docs/200-orm/200-prisma-client/450-testing/100-unit-testing.mdx b/docs/200-orm/200-prisma-client/450-testing/100-unit-testing.mdx new file mode 100644 index 0000000000..d5cbb0d23c --- /dev/null +++ b/docs/200-orm/200-prisma-client/450-testing/100-unit-testing.mdx @@ -0,0 +1,351 @@ +--- +title: 'Unit testing' +metaTitle: 'Unit testing with Prisma' +metaDescription: 'Learn how to setup and run unit tests with Prisma Client' +tocDepth: 3 +--- + + + +Unit testing aims to isolate a small portion (unit) of code and test it for logically predictable behaviors. It generally involves mocking objects or server responses to simulate real world behaviors. Some benefits to unit testing include: + +- Quickly find and isolate bugs in code. +- Provides documentation for each module of code by way of indicating what certain code blocks should be doing. +- A helpful gauge that a refactor has gone well. The tests should still pass after code has been refactored. + +In the context of Prisma, this generally means testing a function which makes database calls using Prisma Client. + +A single test should focus on how your function logic handles different inputs (such as a null value or an empty list). + +This means that you should aim to remove as many dependencies as possible, such as external services and databases, to keep the tests and their environments as lightweight as possible. + + + +> **Note**: This [blog post](https://www.prisma.io/blog/testing-series-2-xPhjjmIEsM) provides a comprehensive guide to implementing unit testing in your Express project with Prisma. If you're looking to delve into this topic, be sure to give it a read! + +## Prerequisites + +This guide assumes you have the JavaScript testing library [`Jest`](https://jestjs.io/) and [`ts-jest`](https://github.com/kulshekhar/ts-jest) already setup in your project. + +## Mocking Prisma Client + +To ensure your unit tests are isolated from external factors you can mock Prisma Client, this means you get the benefits of being able to use your schema (**_type-safety_**), without having to make actual calls to your database when your tests are run. + +This guide will cover two approaches to mocking Prisma Client, a singleton instance and dependency injection. Both have their merits depending on your use cases. To help with mocking Prisma Client the [`jest-mock-extended`](https://github.com/marchaos/jest-mock-extended) package will be used. + +```terminal +npm install jest-mock-extended@2.0.4 --save-dev +``` + + + +At the time of writing, this guide uses `jest-mock-extended` version `^2.0.4`. + + + +### Singleton + +The following steps guide you through mocking Prisma Client using a singleton pattern. + +1. Create a file at your projects root called `client.ts` and add the following code. This will instantiate a Prisma Client instance. + + ```ts file=client.ts + import { PrismaClient } from '@prisma/client' + + const prisma = new PrismaClient() + export default prisma + ``` + +2. Next create a file named `singleton.ts` at your projects root and add the following: + + ```ts file=singleton.ts + import { PrismaClient } from '@prisma/client' + import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended' + + import prisma from './client' + + jest.mock('./client', () => ({ + __esModule: true, + default: mockDeep(), + })) + + beforeEach(() => { + mockReset(prismaMock) + }) + + export const prismaMock = prisma as unknown as DeepMockProxy + ``` + +The singleton file tells Jest to mock a default export (the Prisma Client instance in `./client.ts`), and uses the `mockDeep` method from `jest-mock-extended` to enable access to the objects and methods available on Prisma Client. It then resets the mocked instance before each test is run. + +Next, add the `setupFilesAfterEnv` property to your `jest.config.js` file with the path to your `singleton.ts` file. + +```js file=jest.config.js highlight=5;add +module.exports = { + clearMocks: true, + preset: 'ts-jest', + testEnvironment: 'node', + setupFilesAfterEnv: ['/singleton.ts'], +} +``` + +### Dependency injection + +Another popular pattern that can be used is dependency injection. + +1. Create a `context.ts` file and add the following: + + ```ts file=context.ts + import { PrismaClient } from '@prisma/client' + import { mockDeep, DeepMockProxy } from 'jest-mock-extended' + + export type Context = { + prisma: PrismaClient + } + + export type MockContext = { + prisma: DeepMockProxy + } + + export const createMockContext = (): MockContext => { + return { + prisma: mockDeep(), + } + } + ``` + +:::tip + +If you find that you're seeing a circular dependency error highlighted through mocking Prisma Client, try adding `"strictNullChecks": true` +to your `tsconfig.json`. + +::: + +2. To use the context, you would do the following in your test file: + + ```ts + import { MockContext, Context, createMockContext } from '../context' + + let mockCtx: MockContext + let ctx: Context + + beforeEach(() => { + mockCtx = createMockContext() + ctx = mockCtx as unknown as Context + }) + ``` + +This will create a new context before each test is run via the `createMockContext` function. This (`mockCtx`) context will be used to make a mock call to Prisma and run a query to test. The `ctx` context will be used to run a scenario query that is tested against. + +## Example unit tests + +A real world use case for unit testing Prisma might be a signup form. Your user fills in a form which calls a function, which in turn uses Prisma to make a call to your database. + +All of the examples that follow use the following schema model: + +```prisma file=schema.prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + acceptTermsAndConditions Boolean +} +``` + +The following unit tests will mock the process of + +- Creating a new user +- Updating a users name +- Failing to create a user if terms are not accepted + +The functions that use the dependency injection pattern will have the context injected (passed in as a parameter) into them, whereas the functions that use the singleton pattern will use the singleton instance of Prisma Client. + +```ts file=functions-with-context.ts +import { Context } from './context' + +interface CreateUser { + name: string + email: string + acceptTermsAndConditions: boolean +} + +export async function createUser(user: CreateUser, ctx: Context) { + if (user.acceptTermsAndConditions) { + return await ctx.prisma.user.create({ + data: user, + }) + } else { + return new Error('User must accept terms!') + } +} + +interface UpdateUser { + id: number + name: string + email: string +} + +export async function updateUsername(user: UpdateUser, ctx: Context) { + return await ctx.prisma.user.update({ + where: { id: user.id }, + data: user, + }) +} +``` + +```ts file=functions-without-context.ts +import prisma from './client' + +interface CreateUser { + name: string + email: string + acceptTermsAndConditions: boolean +} + +export async function createUser(user: CreateUser) { + if (user.acceptTermsAndConditions) { + return await prisma.user.create({ + data: user, + }) + } else { + return new Error('User must accept terms!') + } +} + +interface UpdateUser { + id: number + name: string + email: string +} + +export async function updateUsername(user: UpdateUser) { + return await prisma.user.update({ + where: { id: user.id }, + data: user, + }) +} +``` + +The tests for each methodology are fairly similar, the difference is how the mocked Prisma Client is used. + +The **_dependency injection_** example passes the context through to the function that is being tested as well as using it to call the mock implementation. + +The **_singleton_** example uses the singleton client instance to call the mock implementation. + +```ts file=__tests__/with-singleton.ts +import { createUser, updateUsername } from '../functions-without-context' +import { prismaMock } from '../singleton' + +test('should create new user ', async () => { + const user = { + id: 1, + name: 'Rich', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + } + + prismaMock.user.create.mockResolvedValue(user) + + await expect(createUser(user)).resolves.toEqual({ + id: 1, + name: 'Rich', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + }) +}) + +test('should update a users name ', async () => { + const user = { + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + } + + prismaMock.user.update.mockResolvedValue(user) + + await expect(updateUsername(user)).resolves.toEqual({ + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + }) +}) + +test('should fail if user does not accept terms', async () => { + const user = { + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: false, + } + + prismaMock.user.create.mockImplementation() + + await expect(createUser(user)).resolves.toEqual( + new Error('User must accept terms!') + ) +}) +``` + +```ts file=__tests__/with-dependency-injection.ts +import { MockContext, Context, createMockContext } from '../context' +import { createUser, updateUsername } from '../functions-with-context' + +let mockCtx: MockContext +let ctx: Context + +beforeEach(() => { + mockCtx = createMockContext() + ctx = mockCtx as unknown as Context +}) + +test('should create new user ', async () => { + const user = { + id: 1, + name: 'Rich', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + } + mockCtx.prisma.user.create.mockResolvedValue(user) + + await expect(createUser(user, ctx)).resolves.toEqual({ + id: 1, + name: 'Rich', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + }) +}) + +test('should update a users name ', async () => { + const user = { + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + } + mockCtx.prisma.user.update.mockResolvedValue(user) + + await expect(updateUsername(user, ctx)).resolves.toEqual({ + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: true, + }) +}) + +test('should fail if user does not accept terms', async () => { + const user = { + id: 1, + name: 'Rich Haines', + email: 'hello@prisma.io', + acceptTermsAndConditions: false, + } + + mockCtx.prisma.user.create.mockImplementation() + + await expect(createUser(user, ctx)).resolves.toEqual( + new Error('User must accept terms!') + ) +}) +``` diff --git a/docs/200-orm/200-prisma-client/450-testing/150-integration-testing.mdx b/docs/200-orm/200-prisma-client/450-testing/150-integration-testing.mdx new file mode 100644 index 0000000000..278f472708 --- /dev/null +++ b/docs/200-orm/200-prisma-client/450-testing/150-integration-testing.mdx @@ -0,0 +1,475 @@ +--- +title: 'Integration testing' +metaTitle: 'Integration testing with Prisma' +metaDescription: 'Learn how to setup and run integration tests with Prisma and Docker' +tocDepth: 3 +--- + + + +Integration tests focus on testing how separate parts of the program work together. In the context of applications using a database, integration tests usually require a database to be available and contain data that is convenient to the scenarios intended to be tested. + +One way to simulate a real world environment is to use [Docker](https://www.docker.com/get-started) to encapsulate a database and some test data. This can be spun up and torn down with the tests and so operate as an isolated environment away from your production databases. + + + +> **Note:** This [blog post](https://www.prisma.io/blog/testing-series-2-xPhjjmIEsM) offers a comprehensive guide on setting up an integration testing environment and writing integration tests against a real database, providing valuable insights for those looking to explore this topic. + +## Prerequisites + +This guide assumes you have [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine as well as `Jest` setup in your project. + +The following ecommerce schema will be used throughout the guide. This varies from the traditional `User` and `Post` models used in other parts of the docs, mainly because it is unlikely you will be running integration tests against your blog. + +
+ +Ecommerce schema + +```prisma file=schema.prisma +// Can have 1 customer +// Can have many order details +model CustomerOrder { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + customer Customer @relation(fields: [customerId], references: [id]) + customerId Int + orderDetails OrderDetails[] +} + +// Can have 1 order +// Can have many products +model OrderDetails { + id Int @id @default(autoincrement()) + products Product @relation(fields: [productId], references: [id]) + productId Int + order CustomerOrder @relation(fields: [orderId], references: [id]) + orderId Int + total Decimal + quantity Int +} + +// Can have many order details +// Can have 1 category +model Product { + id Int @id @default(autoincrement()) + name String + description String + price Decimal + sku Int + orderDetails OrderDetails[] + category Category @relation(fields: [categoryId], references: [id]) + categoryId Int +} + +// Can have many products +model Category { + id Int @id @default(autoincrement()) + name String + products Product[] +} + +// Can have many orders +model Customer { + id Int @id @default(autoincrement()) + email String @unique + address String? + name String? + orders CustomerOrder[] +} +``` + +
+ +The guide uses a singleton pattern for Prisma Client setup. Refer to the [singleton](/orm/prisma-client/testing/unit-testing#singleton) docs for a walk through of how to set that up. + +## Add Docker to your project + +![Docker compose code pointing towards image of container holding a Postgres database](./Docker_Diagram_V1.png) + +With Docker and Docker compose both installed on your machine you can use them in your project. + +1. Begin by creating a `docker-compose.yml` file at your projects root. Here you will add a Postgres image and specify the environments credentials. + +```yml file=docker-compose.yml +# Set the version of docker compose to use +version: '3.9' + +# The containers that compose the project +services: + db: + image: postgres:13 + restart: always + container_name: integration-tests-prisma + ports: + - '5433:5432' + environment: + POSTGRES_USER: prisma + POSTGRES_PASSWORD: prisma + POSTGRES_DB: tests +``` + +> **Note**: The compose version used here (`3.9`) is the latest at the time of writing, if you are following along be sure to use the same version for consistency. + +The `docker-compose.yml` file defines the following: + +- The Postgres image (`postgres`) and version tag (`:13`). This will be downloaded if you do not have it locally available. +- The port `5433` is mapped to the internal (Postgres default) port `5432`. This will be the port number the database is exposed on externally. +- The database user credentials are set and the database given a name. + +2. To connect to the database in the container, create a new connection string with the credentials defined in the `docker-compose.yml` file. For example: + +```env file=.env.test +DATABASE_URL="postgresql://prisma:prisma@localhost:5433/tests" +``` + + + +The above `.env.test` file is used as part of a multiple `.env` file setup. Checkout the [using multiple .env files.](/orm/more/development-environment/environment-variables/using-multiple-env-files) section to learn more about setting up your project with multiple `.env` files + + + +3. To create the container in a detached state so that you can continue to use the terminal tab, run the following command: + +```terminal +docker-compose up -d +``` + +4. Next you can check that the database has been created by executing a `psql` command inside the container. Make a note of the container id. + + + + + + ``` + docker ps + ``` + + + + + + ```code no-copy + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 1322e42d833f postgres:13 "docker-entrypoint.s…" 2 seconds ago Up 1 second 0.0.0.0:5433->5432/tcp integration-tests-prisma + ``` + + + + + +> **Note**: The container id is unique to each container, you will see a different id displayed. + +5. Using the container id from the previous step, run `psql` in the container, login with the created user and check the database is created: + + + + + + ``` + docker exec -it 1322e42d833f psql -U prisma tests + ``` + + + + + + ```code no-copy + tests=# \l + List of databases + Name | Owner | Encoding | Collate | Ctype | Access privileges + + postgres | prisma | UTF8 | en_US.utf8 | en_US.utf8 | + template0 | prisma | UTF8 | en_US.utf8 | en_US.utf8 | =c/prisma + + | | | | | prisma=CTc/prisma + template1 | prisma | UTF8 | en_US.utf8 | en_US.utf8 | =c/prisma + + | | | | | prisma=CTc/prisma + tests | prisma | UTF8 | en_US.utf8 | en_US.utf8 | + (4 rows) + ``` + + + + + +## Integration testing + +Integration tests will be run against a database in a **dedicated test environment** instead of the production or development environments. + +### The flow of operations + +The flow for running said tests goes as follows: + +1. Start the container and create the database +1. Migrate the schema +1. Run the tests +1. Destroy the container + +Each test suite will seed the database before all the test are run. After all the tests in the suite have finished, the data from all the tables will be dropped and the connection terminated. + +### The function to test + +The ecommerce application you are testing has a function which creates an order. This function does the following: + +- Accepts input about the customer making the order +- Accepts input about the product being ordered +- Checks if the customer has an existing account +- Checks if the product is in stock +- Returns an "Out of stock" message if the product doesn't exist +- Creates an account if the customer doesn't exist in the database +- Create the order + +An example of how such a function might look can be seen below: + +```ts file=create-order.ts +import prisma from '../client' + +export interface Customer { + id?: number + name?: string + email: string + address?: string +} + +export interface OrderInput { + customer: Customer + productId: number + quantity: number +} + +/** + * Creates an order with customer. + * @param input The order parameters + */ +export async function createOrder(input: OrderInput) { + const { productId, quantity, customer } = input + const { name, email, address } = customer + + // Get the product + const product = await prisma.product.findUnique({ + where: { + id: productId, + }, + }) + + // If the product is null its out of stock, return error. + if (!product) return new Error('Out of stock') + + // If the customer is new then create the record, otherwise connect via their unique email + await prisma.customerOrder.create({ + data: { + customer: { + connectOrCreate: { + create: { + name, + email, + address, + }, + where: { + email, + }, + }, + }, + orderDetails: { + create: { + total: product.price, + quantity, + products: { + connect: { + id: product.id, + }, + }, + }, + }, + }, + }) +} +``` + +### The test suite + +The following tests will check if the `createOrder` function works as it should do. They will test: + +- Creating a new order with a new customer +- Creating an order with an existing customer +- Show an "Out of stock" error message if a product doesn't exist + +Before the test suite is run the database is seeded with data. After the test suite has finished a [`deleteMany`](/orm/reference/prisma-client-reference#deletemany) is used to clear the database of its data. + +:::tip + +Using `deleteMany` may suffice in situations where you know ahead of time how your schema is structured. This is because the operations need to be executed in the correct order according to how the model relations are setup. + +However, this doesn't scale as well as having a more generic solution that maps over your models and performs a truncate on them. For those scenarios and examples of using raw SQL queries see [Deleting all data with raw SQL / `TRUNCATE`](/orm/prisma-client/queries/crud#deleting-all-data-with-raw-sql--truncate) + +::: + +```ts file=__tests__/create-order.ts +import prisma from '../src/client' +import { createOrder, Customer, OrderInput } from '../src/functions/index' + +beforeAll(async () => { + // create product categories + await prisma.category.createMany({ + data: [{ name: 'Wand' }, { name: 'Broomstick' }], + }) + + console.log('✨ 2 categories successfully created!') + + // create products + await prisma.product.createMany({ + data: [ + { + name: 'Holly, 11", phoenix feather', + description: 'Harry Potters wand', + price: 100, + sku: 1, + categoryId: 1, + }, + { + name: 'Nimbus 2000', + description: 'Harry Potters broom', + price: 500, + sku: 2, + categoryId: 2, + }, + ], + }) + + console.log('✨ 2 products successfully created!') + + // create the customer + await prisma.customer.create({ + data: { + name: 'Harry Potter', + email: 'harry@hogwarts.io', + address: '4 Privet Drive', + }, + }) + + console.log('✨ 1 customer successfully created!') +}) + +afterAll(async () => { + const deleteOrderDetails = prisma.orderDetails.deleteMany() + const deleteProduct = prisma.product.deleteMany() + const deleteCategory = prisma.category.deleteMany() + const deleteCustomerOrder = prisma.customerOrder.deleteMany() + const deleteCustomer = prisma.customer.deleteMany() + + await prisma.$transaction([ + deleteOrderDetails, + deleteProduct, + deleteCategory, + deleteCustomerOrder, + deleteCustomer, + ]) + + await prisma.$disconnect() +}) + +it('should create 1 new customer with 1 order', async () => { + // The new customers details + const customer: Customer = { + id: 2, + name: 'Hermione Granger', + email: 'hermione@hogwarts.io', + address: '2 Hampstead Heath', + } + // The new orders details + const order: OrderInput = { + customer, + productId: 1, + quantity: 1, + } + + // Create the order and customer + await createOrder(order) + + // Check if the new customer was created by filtering on unique email field + const newCustomer = await prisma.customer.findUnique({ + where: { + email: customer.email, + }, + }) + + // Check if the new order was created by filtering on unique email field of the customer + const newOrder = await prisma.customerOrder.findFirst({ + where: { + customer: { + email: customer.email, + }, + }, + }) + + // Expect the new customer to have been created and match the input + expect(newCustomer).toEqual(customer) + // Expect the new order to have been created and contain the new customer + expect(newOrder).toHaveProperty('customerId', 2) +}) + +it('should create 1 order with an existing customer', async () => { + // The existing customers email + const customer: Customer = { + email: 'harry@hogwarts.io', + } + // The new orders details + const order: OrderInput = { + customer, + productId: 1, + quantity: 1, + } + + // Create the order and connect the existing customer + await createOrder(order) + + // Check if the new order was created by filtering on unique email field of the customer + const newOrder = await prisma.customerOrder.findFirst({ + where: { + customer: { + email: customer.email, + }, + }, + }) + + // Expect the new order to have been created and contain the existing customer with an id of 1 (Harry Potter from the seed script) + expect(newOrder).toHaveProperty('customerId', 1) +}) + +it("should show 'Out of stock' message if productId doesn't exit", async () => { + // The existing customers email + const customer: Customer = { + email: 'harry@hogwarts.io', + } + // The new orders details + const order: OrderInput = { + customer, + productId: 3, + quantity: 1, + } + + // The productId supplied doesn't exit so the function should return an "Out of stock" message + await expect(createOrder(order)).resolves.toEqual(new Error('Out of stock')) +}) +``` + +## Running the tests + +This setup isolates a real world scenario so that you can test your applications functionality against real data in a controlled environment. + +You can add some scripts to your projects `package.json` file which will setup the database and run the tests, then afterwards manually destroy the container. + +```json file=package.json + "scripts": { + "docker:up": "docker-compose up -d", + "docker:down": "docker-compose down", + "test": "yarn docker:up && yarn prisma migrate deploy && jest -i" + }, +``` + +The `test` script does the following: + +1. Runs `docker-compose up -d` to create the container with the Postgres image and database. +1. Applies the migrations found in `./prisma/migrations/` directory to the database, this creates the tables in the container's database. +1. Executes the tests. + +Once you are satisfied you can run `yarn docker:down` to destroy the container, its database and any test data. diff --git a/docs/200-orm/200-prisma-client/450-testing/Docker_Diagram_V1.png b/docs/200-orm/200-prisma-client/450-testing/Docker_Diagram_V1.png new file mode 100644 index 0000000000..e2bc67509d Binary files /dev/null and b/docs/200-orm/200-prisma-client/450-testing/Docker_Diagram_V1.png differ diff --git a/docs/200-orm/200-prisma-client/450-testing/index.mdx b/docs/200-orm/200-prisma-client/450-testing/index.mdx new file mode 100644 index 0000000000..9523cc79c8 --- /dev/null +++ b/docs/200-orm/200-prisma-client/450-testing/index.mdx @@ -0,0 +1,14 @@ +--- +title: 'Testing' +navTitle: Testing +metaTitle: 'Testing with Prisma' +metaDescription: 'How to implement unit and integration testing with Prisma' +--- + + + +This section describes how to approach testing an application that uses Prisma Client. + + + + diff --git a/docs/200-orm/200-prisma-client/500-deployment/001-deploy-prisma.mdx b/docs/200-orm/200-prisma-client/500-deployment/001-deploy-prisma.mdx new file mode 100644 index 0000000000..b733f5c803 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/001-deploy-prisma.mdx @@ -0,0 +1,40 @@ +--- +title: 'Deploy Prisma' +metaTitle: 'Deploying Prisma-based projects' +metaDescription: 'Learn more about the different deployment paradigms for Node.js applications and how they affect deploying an application using Prisma Client.' +tocDepth: 2 +--- + + + +Projects using Prisma Client can be deployed to many different cloud platforms. Given the variety of cloud platforms and different names, it's noteworthy to mention the different deployment paradigms, as they affect the way you deploy an application using Prisma Client. + + + +## Deployment paradigms + +Each paradigm has different tradeoffs that affect the performance, scalability, and operational costs of your application. + +Moreover, the user traffic pattern of your application is also an important factor to consider. For example, any application with consistent user traffic may be better suited for a [continuously running paradigm](#traditional-servers), whereas an application with sudden spikes may be better suited to [serverless](#serverless-functions). + +### Traditional servers + +Your application is [traditionally deployed](/orm/prisma-client/deployment/traditional) if a Node.js process is continuously running and handles multiple requests at the same time. Your application could be deployed to a Platform-as-a-Service (PaaS) like [Heroku](/orm/prisma-client/deployment/traditional/deploy-to-heroku), [Koyeb](/orm/prisma-client/deployment/traditional/deploy-to-koyeb), as a Docker container to Kubernetes, or as a Node.js process on a virtual machine, or good old bare metal server. + +See also: [Connection management in long-running processes](/orm/prisma-client/setup-and-configuration/databases-connections#long-running-processes) + +### Serverless Functions + +Your application is [serverless](/orm/prisma-client/deployment/serverless) if the Node.js processes of your application (or subsets of it broken into functions) are started as requests come in, and each function only handles one request at a time. Your application would most likely be deployed to a Function-as-a-Service (FaaS) offering, such as [AWS Lambda](/orm/prisma-client/deployment/serverless/deploy-to-aws-lambda) or [Azure Functions](/orm/prisma-client/deployment/serverless/deploy-to-azure-functions) + +Serverless environments have the concept of warm starts, which means that for subsequent invocations of the same function, it may use an already existing container that has the allocated processes, memory, file system (`/tmp` is writable on AWS Lambda), and even DB connection still available. + +Typically, any piece of code [outside the handler](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-features.html#gettingstarted-features-programmingmodel) remains initialized. + +See also: [Connection management in serverless environments](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas) + +### Edge Functions + +Your application is [edge deployed](/orm/prisma-client/deployment/edge) if your application is [serverless](#serverless-functions) and the functions are distributed across one or more regions close to the user. + +Typically, edge environments also have a different runtime than a traditional or serverless environment, leading to common APIs being unavailable. diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/200-deploy-to-heroku.mdx b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/200-deploy-to-heroku.mdx new file mode 100644 index 0000000000..4242c91b81 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/200-deploy-to-heroku.mdx @@ -0,0 +1,280 @@ +--- +title: 'Deploy to Heroku' +metaTitle: 'Deploy a Prisma app to Heroku' +metaDescription: 'Learn how to deploy a Node.js server that uses Prisma to Heroku.' +--- + + + +In this guide, you will set up and deploy a Node.js server that uses Prisma with PostgreSQL to [Heroku](https://www.heroku.com). The application exposes a REST API and uses Prisma Client to handle fetching, creating, and deleting records from a database. + +Heroku is a cloud platform as a service (PaaS). In contrast to the popular serverless deployment model, with Heroku, your application is constantly running even if no requests are made to it. This has several benefits due to the connection limits of a PostgreSQL database. For more information, check out the [general deployment documentation](/orm/prisma-client/deployment/deploy-prisma) + +Typically Heroku integrates with a Git repository for automatic deployments upon commits. You can deploy to Heroku from a GitHub repository or by pushing your source to a [Git repository that Heroku creates per app](https://devcenter.heroku.com/articles/git). This guide uses the latter approach whereby you push your code to the app's repository on Heroku, which triggers a build and deploys the application. + +The application has the following components: + +- **Backend**: Node.js REST API built with Express.js with resource endpoints that use Prisma Client to handle database operations against a PostgreSQL database (e.g., hosted on Heroku). +- **Frontend**: Static HTML page to interact with the API. + +![architecture diagram](./images/heroku-architecture.png) + +The focus of this guide is showing how to deploy projects using Prisma to Heroku. The starting point will be the [Prisma Heroku example](https://github.com/prisma/prisma-examples/tree/latest/deployment-platforms/heroku), which contains an Express.js server with a couple of preconfigured REST endpoints and a simple frontend. + +> **Note:** The various **checkpoints** throughout the guide allowing you to validate whether you performed the steps correctly. + + + +## A note on deploying GraphQL servers to Heroku + +While the example uses REST, the same principles apply to a GraphQL server, with the main difference being that you typically have a single GraphQL API endpoint rather than a route for every resource as with REST. + +## Prerequisites + +- [Heroku](https://www.heroku.com) account. +- [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) installed. +- Node.js installed. +- PostgreSQL CLI `psql` installed. + +> **Note:** Heroku doesn't provide a free plan, so billing information is required. + +## Prisma workflow + +At the core of Prisma is the [Prisma schema](/orm/prisma-schema) – a declarative configuration where you define your data model and other Prisma-related configuration. The Prisma schema is also a single source of truth for both Prisma Client and Prisma Migrate. + +In this guide, you will use [Prisma Migrate](/orm/prisma-migrate) to create the database schema. Prisma Migrate is based on the Prisma schema and works by generating `.sql` migration files that are executed against the database. + +Migrate comes with two primary workflows: + +- Creating migrations and applying during local development with `prisma migrate dev` +- Applying generated migration to production with `prisma migrate deploy` + +For brevity, the guide does not cover how migrations are created with `prisma migrate dev`. Rather, it focuses on the production workflow and uses the Prisma schema and SQL migration that are included in the example code. + +You will use Heroku's [release phase](https://devcenter.heroku.com/articles/release-phase) to run the `prisma migrate deploy` command so that the migrations are applied before the application starts. + +To learn more about how migrations are created with Prisma Migrate, check out the [start from scratch guide](/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-postgresql) + +## 1. Download the example and install dependencies + +Open your terminal and navigate to a location of your choice. Create the directory that will hold the application code and download the example code: + +```no-lines wrap +mkdir prisma-heroku +cd prisma-heroku +curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=3 prisma-examples-latest/deployment-platforms/heroku +``` + + + +**Checkpoint:** `ls -1` should show: + +```no-lines +ls -1 +Procfile +README.md +package.json +prisma +public +src +``` + +Install the dependencies: + +```no-lines +npm install +``` + +> **Note:** The `Procfile` tells Heroku the command needed to start the application, i.e. `npm start`, and the command to run during the release phase, i.e., `npx prisma migrate deploy` + +## 2. Create a Git repository for the application + +In the previous step, you downloaded the code. In this step, you will create a repository from the code so that you can push it to Heroku for deployment. + +To do so, run `git init` from the source code folder: + +```no-lines +git init +> Initialized empty Git repository in /Users/alice/prisma-heroku/.git/ +``` + +To use the `main` branch as the default branch, run the following command: + +```no-lines +git branch -M main +``` + +With the repository initialized, add and commit the files: + +```no-lines +git add . +git commit -m 'Initial commit' +``` + +**Checkpoint:** `git log -1` should show the commit: + +```no-lines +git log -1 +commit 895534590fdd260acee6396e2e1c0438d1be7fed (HEAD -> main) +``` + +## 3. Heroku CLI login + +Make sure you're logged in to Heroku with the CLI: + +```no-lines +heroku login +``` + +This will allow you to deploy to Heroku from the terminal. + +**Checkpoint:** `heroku auth:whoami` should show your username: + +```no-lines +heroku auth:whoami +> your-email +``` + +## 4. Create a Heroku app + +To deploy an application to Heroku, you need to create an app. You can do so with the following command: + +```no-lines +heroku apps:create your-app-name +``` + +> **Note:** Use a unique name of your choice instead of `your-app-name`. + +**Checkpoint:** You should see the URL and the repository for your Heroku app: + +```no-lines wrap +heroku apps:create your-app-name +> Creating ⬢ your-app-name... done +> https://your-app-name.herokuapp.com/ | https://git.heroku.com/your-app-name.git +``` + +Creating the Heroku app will add the git remote Heroku created to your local repository. Pushing commits to this remote will trigger a deploy. + +**Checkpoint:** `git remote -v` should show the Heroku git remote for your application: + +```no-lines +heroku https://git.heroku.com/your-app-name.git (fetch) +heroku https://git.heroku.com/your-app-name.git (push) +``` + +If you don't see the heroku remote, use the following command to add it: + +```no-lines +heroku git:remote --app your-app-name +``` + +## 5. Add a PostgreSQL database to your application + +Heroku allows your to provision a PostgreSQL database as part of an application. + +Create the database with the following command: + +```no-lines +heroku addons:create heroku-postgresql:hobby-dev +``` + +**Checkpoint:** To verify the database was created you should see the following: + +```no-lines +Creating heroku-postgresql:hobby-dev on ⬢ your-app-name... free +Database has been created and is available + ! This database is empty. If upgrading, you can transfer + ! data from another database with pg:copy +Created postgresql-parallel-73780 as DATABASE_URL +``` + +> **Note:** Heroku automatically sets the `DATABASE_URL` environment variable when the app is running on Heroku. Prisma uses this environment variable because it's declared in the _datasource_ block of the Prisma schema (`prisma/schema.prisma`) with `env("DATABASE_URL")`. + +## 6. Push to deploy + +Deploy the app by pushing the changes to the Heroku app repository: + +```no-lines +git push heroku main +``` + +This will trigger a build and deploy your application to Heroku. Heroku will also run the `npx prisma migrate deploy` command which executes the migrations to create the database schema before deploying the app (as defined in the `release` step of the `Procfile`). + +**Checkpoint:** `git push` will emit the logs from the build and release phase and display the URL of the deployed app: + +```no-lines wrap +remote: -----> Launching... +remote: ! Release command declared: this new release will not be available until the command succeeds. +remote: Released v5 +remote: https://your-app-name.herokuapp.com/ deployed to Heroku +remote: +remote: Verifying deploy... done. +remote: Running release command... +remote: +remote: Prisma schema loaded from prisma/schema.prisma +remote: Datasource "db": PostgreSQL database "your-db-name", schema "public" at "your-db-host.compute-1.amazonaws.com:5432" +remote: +remote: 1 migration found in prisma/migrations +remote: +remote: The following migration have been applied: +remote: +remote: migrations/ +remote: └─ 20210310152103_init/ +remote: └─ migration.sql +remote: +remote: All migrations have been successfully applied. +remote: Waiting for release.... done. +``` + +> **Note:** Heroku will also set the `PORT` environment variable to which your application is bound. + +## 7. Test your deployed application + +You can use the static frontend to interact with the API you deployed via the preview URL. + +Open up the preview URL in your browser, the URL should like this: `https://APP_NAME.herokuapp.com`. You should see the following: + +![deployed-screenshot](./images/heroku-deployed.png) + +The buttons allow you to make requests to the REST API and view the response: + +- **Check API status**: Will call the REST API status endpoint that returns `{"up":true}`. +- **Seed data**: Will seed the database with a test `user` and `post`. Returns the created users. +- **Load feed**: Will load all `users` in the database with their related `profiles`. + +For more insight into Prisma Client's API, look at the route handlers in the `src/index.js` file. + +You can view the application's logs with the `heroku logs --tail` command: + +```no-lines wrap +2020-07-07T14:39:07.396544+00:00 app[web.1]: +2020-07-07T14:39:07.396569+00:00 app[web.1]: > prisma-heroku@1.0.0 start /app +2020-07-07T14:39:07.396569+00:00 app[web.1]: > node src/index.js +2020-07-07T14:39:07.396570+00:00 app[web.1]: +2020-07-07T14:39:07.657505+00:00 app[web.1]: 🚀 Server ready at: http://localhost:12516 +2020-07-07T14:39:07.657526+00:00 app[web.1]: ⭐️ See sample requests: http://pris.ly/e/ts/rest-express#3-using-the-rest-api +2020-07-07T14:39:07.842546+00:00 heroku[web.1]: State changed from starting to up +``` + +## Heroku specific notes + +There are some implementation details relating to Heroku that this guide addresses and are worth reiterating: + +- **Port binding**: web servers bind to a port so that they can accept connections. When deploying to Heroku The `PORT` environment variable is set by Heroku. Ensure you bind to `process.env.PORT` so that your application can accept requests once deployed. A common pattern is to try binding to try `process.env.PORT` and fallback to a preset port as follows: + +```js +const PORT = process.env.PORT || 3000 +const server = app.listen(PORT, () => { + console.log(`app running on port ${PORT}`) +}) +``` + +- **Database URL**: As part of Heroku's provisioning process, a `DATABASE_URL` config var is added to your app’s configuration. This contains the URL your app uses to access the database. Ensure that your `schema.prisma` file uses `env("DATABASE_URL")` so that Prisma Client can successfully connect to the database. + +## Summary + +Congratulations! You have successfully deployed a Node.js app with Prisma to Heroku. + +You can find the source code for the example in [this GitHub repository](https://github.com/prisma/prisma-examples/tree/latest/deployment-platforms/heroku). + +For more insight into Prisma Client's API, look at the route handlers in the `src/index.js` file. diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/250-deploy-to-koyeb.mdx b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/250-deploy-to-koyeb.mdx new file mode 100644 index 0000000000..6205ffddfb --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/250-deploy-to-koyeb.mdx @@ -0,0 +1,202 @@ +--- +title: 'Deploy to Koyeb' +metaTitle: 'Deploy a Prisma app to Koyeb' +metaDescription: 'Learn how to deploy a Node.js server that uses Prisma to Koyeb Serverless Platform.' +--- + + + +In this guide, you will set up and deploy a Node.js server that uses Prisma with PostgreSQL to [Koyeb](https://www.koyeb.com). The application exposes a REST API and uses Prisma Client to handle fetching, creating, and deleting records from a database. + +Koyeb is a developer-friendly serverless platform to deploy apps globally. The platform lets you seamlessly run Docker containers, web apps, and APIs with git-based deployment, TLS encryption, native autoscaling, a global edge network, and built-in service mesh & discovery. + +When using the [Koyeb git-driven deployment](https://www.koyeb.com/docs/apps/build-from-git) method, each time you push code changes to a GitHub repository a new build and deployment of the application are automatically triggered on the Koyeb Serverless Platform. +This guide uses the latter approach whereby you push your code to the app's repository on GitHub. + +The application has the following components: + +- **Backend**: Node.js REST API built with Express.js with resource endpoints that use Prisma Client to handle database operations against a PostgreSQL database (e.g., hosted on Heroku). +- **Frontend**: Static HTML page to interact with the API. + +![architecture diagram](./images/koyeb-architecture.png) + +The focus of this guide is showing how to deploy projects using Prisma to Koyeb. The starting point will be the [Prisma Koyeb example](https://github.com/koyeb/example-prisma), which contains an Express.js server with a couple of preconfigured REST endpoints and a simple frontend. + +> **Note:** The various **checkpoints** throughout the guide allow you to validate whether you performed the steps correctly. + + + +## Prerequisites + +- Hosted PostgreSQL database and a URL from which it can be accessed, e.g. `postgresql://username:password@your_postgres_db.cloud.com/db_identifier` (you can use Supabase, which offers a [free plan](https://dev.to/prisma/set-up-a-free-postgresql-database-on-supabase-to-use-with-prisma-3pk6)). +- [GitHub](https://github.com) account with an empty public repository we will use to push the code. +- [Koyeb](https://koyeb.com) account. +- Node.js installed. + +## Prisma workflow + +At the core of Prisma is the [Prisma schema](/orm/prisma-schema) – a declarative configuration where you define your data model and other Prisma-related configuration. The Prisma schema is also a single source of truth for both Prisma Client and Prisma Migrate. + +In this guide, you will create the database schema with [Prisma Migrate](/orm/prisma-migrate) to create the database schema. Prisma Migrate is based on the Prisma schema and works by generating `.sql` migration files that are executed against the database. + +Migrate comes with two primary workflows: + +- Creating migrations and applying them during local development with `prisma migrate dev` +- Applying generated migration to production with `prisma migrate deploy` + +For brevity, the guide does not cover how migrations are created with `prisma migrate dev`. Rather, it focuses on the production workflow and uses the Prisma schema and SQL migration that are included in the example code. + +You will use Koyeb's [build step](https://www.koyeb.com/docs/apps/build-from-git#understanding-the-build-process) to run the `prisma migrate deploy` command so that the migrations are applied before the application starts. + +To learn more about how migrations are created with Prisma Migrate, check out the [start from scratch guide](/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-postgresql) + +## 1. Download the example and install dependencies + +Open your terminal and navigate to a location of your choice. Create the directory that will hold the application code and download the example code: + +```no-lines wrap +mkdir prisma-on-koyeb +cd prisma-on-koyeb +curl https://github.com/koyeb/example-prisma/tarball/main/latest | tar xz --strip=1 +``` + + + +**Checkpoint:** Executing the `tree` command should show the following directories and files: + +```no-lines +. +├── README.md +├── package.json +├── prisma +│   ├── migrations +│   │   ├── 20210310152103_init +│   │   │   └── migration.sql +│   │   └── migration_lock.toml +│   └── schema.prisma +├── public +│   └── index.html +└── src + └── index.js + +5 directories, 8 files +``` + +Install the dependencies: + +```no-lines +npm install +``` + +## 2. Initialize a Git repository and push the application code to GitHub + +In the previous step, you downloaded the code. In this step, you will create a repository from the code so that you can push it to a GitHub repository for deployment. + +To do so, run `git init` from the source code folder: + +```no-lines +git init +> Initialized empty Git repository in /Users/edouardb/prisma-on-koyeb/.git/ +``` + +With the repository initialized, add and commit the files: + +```no-lines +git add . +git commit -m 'Initial commit' +``` + +**Checkpoint:** `git log -1` should show the commit: + +```no-lines +git log -1 +commit 895534590fdd260acee6396e2e1c0438d1be7fed (HEAD -> main) +``` + +Then, push the code to your GitHub repository by adding the remote + +```no-lines +git remote add origin git@github.com:/.git +git push -u origin main +``` + +## 3. Deploy the application on Koyeb + +On the [Koyeb Control Panel](https://app.koyeb.com), click the **Create App** button. + +You land on the Koyeb App creation page where you are asked for information about the application to deploy such as the deployment method to use, the repository URL, the branch to deploy, the build and run commands to execute. + +Pick GitHub as the deployment method and select the GitHub repository containing your application and set the branch to deploy to `main`. + +> **Note:** If this is your first time using Koyeb, you will be prompted to install the Koyeb app in your GitHub account. + +In the **Environment variables** section, create a new environment variable `DATABASE_URL` that is type Secret. In the value field, click **Create Secret**, name your secret `prisma-pg-url` and set the PostgreSQL database connection string as the secret value which should look as follows: `postgresql://__USER__:__PASSWORD__@__HOST__/__DATABASE__`. +[Koyeb Secrets](https://www.koyeb.com/docs/secrets) allow you to securely store and retrieve sensitive information like API tokens, database connection strings. They enable you to secure your code by removing hardcoded credentials and let you pass environment variables securely to your applications. + +Last, give your application a name and click the **Create App** button. + +**Checkpoint:** Open the deployed app by clicking on the screenshot of the deployed app. Once the page loads, click on the **Check API status** button, which should return: `{"up":true}` + +![deployed-screenshot](./images/koyeb-app-creation.png) + +Congratulations! You have successfully deployed the app to Koyeb. + +Koyeb will build and deploy the application. Additional commits to your GitHub repository will trigger a new build and deployment on Koyeb. + +**Checkpoint:** Once the build and deployment are completed, you can access your application by clicking the App URL ending with koyeb.app in the Koyeb control panel. Once on the app page loads, Once the page loads, click on the **Check API status** button, which should return: `{"up":true}` + +## 4. Test your deployed application + +You can use the static frontend to interact with the API you deployed via the preview URL. + +Open up the preview URL in your browser, the URL should like this: `https://APP_NAME-ORG_NAME.koyeb.app`. You should see the following: + +![deployed-screenshot](./images/koyeb-deployed.png) + +The buttons allow you to make requests to the REST API and view the response: + +- **Check API status**: Will call the REST API status endpoint that returns `{"up":true}`. +- **Seed data**: Will seed the database with a test `user` and `post`. Returns the created users. +- **Load feed**: Will load all `users` in the database with their related `profiles`. + +For more insight into Prisma Client's API, look at the route handlers in the `src/index.js` file. + +You can view the application's logs clicking the `Runtime logs` tab on your app service from the Koyeb control panel: + +```no-lines wrap +node-72d14691 stdout > prisma-koyeb@1.0.0 start +node-72d14691 stdout > node src/index.js +node-72d14691 stdout 🚀 Server ready at: http://localhost:8080 +node-72d14691 stdout ⭐️ See sample requests: http://pris.ly/e/ts/rest-express#3-using-the-rest-api +``` + +## Koyeb specific notes + +### Build + +By default, for applications using the Node.js runtime, if the `package.json` contains a `build` script, Koyeb automatically executes it after the dependencies installation. +In the example, the `build` script is used to run `prisma generate && prisma migrate deploy && next build`. + +### Deployment + +By default, for applications using the Node.js runtime, if the `package.json` contains a `start` script, Koyeb automatically executes it to launch the application. +In the example, the `start` script is used to run `node src/index.js`. + +### Database migrations and deployments + +In the example you deployed, migrations are applied using the `prisma migrate deploy` command during the Koyeb build (as defined in the `build` script in `package.json`). + +### Additional notes + +In this guide, we kept pre-set values for the region, instance size, and horizontal scaling. You can customize them according to your needs. + +> **Note:** The Ports section is used to let Koyeb know which port your application is listening to and properly route incoming HTTP requests. A default `PORT` environment variable is set to `8080` and incoming HTTP requests are routed to the `/` path when creating a new application. +> If your application is listening on another port, you can define another port to route incoming HTTP requests. + +## Summary + +Congratulations! You have successfully deployed a Node.js app with Prisma to Koyeb. + +You can find the source code for the example in [this GitHub repository](https://github.com/koyeb/example-prisma). + +For more insight into Prisma Client's API, look at the route handlers in the `src/index.js` file. diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-architecture.png b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-architecture.png new file mode 100644 index 0000000000..f501852e4c Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-architecture.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-deployed.png b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-deployed.png new file mode 100644 index 0000000000..fb52deba4b Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/heroku-deployed.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-app-creation.png b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-app-creation.png new file mode 100644 index 0000000000..87aeeacbf9 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-app-creation.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-architecture.png b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-architecture.png new file mode 100644 index 0000000000..c9b98c4707 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-architecture.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-deployed.png b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-deployed.png new file mode 100644 index 0000000000..29c3b606b1 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/images/koyeb-deployed.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/101-traditional/index.mdx b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/index.mdx new file mode 100644 index 0000000000..145b8aa5a9 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/101-traditional/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Traditional servers' +metaTitle: 'Deploy Prisma apps to traditional (PaaS) servers' +metaDescription: 'Learn how to deploy your Prisma-backed apps to PaaS providers like Heroku, Koyeb, or AWS EC2' +tocDepth: 2 +--- + + + +If your application is deployed via a Platform-as-a-Service (PaaS) provider, whether containerized or not, it is a traditionally-deployed app. Common deployment examples include [Heroku](/orm/prisma-client/deployment/traditional/deploy-to-heroku) and [Koyeb](/orm/prisma-client/deployment/traditional/deploy-to-koyeb). + + + +## Traditional (PaaS) guides + + diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/150-deploy-to-azure-functions.mdx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/150-deploy-to-azure-functions.mdx new file mode 100644 index 0000000000..5e0120f978 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/150-deploy-to-azure-functions.mdx @@ -0,0 +1,40 @@ +--- +title: 'Deploy to Azure Functions' +metaTitle: 'How to deploy an app using Prisma to Azure Functions' +metaDescription: 'Learn how to deploy a Prisma based REST API to Azure Functions and connect to an Azure SQL database' +--- + + + +This guide explains how to avoid common issues when deploying a Node.js-based function app to Azure using [Azure Functions](https://azure.microsoft.com/en-us/services/functions/). + +Azure Functions is a serverless deployment platform. You do not need to maintain infrastructure to deploy your code. With Azure Functions, the fundamental building block is the [function app](https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference#function-app). A function app provides an execution context in Azure in which your functions run. It is comprised of one or more individual functions that Azure manages, deploys, and scales together. You can organize and collectively manage multiple functions as a single logical unit. + + + +## Prerequisites + +- An existing function app project with Prisma + +## Things to know + +While Prisma works well with Azure functions, there are a few things to take note of before deploying your application. + +### Define multiple binary targets + +When deploying a function app, the operating system that Azure functions runs a remote build is different from the one used to host your functions. Therefore, we recommend specifying the following [`binaryTargets` options](/orm/reference/prisma-schema-reference#binarytargets-options) in your Prisma schema: + +```prisma file=schema.prisma highlight=3;normal +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "debian-openssl-1.1.x"] +} +``` + +### Connection pooling + +Generally, when you use a FaaS (Function as a Service) environment to interact with a database, every function invocation can result in a new connection to the database. This is not a problem with a constantly running Node.js server. Therefore, it is beneficial to pool DB connections to get better performance. To solve this issue, you can use the [Prisma Accelerate](/accelerate). For other solutions, see the [connection management guide for serverless environments](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas). + +## Summary + +For more insight into Prisma Client's API, explore the function handlers and check out the [Prisma Client API Reference](/orm/reference/prisma-client-reference) diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/300-deploy-to-vercel.mdx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/300-deploy-to-vercel.mdx new file mode 100644 index 0000000000..f62f86db43 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/300-deploy-to-vercel.mdx @@ -0,0 +1,80 @@ +--- +title: 'Deploy to Vercel' +metaTitle: 'Deploy to Vercel' +metaDescription: 'Learn how to deploy a Next.js application based on Prisma Client to Vercel.' +--- + + + +This guide takes you through the steps to set up and deploy a serverless application that uses Prisma to [Vercel](https://vercel.com/). + +Vercel is a cloud platform that hosts static sites, serverless, and edge functions. You can integrate a Vercel project with a GitHub repository to allow you to deploy automatically when you make new commits. + +We created an [example application](https://github.com/prisma/deployment-example-vercel) using Next.js you can use as a reference when deploying an application using Prisma to Vercel. + +While our examples use Next.js, you can deploy other applications to Vercel. See [Using Express with Vercel](https://vercel.com/guides/using-express-with-vercel) and [Nuxt on Vercel](https://vercel.com/docs/frameworks/nuxt) as examples of other options. + + + +## Generate Prisma during build + +Vercel will automatically cache dependencies on deployment. For most applications, this will not cause any issues. However, for Prisma, it may result in an outdated version of Prisma Client on a change in your Prisma schema. To avoid this issue, add `prisma generate` to the `postinstall` script of your application: + +```json file=package.json highlight=4;add +{ + ... + "scripts" { + "postinstall": "prisma generate" + } + ... +} +``` + +This will re-generate Prisma Client at build time so that your deployment always has an up-to-date client. + + + +If you see `prisma: command not found` errors during your deployment to Vercel, you are missing `prisma` in your dependencies. By default, `prisma` is a dev dependency and may need to be moved to be a standard dependency. + + + +Another option to avoid an outdated Prisma Client is to use [a custom output path](/orm/prisma-client/setup-and-configuration/generating-prisma-client#using-a-custom-output-path) and check your client into version control. This way each deployment is guaranteed to include the correct Prisma Client. + +```prisma file=schema.prisma highlight=3;add +generator client { + provider = "prisma-client-js" + output = "./generated/client" +} +``` + +## Add a separate database for preview deployments + +By default, your application will have a single _production_ environment associated with the `main` git branch of your repository. If you open a pull request to change your application, Vercel creates a new _preview_ environment. + +Vercel uses the `DATABASE_URL` environment variable you define when you import the project for both the production and preview environments. This causes problems if you create a pull request with a database schema migration because the pull request will change the schema of the production database. + +To prevent this, use a _second_ hosted database to handle preview deployments. Once you have that connection string, you can add a `DATABASE_URL` for your preview environment using the Vercel dashboard: + +1. Click the **Settings** tab of your Vercel project. + +2. Click **Environment variables**. + +3. Add an environment variable with a key of `DATABASE_URL` and select only the **Preview** environment option: + + ![Add an environment variable for the preview environment](./images/300-60-deploy-to-vercel-preview-environment-variable.png) + +4. Set the value to the connection string of your second database: + + ```code + postgresql://dbUsername:dbPassword@myhost:5432/mydb + ``` + +5. Click **Save**. + +## Connection pooling + +When you use a Function-as-a-Service provider, like Vercel Serverless functions, every invocation may result in a new connection to your database. This can cause your database to quickly run out of open connections and cause your application to stall. For this reason, pooling connections to your database is essential. + +You can use [Accelerate](/accelerate) for connection pooling, to reduce your Prisma Client bundle size, and to avoid cold starts. + +For more information on connection management for serverless environments, refer to our [connection management guide](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas). diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/400-deploy-to-aws-lambda.mdx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/400-deploy-to-aws-lambda.mdx new file mode 100644 index 0000000000..fdad2c298a --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/400-deploy-to-aws-lambda.mdx @@ -0,0 +1,325 @@ +--- +title: 'Deploy to AWS Lambda' +metaTitle: 'Deploy your application using Prisma to AWS Lambda' +metaDescription: 'Learn how to deploy your Prisma-backed applications to AWS Lambda with AWS SAM, Serverless Framework, or SST' +tocDepth: 3 +--- + + + +This guide explains how to avoid common issues when deploying a project using Prisma to [AWS Lambda](https://aws.amazon.com/lambda/). + +While a deployment framework is not required to deploy to AWS Lambda, this guide covers deploying with: + +- [AWS Serverless Application Model (SAM)](https://aws.amazon.com/serverless/sam/) is an open-source framework from AWS that can be used in the creation of serverless applications. AWS SAM includes the [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html#serverless-sam-cli), which you can use to build, test, and deploy your application. +- [Serverless Framework](https://www.serverless.com/framework/) provides a CLI that helps with workflow automation and AWS resource provisioning. While Prisma works well with the Serverless Framework "out of the box", there are a few improvements that can be made within your project to ensure a smooth deployment and performance. There is also additional configuration that is needed if you are using the [`serverless-webpack`](https://www.npmjs.com/package/serverless-webpack) or [`serverless-bundle`](https://www.npmjs.com/package/serverless-bundle) libraries. +- [SST](https://sst.dev/) provides tools that make it easy for developers to define, test, debug, and deploy their applications. Prisma works well with SST but must be configured so that your schema is correctly packaged by SST. + + + +## General considerations when deploying to AWS Lambda + +This section covers changes you will need to make to your application, regardless of framework. After following these steps, follow the steps for your framework. + +- [Deploying with AWS SAM](#deploying-with-aws-sam) +- [Deploying with the Serverless Framework](#deploying-with-the-serverless-framework) +- [Deploying with SST](#deploying-with-sst) + +### Define binary targets in Prisma Schema + +The Prisma schema should contain the following in the `generator` block: + +```prisma +binaryTargets = ["native", "rhel-openssl-1.0.x"] +``` + +This is necessary because the runtimes used in development and deployment differ. Add the [`binaryTarget`](/orm/reference/prisma-schema-reference#binarytargets-options) to make the compatible Prisma engine file available. + +#### Lambda functions with arm64 architectures + +Lambda functions that use [arm64 architectures (AWS Graviton2 processor)](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html#foundation-arch-adv) must use an `arm64` precompiled engine file. + +In the `generator` block of your `schema.prisma` file, add the following: + +```prisma file=schema.prisma +binaryTargets = ["native", "linux-arm64-openssl-1.0.x"] +``` + +### Prisma CLI binary targets + +While we do not recommend running migrations within AWS Lambda, some applications will require it. In these cases, you can use the [PRISMA_CLI_BINARY_TARGETS](/orm/reference/environment-variables-reference#prisma_cli_binary_targets) environment variable to make sure that Prisma CLI commands, including `prisma migrate`, have access to the correct schema engine. + +In the case of AWS lambda, you will have to add the following environment variable: + +```env file=.env +PRISMA_CLI_BINARY_TARGETS=native,rhel-openssl-1.0.x +``` + + + +`prisma migrate` is a command in the `prisma` package. Normally, this package is installed as a dev dependency. Depending on your setup, you may need to install this package as a dependency instead so that it is included in the bundle or archive that is uploaded to Lambda and executed. + + + +### Connection pooling + +Generally, when you use a Function as a Service (FaaS) environment to interact with a database, every function invocation can result in a new connection to the database. This is not a problem with a constantly running Node.js server. Therefore, it is beneficial to pool database connections to get better performance. You can use [Accelerate](/accelerate) to solve this issue. For other solutions, see the [connection management guide for serverless environments](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas). + +## Deploying with AWS SAM + +### Loading environment variables + +AWS SAM does not directly support loading values from a `.env` file. You will have to use one of AWS's services to store and retrieve these parameters. [This guide](https://medium.com/bip-xtech/a-practical-guide-to-surviving-aws-sam-d8ab141b3d25) provides a great overview of your options and how to store and retrieve values in Parameters, SSM, Secrets Manager, and more. + +### Loading required files + +AWS SAM uses [esbuild](https://esbuild.github.io/) to bundle your TypeScript code. However, the full esbuild API is not exposed and esbuild plugins are not supported. This leads to problems when using Prisma in your application as certain files (like `schema.prisma`) must be available at runtime. + +To get around this, you need to directly reference the needed files in your code to bundle them correctly. In your application, you could add the following lines to your application where Prisma is instantiated. + +```ts file=app.ts +import schema from './prisma/schema.prisma' +import x from './node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node' + +if (process.env.NODE_ENV !== 'production') { + console.debug(schema, x) +} +``` + +You will also need to define how to bundle these files with esbuild by adding the following lines to `Metadata.BuildProperties` in your `template.yaml`: + +```yaml file=template.yaml +Loader: + - .prisma=file + - .so.node=file +AssetNames: '[name]' +``` + +This will make sure that files needed by Prisma will be included in the AWS SAM build. + +## Deploying with the Serverless Framework + +### Loading environment variables via a `.env` file + +Your functions will need the `DATABASE_URL` environment variable to access the database. The `serverless-dotenv-plugin` will allow you to use your `.env` file in your deployments. + +First, make sure that the plugin is installed: + +```terminal +npm install -D serverless-dotenv-plugin +``` + +Then, add `serverless-dotenv-plugin` to your list of plugins in `serverless.yml`: + +```code file=serverless.yml no-copy +plugins: + - serverless-dotenv-plugin +``` + +The environment variables in your `.env` file will now be automatically loaded on package or deployment. + + + + +```terminal +serverless package +``` + + + + +```terminal no-copy +Running "serverless" from node_modules +DOTENV: Loading environment variables from .env: + - DATABASE_URL + +Packaging deployment-example-sls for stage dev (us-east-1) +. +. +. +``` + + + + +### Deploy only the required files + +To reduce your deployment footprint, you can update your deployment process to only upload the files your application needs. The Serverless configuration file, `serverless.yml`, below shows a `package` pattern that includes only the Prisma engine file relevant to the Lambda runtime and excludes the others. This means that when Serverless Framework packages your app for upload, it includes only one engine file. This ensures the packaged archive is as small as possible. + +```code file=serverless.yml no-copy +package: + patterns: + - '!node_modules/.prisma/client/libquery_engine-*' + - 'node_modules/.prisma/client/libquery_engine-rhel-*' + - '!node_modules/prisma/libquery_engine-*' + - '!node_modules/@prisma/engines/**' +``` + +If you are deploying to [Lambda functions with ARM64 architecture](#lambda-functions-with-arm64-architectures) you should update the Serverless configuration file to package the `arm64` engine file, as follows: + +```code file=serverless.yml highlight=4;normal +package: + patterns: + - '!node_modules/.prisma/client/libquery_engine-*' + - 'node_modules/.prisma/client/libquery_engine-linux-arm64-*' + - '!node_modules/prisma/libquery_engine-*' + - '!node_modules/@prisma/engines/**' +``` + +If you use `serverless-webpack`, see [Deployment with serverless webpack](#deployment-with-serverless-webpack) below. + +### Deployment with `serverless-webpack` + +If you use `serverless-webpack`, you will need additional configuration so that your `schema.prisma` is properly bundled. You will need to: + +1. Copy your `schema.prisma` with [`copy-webpack-plugin`](https://www.npmjs.com/package/copy-webpack-plugin). +2. Run `prisma generate` via `custom > webpack > packagerOptions > scripts` in your `serverless.yml`. +3. Only package the correct Prisma engine file to save more than 40mb of capacity. + +#### 1. Install webpack specific dependencies + +First, ensure the following webpack dependencies are installed: + +```terminal +npm install --save-dev webpack webpack-node-externals copy-webpack-plugin serverless-webpack +``` + +#### 2. Update `webpack.config.js` + +In your `webpack.config.js`, make sure that you set `externals` to `nodeExternals()` like the following: + +```javascript file=webpack.config.js highlight=1,5;normal; +const nodeExternals = require('webpack-node-externals') + +module.exports = { + // ... other configuration + externals: [nodeExternals()], + // ... other configuration +} +``` + +Update the `plugins` property in your `webpack.config.js` file to include the `copy-webpack-plugin`: + +```javascript file=webpack.config.js highlight=2,7-13;normal; +const nodeExternals = require('webpack-node-externals') +const CopyPlugin = require('copy-webpack-plugin') + +module.exports = { + // ... other configuration + externals: [nodeExternals()], + plugins: [ + new CopyPlugin({ + patterns: [ + { from: './node_modules/.prisma/client/schema.prisma', to: './' }, // you may need to change `to` here. + ], + }), + ], + // ... other configuration +} +``` + +This plugin will allow you to copy your `schema.prisma` file into your bundled code. Prisma requires that your `schema.prisma` be present in order make sure that queries are encoded and decoded according to your schema. In most cases, bundlers will not include this file by default and will cause your application to fail to run. + + + +Depending on how your application is bundled, you may need to copy the schema file to a location other than `./`. Use the `serverless package` command to package your code locally so you can review where your schema should be put. + + + +Refer to the [Serverless Webpack documentation](https://www.serverless.com/plugins/serverless-webpack) for additional configuration. + +#### 3. Update `serverless.yml` + +In your `serverless.yml` file, make sure that the `custom > webpack` block has `prisma generate` under `packagerOptions > scripts` as follows: + +```yaml file=serverless.yml +custom: + webpack: + packagerOptions: + scripts: + - prisma generate +``` + +This will ensure that, after webpack bundles your code, the Prisma Client is generated according to your schema. Without this step, your app will fail to run. + +Lastly, you will want to exclude [Prisma query engines](/orm/more/under-the-hood/engines) that do not match the AWS Lambda runtime. Update your `serverless.yml` by adding the following script that makes sure only the required query engine, `rhel-openssl-1.0.x`, is included in the final packaged archive. + +```yaml file=serverless.yml highlight=6;add +custom: + webpack: + packagerOptions: + scripts: + - prisma generate + -- find . -name "libquery_engine-*" -not -name "libquery_engine-rhel-openssl-*" | xargs rm +``` + +If you are deploying to [Lambda functions with ARM64 architecture](#lambda-functions-with-arm64-architectures) you should update the `find` command to the following: + +```yaml file=serverless.yml highlight=6;add +custom: + webpack: + packagerOptions: + scripts: + - prisma generate + -- find . -name "libquery_engine-*" -not -name "libquery_engine-arm64-openssl-*" | xargs rm +``` + +#### 4. Wrapping up + +You can now re-package and re-deploy your application. To do so, run `serverless deploy`. Webpack output will show the schema file being moved with `copy-webpack-plugin`: + + + + +```terminal +serverless package +``` + + + + +```terminal no-copy +Running "serverless" from node_modules +DOTENV: Loading environment variables from .env: + - DATABASE_URL + +Packaging deployment-example-sls for stage dev (us-east-1) + +asset handlers/posts.js 713 bytes [emitted] [minimized] (name: handlers/posts) + asset schema.prisma 293 bytes [emitted] [from: node_modules/.prisma/client/schema.prisma] [copied] + ./handlers/posts.ts 745 bytes [built] [code generated] + external "@prisma/client" 42 bytes [built] [code generated] + webpack 5.88.2 compiled successfully in 685 ms +Package lock found - Using locked versions +Packing external modules: @prisma/client@^5.1.1 + +✔ Service packaged (5s) +``` + + + + +## Deploying with SST + +### Working with environment variables + +While SST supports `.env` files, [it is not recommended](https://docs.sst.dev/config#should-i-use-configsecret-or-env-for-secrets). SST recommends using `Config` to access these environment variables in a secure way. + +The SST guide [available here](https://docs.sst.dev/config#overview) is a step-by-step guide to get started with `Config`. Assuming you have created a new secret called `DATABASE_URL` and have [bound that secret to your app](https://docs.sst.dev/config#bind-the-config), you can set up `PrismaClient` with the following: + +```ts file=prisma.ts +import { PrismaClient } from '@prisma/client' +import { Config } from 'sst/node/config' + +const globalForPrisma = global as unknown as { prisma: PrismaClient } + +export const prisma = + globalForPrisma.prisma || + new PrismaClient({ + datasourceUrl: Config.DATABASE_URL, + }) + +if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma + +export default prisma +``` diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/500-deploy-to-netlify.mdx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/500-deploy-to-netlify.mdx new file mode 100644 index 0000000000..9600aa265d --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/500-deploy-to-netlify.mdx @@ -0,0 +1,86 @@ +--- +title: 'Deploy to Netlify' +metaTitle: 'Deploy to Netlify' +metaDescription: 'Learn how to deploy Node.js and TypeScript applications that are using Prisma Client to Netlify.' +--- + + + +This guide covers the steps you will need to take in order to deploy your application that uses Prisma to [Netlify](https://www.netlify.com/). + +Netlify is a cloud platform for continuous deployment, static sites, and serverless functions. Netlify integrates seamlessly with GitHub for automatic deployments upon commits. When you follow the steps below, you will use that approach to create a CI/CD pipeline that deploys your application from a GitHub repository. + + + +## Prerequisites + +Before you can follow this guide, you will need to set up your application to begin deploying to Netlify. We recommend the ["Get started with Netlify"](https://docs.netlify.com/get-started/) guide for a quick overview and ["Deploy functions"](https://docs.netlify.com/functions/deploy/?fn-language=ts) for an in-depth look at your deployment options. + +## Binary targets in `schema.prisma` + +Since your code is being deployed to Netlify's environment, which isn't necessarily the same as your development environment, you will need to set [`binaryTargets`](/orm/reference/prisma-schema-reference#binarytargets-options) in order to download the query engine that is compatible with the Netlify runtime during your build step. If you do not set this option, your deployed code will have an incorrect query engine deployed with it and will not function. + +You should update your Prisma schema to contain the following in the `generator` block: + +```prisma +binaryTargets = ["native", "rhel-openssl-1.0.x"] +``` + +## Store environment variables in Netlify + +We recommend keeping `.env` files in your `.gitignore` in order to prevent leakage of sensitives connection strings. Instead, you can use the Netlify CLI to [import values into netlify directly](https://docs.netlify.com/environment-variables/get-started/#import-variables-with-the-netlify-cli). + +Assuming you have a file like the following: + +```env file=.env +# Connect to DB +DATABASE_URL="postgresql://postgres:__PASSWORD__@__HOST__:__PORT__/__DB_NAME__" +``` + +You can upload the file as environment variables using the `env:import` command + +```terminal no-break-terminal +❯ netlify env:import .env +site: my-very-very-cool-site +---------------------------------------------------------------------------------. + Imported environment variables | +---------------------------------------------------------------------------------| + Key | Value | +--------------|------------------------------------------------------------------| + DATABASE_URL | postgresql://postgres:__PASSWORD__@__HOST__:__PORT__/__DB_NAME__ | +---------------------------------------------------------------------------------' +``` + +
+If you are not using an `.env` file + +If you are storing your database connection string and other environment variables in a different method, you will need to manually upload your environment variables to Netlify. These options are [discussed in Netlfiy's documentation](https://docs.netlify.com/environment-variables/get-started/) and one method, uploading via the UI, is described below. + +1. Open the Netlify admin UI for the site. You can use Netlify CLI as follows: + ```terminal + netlify open --admin + ``` +2. Click **Site settings**: + ![Netlify admin UI](images/500-06-deploy-to-netlify-site-settings.png) +3. Navigate to **Build & deploy** in the sidebar on the left and select **Environment**. +4. Click **Edit variables** and create a variable with the key `DATABASE_URL` and set its value to your database connection string. + ![Netlify environment variables](images/500-07-deploy-to-netlify-environment-variables-settings.png) +5. Click **Save**. + +
+ +Now start a new Netlify build and deployment so that the new build can use the newly uploaded environment variables. + +```terminal no-copy +netlify deploy +``` + +You can now test the deployed application. + +## Connection pooling + +When you use a Function-as-a-Service provider, like Netlify, it is beneficial to pool database connections for performance reasons. This is because every function invocation may result in a new connection to your database which can quickly run out of open connections. + +You can use [Accelerate](/accelerate) for connection pooling, to reduce your Prisma Client bundle size, and to avoid cold starts. + +For more information on connection management for serverless environments, refer to our [connection management guide](/orm/prisma-client/setup-and-configuration/databases-connections#serverless-environments-faas). diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-00-deploy-to-vercel-architecture.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-00-deploy-to-vercel-architecture.png new file mode 100644 index 0000000000..87cab2d012 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-00-deploy-to-vercel-architecture.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.png new file mode 100644 index 0000000000..affde53fdf Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.snagx new file mode 100644 index 0000000000..f866b57bff Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-10-deploy-to-vercel-deploy-button.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.png new file mode 100644 index 0000000000..111f6a9ce2 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.snagx new file mode 100644 index 0000000000..0feecdb56b Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-20-deploy-to-vercel-select-github.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.png new file mode 100644 index 0000000000..a4966e9bc7 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.snagx new file mode 100644 index 0000000000..8125ce49b4 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-30-deploy-to-vercel-create-git-repo.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.png new file mode 100644 index 0000000000..84f9f2117e Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.snagx new file mode 100644 index 0000000000..ebdf0f53d1 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-40-deploy-to-vercel-configure-project.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.png new file mode 100644 index 0000000000..a0dabfc0c3 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.snagx new file mode 100644 index 0000000000..8f7c5c7095 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-50-deploy-to-vercel-success.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.png new file mode 100644 index 0000000000..9d107a1046 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.snagx new file mode 100644 index 0000000000..409dae8ce8 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-60-deploy-to-vercel-preview-environment-variable.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.png new file mode 100644 index 0000000000..c004aab78c Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.snagx new file mode 100644 index 0000000000..e27fb6a167 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/300-70-deploy-to-vercel-environment-variables.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-01-deploy-to-netlify-architecture.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-01-deploy-to-netlify-architecture.png new file mode 100644 index 0000000000..f332cc8d3b Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-01-deploy-to-netlify-architecture.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.png new file mode 100644 index 0000000000..cb7ce5c427 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.snagx new file mode 100644 index 0000000000..6851b49d96 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-02-deploy-to-netlify-example-repo-click-fork.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.png new file mode 100644 index 0000000000..50dbe8d37f Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.snagx new file mode 100644 index 0000000000..9d97a156f3 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-03-deploy-to-netlify-example-repo-create-fork-page.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.png new file mode 100644 index 0000000000..90fb67a218 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.snagx new file mode 100644 index 0000000000..e5338f1afb Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-04-deploy-to-netlify-copy-supabase-connection-string.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.png new file mode 100644 index 0000000000..2989a7c882 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.snagx new file mode 100644 index 0000000000..2267e16b13 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-05-deploy-to-netlify-netlify-init-configure-site.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.png new file mode 100644 index 0000000000..9ddf171eca Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.snagx new file mode 100644 index 0000000000..f9ae9c525d Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-06-deploy-to-netlify-site-settings.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.png new file mode 100644 index 0000000000..97f8d42ae8 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.snagx new file mode 100644 index 0000000000..64d550ac63 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-07-deploy-to-netlify-environment-variables-settings.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.png new file mode 100644 index 0000000000..050c85a379 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.snagx new file mode 100644 index 0000000000..941c2310b7 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-08-deploy-to-netlify-application-deployed.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.png b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.png new file mode 100644 index 0000000000..c86a0a2bed Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.snagx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.snagx new file mode 100644 index 0000000000..936008bc12 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/images/500-09-deploy-to-netlify-application-deployed-call-result.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/201-serverless/index.mdx b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/index.mdx new file mode 100644 index 0000000000..7aa4a9f808 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/201-serverless/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Serverless functions' +metaTitle: 'Deploy Prisma apps to serverless function (FaaS) providers' +metaDescription: 'Learn how to deploy your Prisma-backed apps to FaaS providers like AWS Lambda, Netlify, or Vercel Serverless Functions' +tocDepth: 2 +--- + + + +If your application is deployed via a "Serverless Function" or "Function-as-a-Service (FaaS)" offering and uses a standard Node.js runtime, it is a serverless app. Common deployment examples include [AWS Lambda](/orm/prisma-client/deployment/serverless/deploy-to-aws-lambda) and [Vercel Serverless Functions](/orm/prisma-client/deployment/serverless/deploy-to-vercel). + + + +## Guides for Serverless Function providers + + diff --git a/docs/200-orm/200-prisma-client/500-deployment/210-module-bundlers.mdx b/docs/200-orm/200-prisma-client/500-deployment/210-module-bundlers.mdx new file mode 100644 index 0000000000..0452fc3d45 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/210-module-bundlers.mdx @@ -0,0 +1,19 @@ +--- +title: 'Module bundlers' +metaTitle: 'Module bundlers (Reference)' +metaDescription: 'This page gives an overview of the most important things to be aware of when using a module bundler to bundle an application that uses Prisma Client.' +--- + +## Overview + +_Module bundlers_ bundle JavaScript modules into a single JavaScript file. Most bundlers work by copying over the JavaScript code from a variety of source files into the target file. + +Since Prisma Client is not only based on JavaScript code, but also relies on the [**query engine binary file**](/orm/more/under-the-hood/engines#the-query-engine-file) to be available, you need to make sure that your bundled code has access to the binary file. + +To do so, you can use plugins that let you copy over static assets: + +| Bundler | Plugin | +| :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- | +| Webpack | [`copy-webpack-plugin`](https://github.com/webpack-contrib/copy-webpack-plugin#copy-webpack-plugin) | +| Webpack (with [Next.js monorepo](/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-monorepo)) | [`nextjs-monorepo-workaround-plugin`](https://www.npmjs.com/package/@prisma/nextjs-monorepo-workaround-plugin) | +| Parcel | [`parcel-plugin-static-files-copy`](https://github.com/elwin013/parcel-plugin-static-files-copy#readme) | diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/450-deploy-to-cloudflare-workers.mdx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/450-deploy-to-cloudflare-workers.mdx new file mode 100644 index 0000000000..42dffa603a --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/301-edge/450-deploy-to-cloudflare-workers.mdx @@ -0,0 +1,309 @@ +--- +title: 'Deploy to Cloudflare Workers' +metaTitle: 'Deploy to Cloudflare Workers' +metaDescription: 'Learn how to deploy a TypeScript application to Cloudflare Workers that connects to PostgreSQL.' +--- + + + +Today you'll be deploying a Cloudflare Worker that uses Prisma to save every request to a PostgreSQL database and fetches 20 of the most recent logs. + +This guide covers Prisma, TypeScript, PostgreSQL, Prisma Accelerate, and Cloudflare Workers. + + + +## Prerequisites + +- A PostgreSQL database that is publicly accessible +- [Cloudflare Workers](https://workers.cloudflare.com/) account +- [Prisma Data Platform](https://console.prisma.io/) account +- Node.js & npm installed +- Git installed + +## 1. Set up your application + +Wrangler is the official Cloudflare Worker CLI. You will use it to develop and deploy to Cloudflare Workers. This guide uses [Wrangler v3](https://developers.cloudflare.com/workers/wrangler/). + +Open your terminal and navigate to a location of your choice. First, initialize your project using the [create-cloudflare-cli](https://www.npmjs.com/package/create-cloudflare). To do this, run the following command in your terminal: + +```terminal +npm create cloudflare@latest +``` + +This will ask you a few questions. + +```terminal +In which directory do you want to create your application? +``` + +Enter the name of your project, for example: `prisma-cloudflare-accelerate` + +```terminal +What type of application do you want to create? +``` + +Select the `"Hello World" Worker` option. + +```terminal +Would you like to use TypeScript? (y/n) +``` + +We also want to use TypeScript, so answer yes. + +```terminal +Would you like to use git to manage this Worker? (y/n) +``` + +We want to use Git, so answer yes. + +The command this will create a new project with a minimal preset configuration. Once `create-cloudflare-cli` is done, navigate to the project and open it on your editor of choice. + +Next, authenticate the Wrangler CLI with your Cloudflare Workers account. To do this, run the following command in your terminal: + +```terminal +npx wrangler login +``` + +You can now verify that you're logged in by running `npx wrangler whoami`. + +```terminal +npx wrangler whoami +``` + +## 2. Set up Prisma + +Now you're ready to add Prisma to the project. + +Install `prisma` as a development dependency: + +```terminal +npm install --save-dev prisma +``` + +Next, initialize Prisma in your project with the following command: + +```terminal +npx prisma init +``` + +This creates a Prisma schema in `prisma/schema.prisma`. + + + +**Note:**

+ +`prisma init` also creates an `.env` file. The `.env` file will contain a placeholder `DATABASE_URL` variable that will be used to update your database schema using Prisma Migrate. Update this value with your database's connection string. + +
+ +Update your Prisma schema with the following data model: + +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Log { + id Int @id @default(autoincrement()) + level Level + message String + meta Json +} + +enum Level { + Info + Warn + Error +} +``` + +The above data model will be used to persist and retrieve logs from your Cloudflare Worker + +## 3. Update your database schema + +To map your data model to the database schema, you need to use the `prisma migrate dev` CLI command: + +```terminal +npx prisma migrate dev --name init +``` + +The command does two things: + +1. It creates a new SQL migration file for this migration +1. It runs the SQL migration file against the database + +## 4. Enable Accelerate in the Prisma Data Platform + +Prisma currently does not work on Cloudflare Workers yet. However, you can use Prisma on Cloudflare Workers through [Prisma Accelerate](/accelerate). + +To get started with Prisma Accelerate: + +1. Sign up for a free [Prisma Data Platform account](https://console.prisma.io/) +1. Create a project +1. Navigate to the project you created +1. Enable Accelerate +1. Generate an Accelerate connection string and copy it to your clipboard + +## 5. Configure the Accelerate connection string in your project + +1. Rename the existing `DATABASE_URL` environment variable to `DIRECT_URL`. The `DIRECT_URL` variable will be used perform migrations and introspections. +1. Add the Prisma Accelerate connection string to your `.env` file. + + ```diff file=.env + -DATABASE_URL="postgres://..." + +DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=__API_KEY__" + +DIRECT_URL="postgres://..." + ``` + + Add [`directUrl`](/orm/reference/prisma-schema-reference#fields) property in `datasource` block in the `schema.prisma` file. + + ```prisma highlight=4;add file=prisma/schema.prisma + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_URL") + } + ``` + + > The `directUrl` field is not required for Prisma Accelerate. It allows you to introspect and perform schema migrations. + +1. In `wrangler.toml` file, add a `[vars]` key and your connection string. + + ```diff file=wrangler.toml + name = "prisma-cloudflare-accelerate" + main = "src/main.ts" + compatibility_date = "2022-11-07" + + + [vars] + + DATABASE_URL = "prisma://accelerate.prisma-data.net/?api_key=__API_KEY__" + ``` + + + + Cloudflare Workers does not support `.env` files. To set environment variables, you can either adding the `[vars]` key in your `wrangler.toml` file or saving your environment variables in a `.dev.vars` file. Refer to [Cloudflare's documentation](https://developers.cloudflare.com/pages/platform/functions/bindings/#interact-with-your-environment-variables-locally) to learn more. + + + +1. Install the Prisma Accelerate extension + + ```bash + npm install @prisma/extension-accelerate + ``` + +You are now ready to generate a Prisma Client. + +## 6. Generate a Prisma Client + +Next, generate Prisma Client that connects to your database through [Prisma Accelerate](/accelerate) over HTTP. + +```terminal +npx prisma generate --no-engine +``` + + + +The `--no-engine` flag is available from Prisma 5.2.0 and later. If you're using an earlier version of Prisma, use the `--accelerate` flag. + +```terminal +npx prisma generate --accelerate +``` + + + +The generated Client has a smaller bundle size and is optimized for edge environments like Cloudflare Workers. + +The smaller bundle size is due to the fact that the interfaces talking to the database (the [Prisma engines](/orm/more/under-the-hood/engines)) are no longer bundled with Prisma Client as this logic is now handled by Prisma Accelerate. + +## 7. Develop the Cloudflare Worker function + +You're now ready to create a Cloudflare Worker. Create a `src/index.ts` file with the following code: + +```ts +import { PrismaClient } from '@prisma/client/edge' +import { withAccelerate } from '@prisma/extension-accelerate' + +export interface Env { + DATABASE_URL: string +} + +export default { + async fetch( + request: Request, + env: Env, + ctx: ExecutionContext + ): Promise { + const prisma = new PrismaClient({ + datasourceUrl: env.DATABASE_URL, + }).$extends(withAccelerate()) + + await prisma.log.create({ + data: { + level: 'Info', + message: `${request.method} ${request.url}`, + meta: { + headers: JSON.stringify(request.headers), + }, + }, + }) + + const { data, info } = await prisma.log + .findMany({ + take: 20, + orderBy: { + id: 'desc', + }, + }) + .withAccelerateInfo() + + console.log(JSON.stringify(info)) + + return new Response(`request method: ${request.method}!`) + }, +} +``` + +> The [`info`](/accelerate/api-reference#return-type) object has additional information which can be useful for debugging. +> Accelerate can also be used to cache your query results. You can find more information on caching with Prisma Accelerate in [here](/accelerate). + +Run `npm run dev` to see your worker in development: + +``` +👂 Listening on http://127.0.0.1:8787 +``` + +Go ahead and open `http://127.0.0.1:8787`. If all goes well, you should see: + +``` +request method: GET! +``` + +Refresh the page a couple times to verify that it's working. + +## 8. Publish to Cloudflare Workers + +You're now ready to deploy to Cloudflare Workers. Run the following command: + +```terminal +npm run deploy +``` + +This will package and upload to Cloudflare. With a bit of luck, you'll see the following: + +``` +✨ Built successfully, built project size is 94 KiB. +✨ Successfully published your script to +https://prisma-cloudflare-accelerate.ankman.workers.dev +``` + +Visit your deployment URL and you'll again see: + +``` +request method: GET! +``` + +You're all set! You've successfully deployed a Cloudflare Worker written in TypeScript that uses Prisma to talk to your PostgreSQL database. diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/550-deploy-to-deno-deploy.mdx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/550-deploy-to-deno-deploy.mdx new file mode 100644 index 0000000000..7e9dd03f9b --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/301-edge/550-deploy-to-deno-deploy.mdx @@ -0,0 +1,271 @@ +--- +title: 'Deploy to Deno Deploy' +metaTitle: 'Deploy to Deno Deploy' +metaDescription: 'Learn how to deploy a TypeScript application to Deno Deploy.' +--- + + + +With this guide, you can learn how to build and deploy a simple application to [Deno Deploy](https://deno.com/deploy). The application uses Prisma to save a log of each request to a PostgreSQL database. + +This guide covers the use of Prisma CLI with Deno CLI, Deno Deploy, Prisma Client, and Prisma Accelerate. + + + +This guide demonstrates how to deploy an application to Deno Deploy in conjunction with a PostgreSQL database, but you can use [any database type that Prisma supports](/orm/reference/supported-databases). + + + + + +## Prerequisites + +- a free [Prisma Data Platform](https://console.prisma.io) account +- a free [Deno Deploy](https://deno.com/deploy) account +- a PostgreSQL database +- Node.js & npm installed +- Deno v1.29.4 or later installed. [Learn more](https://deno.land/manual/getting_started/installation). +- (Recommended) Latest version of Prisma ORM. +- (Recommended) Deno extension for VS Code. [Learn more](https://deno.land/manual/getting_started/setup_your_environment#visual-studio-code). + +## 1. Set up your application + +To start, you create a directory for your project, and then use `deno run` to initialize your application with `prisma init` as an [npm package with npm specifiers](https://deno.land/manual/node/npm_specifiers). + +To set up your application: + +1. Open your terminal and navigate to a location of your choice. +2. Run the following commands to set up your application. + + ```terminal + mkdir prisma-deno-deploy + cd prisma-deno-deploy + deno run -A npm:prisma init + ``` + +3. Edit the `prisma/schema.prisma` file to define the data model and enable the `deno` preview feature flag. + + Later in the guide, you create an application that uses the `Log` model to store data for incoming requests from the application. + + To use Deno, you need to add the preview feature flag `deno` to the `generator` block of your `schema.prisma` file. Also, Deno requires that you generate Prisma Client in a custom location. You can enable this with the `output` parameter in the `generator` block. To satisfy both of these requirements, add the following lines to the `generator` block: + + ```prisma file=schema.prisma highlight=3-4,12-23;add + generator client { + provider = "prisma-client-js" + previewFeatures = ["deno"] + output = "../generated/client" + } + + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model Log { + id Int @id @default(autoincrement()) + level Level + message String + meta Json + } + + enum Level { + Info + Warn + Error + } + ``` + +4. In your `.env` file, replace the current placeholder connection string `postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public` with your PostgreSQL connection string. + +## 2. Create the database schema + +With the data model in place and your database connection configured, you can now apply the data model to your database. + +```terminal +deno run -A npm:prisma migrate dev --name init +``` + +The command does two things: + +1. It creates a new SQL migration file for this migration +1. It runs the SQL migration file against the database + +At this point, the command has two additional side effects. The command installs Prisma Client and creates the `package.json` file for the project, which includes the `@prisma/client` package as a dependency. + +## 3. Generate Prisma Client for Prisma Accelerate + +Next, generate Prisma Client for the Prisma Accelerate with the `--no-engine` flag. Later, you will use [Prisma Accelerate](/accelerate) to connect to your database over HTTP. + +```terminal +deno run -A --unstable npm:prisma generate --no-engine +``` + + + +Prior to Prisma 5.2.0, the `--no-engine` flag is not available. Instead, use the `--accelerate` flag. + +```terminal +deno run -A npm:prisma generate --accelerate +``` + + + +You now have a database schema and a locally generated Prisma Client for the Prisma Accelerate. + +## 4. Create your application + +You can now create a local Deno application. Create `index.ts` in the root folder of your project and add the content below: + +```ts +import { serve } from 'https://deno.land/std@0.140.0/http/server.ts' +import { PrismaClient } from './generated/client/deno/edge.ts' + +const prisma = new PrismaClient() + +async function handler(request: Request) { + const log = await prisma.log.create({ + data: { + level: 'Info', + message: `${request.method} ${request.url}`, + meta: { + headers: JSON.stringify(request.headers), + }, + }, + }) + const body = JSON.stringify(log, null, 2) + return new Response(body, { + headers: { 'content-type': 'application/json; charset=utf-8' }, + }) +} + +serve(handler) +``` + + + +**VS Code error: `An import path cannot end with a '.ts' extension`**

+ +If you use VS Code and see the error `An import path cannot end with a '.ts' extension` for the `import` statements at the beginning of `index.ts`, you need to install the [Deno extension for VS Code](https://deno.land/manual/getting_started/setup_your_environment#visual-studio-code), select **View** > **Command Palette** and run the command **Deno: Initialize Workspace Configuration**. This tells VS Code that the TypeScript files in the current project need to run with Deno, which then triggers the correct validations. + +
+ +### What's next + +You cannot run this script yet, because you do not yet have the required Prisma Accelerate connection string to use Prisma Client with your database. Later in this guide, you will obtain the required credentials when you next add your application to the Prisma Data Platform. + +After that, you test your application locally. + +## 5. Enable Accelerate in the Prisma Data Platform + +To get started with Prisma Accelerate: + +1. Sign up for a free [Prisma Data Platform account](https://console.prisma.io/) +1. Create a project +1. Navigate to the project you created +1. Enable Accelerate +1. Generate an Accelerate connection string and copy it to your clipboard + +## 6. Configure Prisma Accelerate in your environment + +With the Accelerate connection string copied, you can replace the existing connection string that you used to create the database schema in your `.env` file. + +Prisma Client does not read `.env` files by default on Deno, so you must also install `dotenv-cli` locally. + +To configure Prisma Accelerate: + +1. Install the `dotenv-cli`. + + ```sh + npm install dotenv-cli + ``` + +2. Add the Prisma Accelerate connection string to the `.env` file. Also, comment out the direct connection string. + + ```bash file=.env + DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=__API_KEY__" + # Previous database connection + # DATABASE_URL="postgres://..." + ``` + +The configuration of your local environment is now ready to send Prisma queries to the database through Prisma Accelerate. + +## 7. Test your application locally + +You can now start your application locally and test the creation of log entries. + +```terminal +npx dotenv -- deno run -A ./index.ts +``` + +In a web browser, open [http://localhost:8000/](http://localhost:8000/). This page writes your request to the database. + +``` +{ + "id": 3, + "level": "Info", + "message": "GET http://localhost:8000/", + "meta": { + "headers": "{}" + } +} +``` + +Reload the page a few times.

Every time you reload, the script generates a new log entry and the id of the current log entry increments. + +This confirms that your application works when you run it from your local environment. + +## 8. Create a repository and push to GitHub + +You need a GitHub repository to add your project to Deno Deploy and enable automated deployments whenever you push changes. + +To set up a GitHub repository: + +1. [Create a private GitHub repository](https://github.com/new). + +2. Initialize your repository locally and push your changes to GitHub, with the following commands: + + ```terminal + git init -b main + git remote add origin https://github.com//prisma-deno-deploy + git add . + git commit -m "initial commit" + git push -u origin main + ``` + +## 9. Deploy to Deno Deploy + +Use the GitHub repository to add your application to Deno Deploy: + +1. Go to [https://dash.deno.com/new](https://dash.deno.com/new). +1. Select a GitHub organization or user and then select a repository. +1. Select a production branch and select **Automatic** mode so that Deno Deploy can deploy every time you push a change to the repository. +1. Select `index.ts` as the entry point to your project. +1. To define the Accelerate connection string, click **Add Env Variable**. + 1. For **KEY**, enter `DATABASE_URL`. + 1. For **VALUE**, paste the Accelerate connection string. + ![Deno Deploy - project parameters](./images/550-02-deploy-to-deno-project-parameters.png) +1. Click **Link**.
+ Wait for the first Deno deployment to finish. + +When the first deployment finishes, your browser is redirected to the project view. + +### What's next + +Click the blue **View** button at the top right to open the deployed Deno application. + +The application shows a similar result as when you tested locally with a further increment of the new `Log` record id number. + +``` +{ + "id": 5, + "level": "Info", + "message": "GET https://prisma-deno-deploy.deno.dev/", + "meta": { + "headers": "{}" + } +} +``` + +## Summary + +You successfully deployed a Deno application that you created in TypeScript, which uses Prisma Client for the Prisma Accelerate to connect to a PostgreSQL database. diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.png b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.png new file mode 100644 index 0000000000..5a2f89e053 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.snagx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.snagx new file mode 100644 index 0000000000..0b6664a3e9 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-03-import-project.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.png b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.png new file mode 100644 index 0000000000..d28f552189 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.snagx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.snagx new file mode 100644 index 0000000000..5295d13078 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-04-connect-db.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.png b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.png new file mode 100644 index 0000000000..e5097cba6a Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.snagx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.snagx new file mode 100644 index 0000000000..0fda8e104c Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/450-05-data-proxy.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-01-create-repo.png b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-01-create-repo.png new file mode 100644 index 0000000000..9547488f90 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-01-create-repo.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.png b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.png new file mode 100644 index 0000000000..51e5a7d9b9 Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.png differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.snagx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.snagx new file mode 100644 index 0000000000..6d0593512a Binary files /dev/null and b/docs/200-orm/200-prisma-client/500-deployment/301-edge/images/550-02-deploy-to-deno-project-parameters.snagx differ diff --git a/docs/200-orm/200-prisma-client/500-deployment/301-edge/index.mdx b/docs/200-orm/200-prisma-client/500-deployment/301-edge/index.mdx new file mode 100644 index 0000000000..461d3d86b3 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/301-edge/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Edge functions' +metaTitle: 'Deploy Prisma apps to edge function (distributed FaaS) providers' +metaDescription: 'Learn how to deploy your Prisma-backed apps to edge functions like Cloudflare Workers or Vercel Edge Functions' +tocDepth: 2 +--- + + + +If your application is deployed via an "Edge Function" offering or is deployed from a [serverless](/orm/prisma-client/deployment/serverless) offering and has a non-standard runtime, it is a edge deployed app. Common examples include [Cloudflare Workers](/orm/prisma-client/deployment/edge/deploy-to-cloudflare-workers), [Deno Deploy](/orm/prisma-client/deployment/edge/deploy-to-deno-deploy), and Vercel Edge Functions. + + + +## Guides for Edge Function providers + + diff --git a/docs/200-orm/200-prisma-client/500-deployment/550-deploy-database-changes-with-prisma-migrate.mdx b/docs/200-orm/200-prisma-client/500-deployment/550-deploy-database-changes-with-prisma-migrate.mdx new file mode 100644 index 0000000000..0bd45a44fd --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/550-deploy-database-changes-with-prisma-migrate.mdx @@ -0,0 +1,68 @@ +--- +title: 'Deploying database changes with Prisma Migrate' +navTitle: 'Deploying database changes' +metaDescription: 'Learn how to deploy database changes with Prisma Migrate.' +--- + + + +To apply pending migrations to staging, testing, or production environments, run the `migrate deploy` command as part of your CI/CD pipeline: + +```terminal +npx prisma migrate deploy +``` + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate deploy`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb)). + +
+ +Exactly when to run `prisma migrate deploy` depends on your platform. For example, a simplified [Heroku](/orm/prisma-client/deployment/traditional/deploy-to-heroku) workflow includes: + +1. Ensuring the `./prisma/migration` folder is in source control +2. Running `prisma migrate deploy` during the [release phase](https://devcenter.heroku.com/articles/release-phase) + +Ideally, `migrate deploy` should be part of an automated CI/CD pipeline, and we do not generally recommend running this command locally to deploy changes to a production database (for example, by temporarily changing the `DATABASE_URL` environment variable). It is not generally considered good practice to store the production database URL locally. + +Beware that in order to run the `prisma migrate deploy` command, you need access to the `prisma` dependency that is typically added to the `devDependencies`. Some platforms like Vercel, prune development dependencies during the build, thereby preventing you from calling the command. This can be worked around by making the `prisma` a production dependency, by moving it to `dependencies` in your `package.json`. +For more information about the `migrate deploy` command, see: + +- [`migrate deploy` reference](/orm/reference/prisma-cli-reference#migrate-deploy) +- [How `migrate deploy` works](/orm/prisma-migrate/workflows/development-and-production#production-and-testing-environments) +- [Production troubleshooting](/orm/prisma-migrate/workflows/patching-and-hotfixing) + +
+ +## Deploying database changes using GitHub Actions + +As part of your CI/CD, you can run `prisma migrate deploy` as part of your pipeline to apply pending migrations to your production database. + +Here is an example action that will run your migrations against your database: + +```yaml file=deploy.yml highlight=17-20 +name: Deploy +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v3 + - name: Install dependencies + run: npm install + - run: npm run build + - name: Apply all pending migrations to the database + run: npx prisma migrate deploy + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} +``` + +Ensure you have the `DATABASE_URL` variable [set as a secret in your repository](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions), without quotes around the connection string. diff --git a/docs/200-orm/200-prisma-client/500-deployment/600-deploy-migrations-from-a-local-environment.mdx b/docs/200-orm/200-prisma-client/500-deployment/600-deploy-migrations-from-a-local-environment.mdx new file mode 100644 index 0000000000..8271afb12b --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/600-deploy-migrations-from-a-local-environment.mdx @@ -0,0 +1,54 @@ +--- +title: 'Deploy migrations from a local environment' +metaTitle: 'Deploy migrations from a local environment' +metaDescription: 'Learn how to deploy Node.js and TypeScript applications that are using Prisma Client locally.' +tocDepth: 3 +--- + + + +There are two scenarios where you might consider deploying migrations directly from a local environment to a production environment. + +- You have a local CI/CD pipeline +- You are [baselining](/orm/prisma-migrate/workflows/baselining) a production environment + +This page outlines some examples of how you can do that and **why we would generally not recommend it**. + + + +## Local CI/CD pipeline + +If you do not have an automated CI/CD process, you can technically deploy new migrations from your local environment to production in the following ways: + +1. Make sure your migration history is up to date. You can do this through running `prisma migrate dev`, which will generate a migration history from the latest changes made. +2. Swap your local connection URL for your production connection URL + +```bash file=.env highlight=1;delete|3;add +DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/my_local_database" + +DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/my_production_database" +``` + +3. Run `prisma migrate deploy` + +
+ ⛔{' '} + + We strongly discourage this solution due to the following reasons + +
+ +- You risk exposing your production database connection URL to version control. +- You may accidentally use your production connection URL instead and in turn **override or delete your production database**. + +
+ ✅ We recommend setting up an automated CI/CD pipeline +
+ +The pipeline should handle deployment to staging and production environments, and use `migrate deploy` in a pipeline step. See the [deployment guides](/orm/prisma-client/deployment) for examples. + +## Baselining a production database + +When you add Prisma Migrate to an **existing database**, you must [baseline](/orm/prisma-migrate/workflows/baselining) the production database. Baselining is performed **once**, and can be done from a local instance. + +![](/img/baseline-production-from-local.png) diff --git a/docs/200-orm/200-prisma-client/500-deployment/650-caveats-when-deploying-to-aws-platforms.mdx b/docs/200-orm/200-prisma-client/500-deployment/650-caveats-when-deploying-to-aws-platforms.mdx new file mode 100644 index 0000000000..d9f560e340 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/650-caveats-when-deploying-to-aws-platforms.mdx @@ -0,0 +1,65 @@ +--- +title: 'Caveats when deploying to AWS platforms' +metaTitle: 'Caveats when deploying to AWS platforms' +metaDescription: 'Known caveats when deploying to an AWS platform' +--- + + + +The following describes some caveats you might face when deploying to different AWS platforms. + + + +## AWS RDS Proxy + +Prisma is compatible with AWS RDS Proxy. However, there is no benefit in using it for connection pooling with Prisma due to the way RDS Proxy pins connections: + +> "Your connections to the proxy can enter a state known as pinning. When a connection is pinned, each later transaction uses the same underlying database connection until the session ends. Other client connections also can't reuse that database connection until the session ends. The session ends when Prisma Client's connection is dropped." - [AWS RDS Proxy Docs](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-managing.html#rds-proxy-pinning) + +[Prepared statements (of any size) or query statements greater than 16 KB cause RDS Proxy to pin the session.](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-managing.html#rds-proxy-pinning.all) Because Prisma uses prepared statements for all queries, you won't see any benefit when using RDS Proxy with Prisma. + +## AWS Elastic Beanstalk + +AWS Elastic Beanstalk is a PaaS-like deployment service that abstracts away infrastructure and allows you to deploy applications to AWS quickly. + +When deploying an app using Prisma Client to AWS Elastic Beanstalk, Prisma generates the Prisma Client code into `node_modules`. This is typically done in the `postinstall` hook. + +Because Beanstalk limits the ability to write to the filesystem in the `postinstall` hook, you need to create an [`.npmrc`](https://docs.npmjs.com/cli/v6/configuring-npm/npmrc) file in the root of your project and add the following configuration: + +```config file=.npmrc +unsafe-perm=true +``` + +Enabling `unsafe-perm` forces _npm_ to run as _root_, avoiding the filesystem access problem, thereby allowing the `prisma generate` command in the `postinstall` hook to generate code into `node_modules`. + +### Error: @prisma/client did not initialize yet + +This error happens because AWS Elastic Beanstalk doesn't install `devDependencies`, which means that it doesn't pick up the Prisma CLI. To remedy this you can either: + +1. Add the `prisma` CLI package to your `dependencies` instead of the `devDependencies`. (Making sure to run `npm install` afterward to update the `package-lock.json`). +2. Or install your `devDependencies` on AWS Elastic Beanstalk instances. To do this you must set the AWS Elastic Beanstalk `NPM_USE_PRODUCTION` environment property to false. + +## AWS Lambda upload limit + +AWS Lambda defines an **deployment package upload limit**, which includes: + +- All application code +- Binaries like the [Prisma query engine](/orm/more/under-the-hood/engines) + +The [deployment package (.zip) size limit for lambdas is 50MB](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html). When you prepare a deployment package, remove any files that the function does not require in production to keep the final .zip as small as possible. This includes some [Prisma engine binaries](#deleting-prisma-engines-that-are-not-required). + +### Deleting Prisma engines that are not required + +Prisma CLI downloads additional engine binaries that are **not required** in production. You can delete the following files and folders: + +1. The entire `node_modules/@prisma/engines` folder (refer to the [sample bash script](https://github.com/prisma/ecosystem-tests/blob/13e74dc47eababa5d3c8f488b73fe7fc8bffead7/platforms-serverless/lambda/run.sh#L16) used by the Prisma end-to-end tests) +2. The **local engine file** for your development platform from the `node_modules/.prisma/client` folder. For example, your schema might define the following `binaryTargets` if you develop on Debian (`native`) but deploy to AWS Lambda (`rhel-openssl-1.0.x`): + + ```prisma + binaryTargets = ["native", "rhel-openssl-1.0.x"] + ``` + + In this scenario: + + - Keep `node_modules/.prisma/client/query-engine-rhel-openssl-1.0.x`, which is the engine file used by AWS Lambda + - Delete `node_modules/.prisma/client/query-engine-debian-openssl-1.1.x`, which is only required locally diff --git a/docs/200-orm/200-prisma-client/500-deployment/700-deploy-to-a-different-os.mdx b/docs/200-orm/200-prisma-client/500-deployment/700-deploy-to-a-different-os.mdx new file mode 100644 index 0000000000..a1dd19f9be --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/700-deploy-to-a-different-os.mdx @@ -0,0 +1,19 @@ +--- +title: 'Deploy to a different OS' +metaTitle: 'Deploy to a different OS' +metaDescription: 'Learn how to deploy Node.js and TypeScript applications that are using Prisma Client to a different operating system.' +--- + + + +Prisma Client depends on the [query engine](/orm/more/under-the-hood/engines) that is running as a binary on the same host as your application. + +The query engine is implemented in Rust and is used by Prisma in the form of executable binary files. The binary is downloaded when `prisma generate` is called. + +If you have developed your application on a Windows machine for example, and wish to upload to AWS Lambda, which is a Linux environment, you may encounter issues and be presented with some warnings in your terminal. + +To solve this, if you know ahead of time that you will be deploying to a different environment, you can use the [binary targets](/orm/prisma-schema/overview/generators#binary-targets) and specify which of the [supported operating systems](/orm/reference/prisma-schema-reference#binarytargets-options) binaries should be included. + +> **Note**: If your OS isn't supported you can include a [custom binary](/orm/more/under-the-hood/engines#using-custom-engine-libraries-or-binaries). + + diff --git a/docs/200-orm/200-prisma-client/500-deployment/index.mdx b/docs/200-orm/200-prisma-client/500-deployment/index.mdx new file mode 100644 index 0000000000..19ccb52b47 --- /dev/null +++ b/docs/200-orm/200-prisma-client/500-deployment/index.mdx @@ -0,0 +1,15 @@ +--- +title: 'Deployment' +metaTitle: 'Deploy a Node.js application with Prisma' +metaDescription: 'How to deploy a Node.js application that uses Prisma Client and TypeScript to various cloud platforms.' +--- + + + +This section describes how to deploy Node.js applications that use Prisma Client and TypeScript to various platforms. + + + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/130-logging.mdx b/docs/200-orm/200-prisma-client/600-observability-and-logging/130-logging.mdx new file mode 100644 index 0000000000..fba5c74fc5 --- /dev/null +++ b/docs/200-orm/200-prisma-client/600-observability-and-logging/130-logging.mdx @@ -0,0 +1,174 @@ +--- +title: 'Logging' +metaTitle: 'Logging' +metaDescription: 'Learn how to configure Prisma Client to log the raw SQL queries it sends to the database and other information.' +--- + + + +Use the `PrismaClient` [`log`](/orm/reference/prisma-client-reference#log) parameter to configure [log levels](/orm/reference/prisma-client-reference#log-levels) , including warnings, errors, and information about the queries sent to the database. + +Prisma supports two types of logging: + +- Logging to [stdout](https://en.wikipedia.org/wiki/Standard_streams) (default) +- Event-based logging (use [`$on()`](/orm/reference/prisma-client-reference#on) method to [subscribe to events](#event-based-logging)) + + + +You can also use the `DEBUG` environment variable to enable debugging output in Prisma Client. See [Debugging](/orm/prisma-client/debugging-and-troubleshooting/debugging) for more information. + + + + + +If you want a detailed insight into your Prisma Client's performance at the level of individual operations, see [Tracing](/orm/prisma-client/observability-and-logging/opentelemetry-tracing). + + + + + +## Log to stdout + +The simplest way to print _all_ log levels to stdout is to pass in an array `LogLevel` objects: + +```ts +const prisma = new PrismaClient({ + log: ['query', 'info', 'warn', 'error'], +}) +``` + +This is the short form of passing in an array of `LogDefinition` objects where the value of `emit` is always `stdout`: + +```ts +const prisma = new PrismaClient({ + log: [ + { + emit: 'stdout', + level: 'query', + }, + { + emit: 'stdout', + level: 'error', + }, + { + emit: 'stdout', + level: 'info', + }, + { + emit: 'stdout', + level: 'warn', + }, + ], +}) +``` + +## Event-based logging + +To use event-based logging: + +1. Set `emit` to `event` for a specific log level, such as query +2. Use the `$on()` method to subscribe to the event + +The following example subscribes to all `query` events and write the `duration` and `query` to console: + + + + + + + + +```ts highlight=4,5,22-26;normal +const prisma = new PrismaClient({ + log: [ + { + emit: 'event', + level: 'query', + }, + { + emit: 'stdout', + level: 'error', + }, + { + emit: 'stdout', + level: 'info', + }, + { + emit: 'stdout', + level: 'warn', + }, + ], +}) + +prisma.$on('query', (e) => { + console.log('Query: ' + e.query) + console.log('Params: ' + e.params) + console.log('Duration: ' + e.duration + 'ms') +}) +``` + + + + +```sql no-copy +Query: SELECT "public"."User"."id", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1 +Params: [0] +Duration: 3ms +Query: SELECT "public"."Post"."id", "public"."Post"."title", "public"."Post"."authorId" FROM "public"."Post" WHERE "public"."Post"."authorId" IN ($1,$2,$3,$4) OFFSET $5 +Params: [2, 7, 18, 29] +Duration: 2ms +``` + + + + + + + + + + + +```ts highlight=4,5,22-25;normal +const prisma = new PrismaClient({ + log: [ + { + emit: 'event', + level: 'query', + }, + { + emit: 'stdout', + level: 'error', + }, + { + emit: 'stdout', + level: 'info', + }, + { + emit: 'stdout', + level: 'warn', + }, + ], +}) + +prisma.$on('query', (e) => { + console.log('Query: ' + e.query) +}) +``` + + + + +```terminal no-copy +Query: db.User.aggregate([ { $project: { _id: 1, email: 1, name: 1, }, }, ]) +Query: db.Post.aggregate([ { $match: { userId: { $in: [ "622f0bbbdf635a42016ee325", ], }, }, }, { $project: { _id: 1, slug: 1, title: 1, body: 1, userId: 1, }, }, ]) +``` + + + + + + + + +The exact [event (`e`) type and the properties available](/orm/reference/prisma-client-reference#event-types) depends on the log level. diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/240-metrics.mdx b/docs/200-orm/200-prisma-client/600-observability-and-logging/240-metrics.mdx new file mode 100644 index 0000000000..e805599be7 --- /dev/null +++ b/docs/200-orm/200-prisma-client/600-observability-and-logging/240-metrics.mdx @@ -0,0 +1,563 @@ +--- +title: 'Metrics' +metaTitle: 'Metrics (Preview)' +metaDescription: 'Diagnose application performance with insights into Prisma Client database activity.' +preview: true +tocDepth: 4 +--- + + + +Prisma metrics give you a detailed insight into how Prisma Client interacts with your database. You can use this insight to help diagnose performance issues with your application. + + + +If you want an even more detailed insight into your Prisma Client's performance, at the level of individual operations, see [Tracing](/orm/prisma-client/observability-and-logging/opentelemetry-tracing). + + + + + +## About metrics + +You can export metrics in JSON or Prometheus formats and view them in a console log, or integrate them into an external metrics system, such as [StatsD](https://github.com/statsd/statsd) or [Prometheus](https://prometheus.io/). If you integrate them into an external metrics system, then you can view the metrics data over time. For example, you can use metrics to help diagnose how your application's number of idle and active connections changes. + +Prisma Client provides the following metrics: + +- Counters (always increase): + + - `prisma_client_queries_total`: The total number of Prisma Client queries executed. + - `prisma_datasource_queries_total`: The total number of datasource queries executed (SQL queries in relational databases, and commands in MongoDB). + - The value returned by `prisma_datasource_queries_total` can be greater than `prisma_client_queries_total`, because some Prisma Client operations create multiple queries. + - `prisma_pool_connections_closed_total`: The total number of pool connections closed. + - `prisma_pool_connections_opened_total`: The number of currently open pool connections. + +- Gauges (can increase or decrease): + + - `prisma_client_queries_active`: The number of currently active Prisma Client queries. + - `prisma_client_queries_wait`: The number of Prisma Client queries currently waiting for a connection because all connections are in use. + - `prisma_pool_connections_busy`: The number of currently busy pool connections. These pool connections are currently executing a datasource query. + - `prisma_pool_connections_idle`: The number of pool connections that are not currently being used. These pool connections are waiting for the next datasource query to run. + - `prisma_pool_connections_open`: The number of [pool connections](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool#default-connection-pool-size) open. + +- Histograms (metrics data divided into a collection of values; we call each container in the collection a "bucket"): + + - `prisma_client_queries_wait_histogram_ms`: The time waiting for a pool connection for all Prisma Client queries in ms. + - `prisma_client_queries_duration_histogram_ms`: The execution time for all executed Prisma Client queries in ms. This includes the time taken to execute all database queries, and to carry out all database engine activities, such as joining data and transforming data to the correct format. + - `prisma_datasource_queries_duration_histogram_ms`: The execution time for all executed Datasource queries in ms. + +You can [add global labels to your metrics data](#global-labels) to help you group and separate your metrics, for example by infrastructure region or server. + +## Prerequisites + +To use Prisma metrics, you must do the following: + +1. [Install the appropriate dependencies](#step-1-install-up-to-date-prisma-dependencies). +1. [Enable the `metrics` feature flag in your Prisma schema file](#step-2-enable-the-feature-flag-in-the-prisma-schema-file). + +### Step 1. Install up-to-date Prisma dependencies + +Use version `3.15.0` or higher of the `prisma` and `@prisma/client` npm packages. + +```terminal +npm install prisma@latest --save-dev +npm install @prisma/client@latest --save +``` + +### Step 2: Enable the feature flag in the Prisma schema file + +In the `generator` block of your `schema.prisma` file, enable the `metrics` feature flag: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["metrics"] +} +``` + +## Retrieve metrics in JSON format + +When you retrieve metrics in JSON format, you can use them in the format they are returned, or [send them to StatSD](#use-prisma-metrics-with-statsd) to visualize how they change over time. + +To retrieve metrics in JSON format, add the following lines to your application code: + +```ts +const metrics = await prisma.$metrics.json() +console.log(metrics) +``` + +This returns metrics as follows: + +```json +{ + "counters": [ + { + "key": "prisma_client_queries_total", + "labels": {}, + "value": 0, + "description": "Total number of Prisma Client queries executed" + }, + { + "key": "prisma_datasource_queries_total", + "labels": {}, + "value": 0, + "description": "Total number of Datasource Queries executed" + }, + { + "key": "prisma_pool_connections_closed_total", + "labels": {}, + "value": 0, + "description": "Total number of Pool Connections closed" + }, + { + "key": "prisma_pool_connections_opened_total", + "labels": {}, + "value": 1, + "description": "Total number of Pool Connections opened" + } + ... + ], + "gauges": [ + ... + ], + "histograms": [ + ... + ] +} +``` + +
+ +Expand to view the full output + +```json no-copy +{ + "counters": [ + { + "key": "prisma_client_queries_total", + "labels": {}, + "value": 2, + "description": "Total number of Prisma Client queries executed" + }, + { + "key": "prisma_datasource_queries_total", + "labels": {}, + "value": 5, + "description": "Total number of Datasource Queries executed" + }, + { + "key": "prisma_pool_connections_open", + "labels": {}, + "value": 1, + "description": "Number of currently open Pool Connections" + } + ], + "gauges": [ + { + "key": "prisma_client_queries_active", + "labels": {}, + "value": 0, + "description": "Number of currently active Prisma Client queries" + }, + { + "key": "prisma_client_queries_wait", + "labels": {}, + "value": 0, + "description": "Number of Prisma Client queries currently waiting for a connection" + }, + { + "key": "prisma_pool_connections_busy", + "labels": {}, + "value": 0, + "description": "Number of currently busy Pool Connections (executing a datasource query)" + }, + { + "key": "prisma_pool_connections_idle", + "labels": {}, + "value": 21, + "description": "Number of currently unused Pool Connections (waiting for the next datasource query to run)" + }, + { + "key": "prisma_pool_connections_open", + "labels": {}, + "value": 1, + "description": "Number of currently open Pool Connections" + } + ], + "histograms": [ + { + "key": "prisma_client_queries_duration_histogram_ms", + "labels": {}, + "value": { + "buckets": [ + [0, 0], + [1, 0], + [5, 0], + [10, 1], + [50, 1], + [100, 0], + [500, 0], + [1000, 0], + [5000, 0], + [50000, 0] + ], + "sum": 47.430541000000005, + "count": 2 + }, + "description": "Histogram of the duration of all executed Prisma Client queries in ms" + }, + { + "key": "prisma_client_queries_wait_histogram_ms", + "labels": {}, + "value": { + "buckets": [ + [0, 0], + [1, 3], + [5, 0], + [10, 0], + [50, 0], + [100, 0], + [500, 0], + [1000, 0], + [5000, 0], + [50000, 0] + ], + "sum": 0.0015830000000000002, + "count": 3 + }, + "description": "Histogram of the wait time of all Prisma Client Queries in ms" + }, + { + "key": "prisma_datasource_queries_duration_histogram_ms", + "labels": {}, + "value": { + "buckets": [ + [0, 0], + [1, 0], + [5, 2], + [10, 2], + [50, 1], + [100, 0], + [500, 0], + [1000, 0], + [5000, 0], + [50000, 0] + ], + "sum": 47.134498, + "count": 5 + }, + "description": "Histogram of the duration of all executed Datasource Queries in ms" + } + ] +} +``` + +
+ +### Histograms in JSON data + +Each histogram "bucket" has two values. The first one is the upper bound of the bucket, and the second one is the count (the number of data values that fall into that bucket). In the following example, there are two instances of values between 11 and 20, and five instances of values between 21 and 30: + +```json +... +[20, 2], +[30, 5], +... +``` + +### Use Prisma metrics with StatsD + +You can send JSON-formatted metrics to [StatsD](https://github.com/statsd/statsd) to visualize your metrics data over time. + + + +Note: You must provide counter metrics to StatsD as a series of values that are incremented or decremented from a previous retrieval of the metrics. However, Prisma counter +metrics return absolute values. Therefore, you must convert your counter metrics to a series of incremented and decremented values and send them to StatsD as gauge data. In the code example below, we convert counter metrics into incremented and decremented gauge data in `diffHistograms`. + + + +In the following example, we send metrics to StatsD every 10 seconds. This timing aligns with the default 10s flush rate of StatsD. + +```ts +import StatsD from 'hot-shots' +let statsd = new StatsD({ + port: 8125, +}) + +let diffMetrics = (metrics: any) => { + return metrics.map((metric: any) => { + let prev = 0 + let diffBuckets = metric.value.buckets.map((values: any) => { + let [bucket, value] = values + let diff = value - prev + prev = value + return [bucket, diff] + }) + metric.value.buckets = diffBuckets + return metric + }) +} + +let previousHistograms: any = null +let statsdSender = async () => { + let metrics = await prisma.$metrics.json() + + metrics.counters.forEach((counter: any) => { + statsd.gauge('prisma.' + counter.key, counter.value, (...res) => {}) + }) + + metrics.gauges.forEach((counter: any) => { + statsd.gauge('prisma.' + counter.key, counter.value, (...res) => {}) + }) + + if (previousHistograms === null) { + previousHistograms = diffMetrics(metrics.histograms) + return + } + + let diffHistograms = diffMetrics(metrics.histograms) + + diffHistograms.forEach((diffHistograms: any, histogramIndex: any) => { + diffHistograms.value.buckets.forEach((values: any, bucketIndex: any) => { + let [bucket, count] = values + let [_, prev] = + previousHistograms[histogramIndex].value.buckets[bucketIndex] + let change = count - prev + + for (let sendTimes = 0; sendTimes < change; sendTimes++) { + statsd.timing('prisma.' + diffHistograms.key, bucket) + } + }) + }) + + previousHistograms = diffHistograms +} + +setInterval(async () => await statsdSender(), 10000) +``` + +## Retrieve metrics in Prometheus format + +When you retrieve Prisma metrics in Prometheus format, you can use them in the format they are returned, or [send them to the Prometheus metrics system](#use-prisma-metrics-with-the-prometheus-metrics-system) to visualize how they change over time. + +To retrieve metrics in Prometheus format, add the following lines to your application code: + +```ts +const metrics = await prisma.$metrics.prometheus() +console.log(metrics) +``` + +This returns metrics as follows: + +```c +# HELP prisma_client_queries_total Total number of Prisma Client queries executed +# TYPE prisma_client_queries_total counter +prisma_client_queries_total 14 + +... +# HELP prisma_pool_connections_busy The number of active connections in use. +# TYPE prisma_pool_connections_busy gauge +prisma_pool_connections_busy 0 + +... +# HELP prisma_client_queries_wait_histogram_ms The wait time for a worker to get a connection. +# TYPE prisma_client_queries_wait_histogram_ms histogram +prisma_client_queries_wait_histogram_ms_bucket{le="0"} 0 +prisma_client_queries_wait_histogram_ms_bucket{le="1"} 3 +``` + +
+ +Expand to view the full output + +```c +# HELP query_total_operations +# TYPE query_total_operations counter +query_total_operations 2 + +# HELP prisma_datasource_queries_total +# TYPE prisma_datasource_queries_total counter +prisma_datasource_queries_total 28 + +# HELP prisma_pool_connections_closed_total Total number of Pool Connections closed +# TYPE prisma_pool_connections_closed_total counter +prisma_pool_connections_closed_total 0 + +# HELP prisma_pool_connections_opened_total Total number of Pool Connections opened +# TYPE prisma_pool_connections_opened_total counter +prisma_pool_connections_opened_total 0 + +# HELP prisma_client_queries_active Number of currently active Prisma Client queries +# TYPE prisma_client_queries_active gauge +prisma_client_queries_active 0 + +# HELP prisma_client_queries_wait Number of queries currently waiting for a connection +# TYPE prisma_client_queries_wait gauge +prisma_client_queries_wait 0 + +# HELP prisma_pool_connections_busy Number of currently busy Pool Connections (executing a datasource query) +# TYPE prisma_pool_connections_busy gauge +prisma_pool_connections_busy 0 + +# HELP prisma_pool_connections_idle Number of currently unused Pool Connections (waiting for the next pool query to run) +# TYPE prisma_pool_connections_idle gauge +prisma_pool_connections_idle 21 + +# HELP prisma_pool_connections_open Number of currently open Pool Connections +# TYPE prisma_pool_connections_open gauge +prisma_pool_connections_open 1 + +# HELP prisma_pool_connections_open Number of currently open Pool Connections (able to execute a datasource query) +# TYPE prisma_pool_connections_open gauge +prisma_pool_connections_open 0 + +# HELP prisma_client_queries_wait_histogram_ms The wait time for a worker to get a connection. +# TYPE prisma_client_queries_wait_histogram_ms histogram +prisma_client_queries_wait_histogram_ms_bucket{le="0"} 0 +prisma_client_queries_wait_histogram_ms_bucket{le="1"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="5"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="10"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="50"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="100"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="500"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="1000"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="5000"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="50000"} 3 +prisma_client_queries_wait_histogram_ms_bucket{le="+Inf"} 3 +prisma_client_queries_wait_histogram_ms_sum 0.023208 +prisma_client_queries_wait_histogram_ms_count 3 + +# HELP prisma_client_queries_duration_histogram_ms Histogram of the duration of all executed Prisma Client queries in ms +# TYPE prisma_client_queries_duration_histogram_ms histogram +prisma_client_queries_duration_histogram_ms_bucket{le="0"} 0 +prisma_client_queries_duration_histogram_ms_bucket{le="1"} 1 +prisma_client_queries_duration_histogram_ms_bucket{le="5"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="10"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="50"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="100"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="500"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="1000"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="5000"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="50000"} 2 +prisma_client_queries_duration_histogram_ms_bucket{le="+Inf"} 2 +prisma_client_queries_duration_histogram_ms_sum 3.197624 +prisma_client_queries_duration_histogram_ms_count 2 + +# HELP prisma_datasource_queries_duration_histogram_ms Histogram of the duration of all executed Datasource Queries in ms +# TYPE prisma_datasource_queries_duration_histogram_ms histogram +prisma_datasource_queries_duration_histogram_ms_bucket{le="0"} 0 +prisma_datasource_queries_duration_histogram_ms_bucket{le="1"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="5"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="10"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="50"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="100"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="500"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="1000"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="5000"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="50000"} 5 +prisma_datasource_queries_duration_histogram_ms_bucket{le="+Inf"} 5 +prisma_datasource_queries_duration_histogram_ms_sum 1.8407059999999997 +prisma_datasource_queries_duration_histogram_ms_count 5 +``` + +
+Metrics of type `histogram` expose three different class of values in the Prometheus format: + +1. Multiple cumulative counters for observation buckets. These counters are suffixed with `_bucket{le=""}`. For example, `prisma_datasource_queries_duration_histogram_ms` has a counter exposed as `prisma_datasource_queries_duration_histogram_ms_bucket{le="1"}` + + When an observed value is less than or equal to the upper inclusive bound of a bucket, then Prisma Metrics increments that bucket by 1. Suppose that you have buckets with the upper inclusive bounds 0, 1, 5, 10, and 50 respectively. If the observed value is 5 then Prisma Metrics increments the third bucket onwards, because the value is greater than 0 and greater than 1, but less than or equal to 5, 10, and 50. + +2. A single **total sum** for all observed values. This counter is suffixed with `_sum`. For example the total sum of `prisma_datasource_queries_duration_histogram_ms` is exposed as `prisma_datasource_queries_duration_histogram_ms_sum`. +3. The **count** of the number of events that have been observed. This counter is suffixed with `_count`. For example the total count of `prisma_datasource_queries_duration_histogram_ms` events is exposed as `prisma_datasource_queries_duration_histogram_ms_count`. + +For more information, read the Prometheus documentation on [metric types](https://prometheus.io/docs/concepts/metric_types/#histogram). + +### Use Prisma metrics with the Prometheus metrics system + +In the majority of cases, Prometheus must scrape an endpoint to retrieve metrics. The following example shows how to send data with `Express.js`: + +```js +import { PrismaClient } from '@prisma/client' +import express, { Request, Response } from 'express' + +const app = express() +const port = 4000 +const prisma = new PrismaClient() + +app.get('/metrics', async (_req, res: Response) => { + const metrics = await prisma.$metrics.prometheus() + res.end(metrics) +}) + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`) +}) +``` + +The following example shows how to combine Prisma metrics with other Prometheus client libraries that are also served with a REST API endpoint in conjunction with `Express.js`: + +```js +import { PrismaClient } from '@prisma/client' +import express, { Request, Response } from 'express' +import prom from 'prom-client' + +const app = express() +const port = 4000 +const prisma = new PrismaClient() + +const register = new prom.Registry() +prom.collectDefaultMetrics({ register }) + +app.get('/metrics', async (_req, res: Response) => { + const prismaMetrics = await prisma.$metrics.prometheus() + const appMetrics = await register.metrics() + res.end(prismaMetrics + appMetrics) +}) + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`) +}) +``` + +## Global labels + +You can add global labels to your metrics to help you group and separate your metrics. Each instance of Prisma Client adds these labels to the metrics that it generates. For example, you can group your metrics by infrastructure region, or by server, with a label like `{ server: us_server1', 'app_version': 'one' }`. + +Global labels work with JSON and Prometheus-formatted metrics. + +For example, to add global labels to JSON-format metrics, add the following code to your application: + +```ts +const metrics = prisma.$metrics.json({ + globalLabels: { server: 'us_server1', app_version: 'one' }, +}) +console.log(metrics) +``` + +This returns information in the following format: + +```json highlight=5,11;add +{ + "counters": [ + { + "key": "query_total_operations", + "labels": { "server": "us_server1", "app_version": "one" }, + "value": 0, + "description": "The total number of operations executed" + }, + { + "key": "prisma_datasource_queries_total", + "labels": { "server": "us_server1", "app_version": "one" }, + "value": 0, + "description": "The total number of queries executed" + }, + ... + ], + "gauges": [ + ... + ], + "histograms": [ + ... + ] +} +``` diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx b/docs/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx new file mode 100644 index 0000000000..0c06f8d2b2 --- /dev/null +++ b/docs/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx @@ -0,0 +1,419 @@ +--- +title: 'OpenTelemetry tracing' +metaTitle: 'OpenTelemetry tracing (Preview)' +metaDescription: 'Diagnose application performance with detailed traces of each query.' +preview: true +tocDepth: 4 +--- + + + +Tracing provides a detailed log of the activity that Prisma Client carries out, at an operation level, including the time taken to execute each query. It helps you analyze your application's performance and identify bottlenecks. Tracing is fully compliant with [OpenTelemetry](https://opentelemetry.io/), so you can use it as part of your end-to-end application tracing system. + + + +Tracing gives you a highly detailed, operation-level insight into your Prisma project. If you want aggregated numerical reporting, such as query counts, connection counts, and total query execution times, see [Metrics](/orm/prisma-client/observability-and-logging/metrics). + + + + + +## About tracing + +When you enable tracing, Prisma Client outputs the following: + +- One trace for each operation (e.g. findMany) that Prisma Client makes. +- In each trace, one or more [spans](https://www.opentelemetry.io/docs/reference/specification/trace/api/#span). Each span represents the length of time that one stage of the operation takes, such as serialization, or a database query. Spans are represented in a tree structure, where child spans indicate that execution is happening within a larger parent span. + +The number and type of spans in a trace depends on the type of operation the trace covers, but an example is as follows: + + + +![image](trace-diagram.png) + +You can [send tracing output to the console](#send-tracing-output-to-the-console), or analyze it in any OpenTelemetry-compatible tracing system, such as [Jaeger](https://www.jaegertracing.io/), [Honeycomb](https://www.honeycomb.io/trace/) and [Datadog](https://www.datadoghq.com/). On this page, we give an example of how to send tracing output to Jaeger, which you can [run locally](#visualize-traces-with-jaeger). + +## Trace output + +For each trace, Prisma Client outputs a series of spans. The number and type of these spans depends on the Prisma operation. A typical Prisma trace has the following spans: + +- `prisma:client:operation`: Represents the entire Prisma operation, from Prisma Client to the database and back. It contains details such as the model and method called by Prisma Client. Depending on the Prisma operation, it contains one or more of the following spans: + - `prisma:client:connect`: Represents how long it takes for Prisma Client to connect to the database. + - `prisma:client:serialize`: Represents how long it takes to validate and transform a Prisma operation into a query for the [query engine](/orm/more/under-the-hood/engines). + - `prisma:engine`: Represents how long a query takes in the query engine. + - `prisma:engine:connection`: Represents how long it takes for Prisma Client to get a database connection. + - `prisma:engine:db_query`: Represents the database query that was executed against the database. It includes the query in the tags, and how long the query took to run. + - `prisma:engine:serialize`: Represents how long it takes to transform a database query result into a Prisma Client result. + +For example, given the following Prisma Client code: + +```ts +prisma.user.findMany({ + where: { + email: email, + }, + include: { + posts: true, + }, +}) +``` + +The trace is structured as follows: + +- `prisma:client:operation` + - `prisma:client:serialize` + - `prisma:engine` + - `prisma:engine:connection` + - `prisma:engine:db_query`: details of the first SQL query or command... + - `prisma:engine:db_query`: ...details of the next SQL query or command... + - `prisma:engine:serialize` + +## Considerations and prerequisites + +If your application sends a large number of spans to a [collector](https://opentelemetry.io/docs/collector/), this can have a significant performance impact. For information on how to minimize this impact, see [Reducing performance impact](#reduce-performance-impact). + +To use tracing, you must do the following: + +1. [Install the appropriate dependencies](#step-1-install-up-to-date-prisma-dependencies). +1. [Enable the `tracing` feature flag in your Prisma schema file](#step-2-enable-the-feature-flag-in-your-prisma-schema-file). +1. [Install OpenTelemetry packages](#step-3-install-opentelemetry-packages). + +## Get started with tracing in Prisma + +This section explains how to install and register tracing in your application. + +### Step 1. Install up-to-date Prisma dependencies + +Use version `4.2.0` or later of the `prisma`, `@prisma/client`, and `@prisma/instrumentation` npm packages. + +```terminal +npm install prisma@latest --save-dev +npm install @prisma/client@latest --save +npm install @prisma/instrumentation@latest --save +``` + +### Step 2: Enable the feature flag in your Prisma schema file + +In the `generator` block of your `schema.prisma` file, enable the `tracing` feature flag: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["tracing"] +} +``` + +### Step 3: Install OpenTelemetry packages + +Finally, install the appropriate OpenTelemetry packages, as follows: + +```console +npm install @opentelemetry/semantic-conventions @opentelemetry/exporter-trace-otlp-http @opentelemetry/instrumentation @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/resources +``` + +### Register tracing in your application + +The following code provides a minimal tracing configuration. You need to customize this configuration for your specific application. + +```ts file=setup.ts +// Imports +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { registerInstrumentations } from '@opentelemetry/instrumentation' +import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base' +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' +import { PrismaInstrumentation } from '@prisma/instrumentation' +import { Resource } from '@opentelemetry/resources' + +// Configure the trace provider +const provider = new NodeTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: 'example application', + }), +}) + +// Configure how spans are processed and exported. In this case we're sending spans +// as we receive them to an OTLP-compatible collector (e.g. Jaeger). +provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter())) + +// Register your auto-instrumentors +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [new PrismaInstrumentation()], +}) + +// Register the provider globally +provider.register() +``` + +OpenTelemetry is highly configurable. You can customize the resource attributes, what components gets instrumented, how spans are processed, and where spans are sent. + +You can find a complete example that includes metrics in [this sample application](https://github.com/garrensmith/prisma-metrics-sample). + +## Tracing how-tos + +### Visualize traces with Jaeger + +[Jaeger](https://www.jaegertracing.io/) is a free and open source OpenTelemetry collector and dashboard that you can use to visualize your traces. + +The following screenshot shows an example trace visualization: + +![Jaeger UI](./jaeger.png) + +To run Jaeger locally, use the following [Docker](https://www.docker.com/) command: + +```console +docker run --rm --name jaeger -d -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest +``` + +You'll now find the tracing dashboard available at `http://localhost:16686/`. When you use your application with tracing enabled, you'll start to see traces in this dashboard. + +### Send tracing output to the console + +The following example sends output tracing to the console with `ConsoleSpanExporter` from `@opentelemetry/sdk-trace-base`. + +```ts +// Imports +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { registerInstrumentations } from '@opentelemetry/instrumentation' +import { + BasicTracerProvider, + ConsoleSpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-base' +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks' +import * as api from '@opentelemetry/api' +import { PrismaInstrumentation } from '@prisma/instrumentation' +import { Resource } from '@opentelemetry/resources' + +// Export the tracing +export function otelSetup() { + const contextManager = new AsyncHooksContextManager().enable() + + api.context.setGlobalContextManager(contextManager) + + //Configure the console exporter + const consoleExporter = new ConsoleSpanExporter() + + // Configure the trace provider + const provider = new BasicTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: 'test-tracing-service', + [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', + }), + }) + + // Configure how spans are processed and exported. In this case we're sending spans + // as we receive them to the console + provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter)) + + // Register your auto-instrumentors + registerInstrumentations({ + tracerProvider: provider, + instrumentations: [new PrismaInstrumentation()], + }) + + // Register the provider + provider.register() +} +``` + +### Trace Prisma Client middleware + +By default, tracing does not output spans for [Prisma Client middleware](/orm/prisma-client/client-extensions/middleware). To include your middleware in your traces, set `middleware` to `true` in your `registerInstrumentations` statement, as follows: + +```ts +registerInstrumentations({ + instrumentations: [new PrismaInstrumentation({ middleware: true })], +}) +``` + +This will add the following span type to your traces: + +- `prisma:client:middleware`: Represents how long the operation spent in your [middleware](/orm/prisma-client/client-extensions/middleware). + +### Trace interactive transactions + +When you perform an interactive transaction, you'll see the following spans in addition to the [standard spans](#trace-output): + +- `prisma:client:transaction`: A [root span](https://opentelemetry.io/docs/concepts/observability-primer/#distributed-traces) that wraps the `prisma` span. + - `prisma:engine:itx_runner`: Represents how long an interactive transaction takes in the [query engine](/orm/more/under-the-hood/engines). + - `prisma:engine:itx_query_builder`: Represents the time it takes to build an interactive transaction. + +As an example, take the following Prisma schema: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["tracing", "interactiveTransactions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique +} + +model Audit { + id Int @id + table String + action String +} +``` + +Given the following interactive transaction: + +```ts +await prisma.$transaction(async (tx) => { + const user = await tx.user.create({ + data: { + email: email, + }, + }) + + await tx.audit.create({ + data: { + table: 'user', + action: 'create', + id: user.id, + }, + }) + + return user +}) +``` + +The trace is structured as follows: + +- `prisma:client:transaction` +- `prisma:client:connect` +- `prisma:engine:itx_runner` + - `prisma:engine:connection` + - `prisma:engine:db_query` + - `prisma:engine:itx_query_builder` + - `prisma:engine:db_query` + - `prisma:engine:db_query` + - `prisma:engine:serialize` + - `prisma:engine:itx_query_builder` + - `prisma:engine:db_query` + - `prisma:engine:db_query` + - `prisma:engine:serialize` +- `prisma:client:operation` + - `prisma:client:serialize` +- `prisma:client:operation` + - `prisma:client:serialize` + +### Add more instrumentation + +A nice benefit of OpenTelemetry is the ability to add more instrumentation with only minimal changes to your application code. + +For example, to add HTTP and [ExpressJS](https://expressjs.com/) tracing, add the following instrumentations to your OpenTelemetry configuration. These instrumentations add spans for the full request-response lifecycle. These spans show you how long your HTTP requests take. + +```js +// Imports +import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express' +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http' + +// Register your auto-instrumentors +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + new HttpInstrumentation(), + new ExpressInstrumentation(), + new PrismaInstrumentation(), + ], +}) +``` + +For a full list of available instrumentation, take a look at the [OpenTelemetry Registry](https://opentelemetry.io/registry/?language=js&component=instrumentation). + +### Customize resource attributes + +You can adjust how your application's traces are grouped by changing the resource attributes to be more specific to your application: + +```js +const provider = new NodeTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: 'weblog', + [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', + }), +}) +``` + +There is an ongoing effort to standardize common resource attributes. Whenever possible, it's a good idea to follow the [standard attribute names](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/README.md). + +### Reduce performance impact + +If your application sends a large number of spans to a collector, this can have a significant performance impact. You can use the following approaches to reduce this impact: + +- [Use the BatchSpanProcessor](#send-traces-in-batches-using-the-batchspanprocessor) +- [Send fewer spans to the collector](#send-fewer-spans-to-the-collector-with-sampling) + +#### Send traces in batches using the `BatchSpanProcessor` + +In a production environment, you can use OpenTelemetry's `BatchSpanProcessor` to send the spans to a collector in batches rather than one at a time. However, during development and testing, you might not want to send spans in batches. In this situation, you might prefer to use the `SimpleSpanProcessor`. + +You can configure your tracing configuration to use the appropriate span processor, depending on the environment, as follows: + +```ts +import { + SimpleSpanProcessor, + BatchSpanProcessor, +} from '@opentelemetry/sdk-trace-base' + +if (process.env.NODE_ENV === 'production') { + provider.addSpanProcessor(new BatchSpanProcessor(otlpTraceExporter)) +} else { + provider.addSpanProcessor(new SimpleSpanProcessor(otlpTraceExporter)) +} +``` + +#### Send fewer spans to the collector with sampling + +Another way to reduce the performance impact is to [use probability sampling](https://opentelemetry.io/docs/reference/specification/trace/tracestate-probability-sampling/) to send fewer spans to the collector. This reduces the collection cost of tracing but still gives a good representation of what is happening in your application. + +An example implementation looks like this: + +```ts highlight=3,7;add +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' +import { TraceIdRatioBasedSampler } from '@opentelemetry/core' +import { Resource } from '@opentelemetry/resources' + +const provider = new NodeTracerProvider({ + sampler: new TraceIdRatioBasedSampler(0.1), + resource: new Resource({ + // we can define some metadata about the trace resource + [SemanticResourceAttributes.SERVICE_NAME]: 'test-tracing-service', + [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', + }), +}) +``` + +## Troubleshoot tracing + +### My traces aren't showing up + +The order in which you set up tracing matters. In your application, ensure that you register tracing and instrumentation before you import any instrumented dependencies. For example: + +```ts +import { registerTracing } from './tracing' + +registerTracing({ + name: 'tracing-example', + version: '0.0.1', +}) + +// You must import any dependencies after you register tracing. +import { PrismaClient } from '@prisma/client' +import async from 'express-async-handler' +import express from 'express' +``` + +### Child traces start before parent traces + +We're still investigating [this issue](https://github.com/prisma/prisma/issues/14612). diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/index.mdx b/docs/200-orm/200-prisma-client/600-observability-and-logging/index.mdx new file mode 100644 index 0000000000..f34f9635d0 --- /dev/null +++ b/docs/200-orm/200-prisma-client/600-observability-and-logging/index.mdx @@ -0,0 +1,10 @@ +--- +title: 'Observability & logging' +metaTitle: 'Observability & logging' +metaDescription: 'Observability & logging' +tocDepth: 3 +--- + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/jaeger.png b/docs/200-orm/200-prisma-client/600-observability-and-logging/jaeger.png new file mode 100644 index 0000000000..0a5a124dbe Binary files /dev/null and b/docs/200-orm/200-prisma-client/600-observability-and-logging/jaeger.png differ diff --git a/docs/200-orm/200-prisma-client/600-observability-and-logging/trace-diagram.png b/docs/200-orm/200-prisma-client/600-observability-and-logging/trace-diagram.png new file mode 100644 index 0000000000..86707a8aa5 Binary files /dev/null and b/docs/200-orm/200-prisma-client/600-observability-and-logging/trace-diagram.png differ diff --git a/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/140-debugging.mdx b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/140-debugging.mdx new file mode 100644 index 0000000000..30be2c7216 --- /dev/null +++ b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/140-debugging.mdx @@ -0,0 +1,49 @@ +--- +title: 'Debugging' +metaTitle: 'Debugging (Reference)' +metaDescription: 'This page explains how to enable debugging output for Prisma Client by setting the `DEBUG` environment variable.' +--- + + + +You can enable debugging output in Prisma Client via the [`DEBUG`](/orm/reference/environment-variables-reference#debug) environment variable. It accepts two namespaces to print debugging output: + +- `prisma:engine`: Prints relevant debug messages happening in a Prisma [engine](https://github.com/prisma/prisma-engines/) +- `prisma:client`: Prints relevant debug messages happening in the Prisma Client runtime +- `prisma*`: Prints all debug messages from Prisma Client or CLI +- `*`: Prints all debug messages + + + +Prisma Client can be configured to log warnings, errors and information related to queries sent to the database. See [Configuring logging](/orm/prisma-client/observability-and-logging/logging) for more information. + + + + + +## Setting the `DEBUG` environment variable + +Here are examples for setting these debugging options in bash: + +```terminal +# enable only `prisma:engine`-level debugging output +export DEBUG="prisma:engine" + +# enable only `prisma:client`-level debugging output +export DEBUG="prisma:client" + +# enable both `prisma-client`- and `engine`-level debugging output +export DEBUG="prisma:client,prisma:engine" +``` + +To enable all `prisma` debugging options, set `DEBUG` to `prisma*`: + +```terminal +export DEBUG="prisma*" +``` + +To enable _all_ debugging options, set `DEBUG` to `*`: + +```terminal +export DEBUG="*" +``` diff --git a/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/230-handling-exceptions-and-errors.mdx b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/230-handling-exceptions-and-errors.mdx new file mode 100644 index 0000000000..6faf5c4319 --- /dev/null +++ b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/230-handling-exceptions-and-errors.mdx @@ -0,0 +1,45 @@ +--- +title: 'Handling exceptions and errors' +metaTitle: 'Handling exceptions and errors (Reference)' +metaDescription: 'This page covers how to handle exceptions and errors' +--- + + + +In order to handle different types of errors you can use `instanceof` to check what the error is and handle it accordingly. + +The following example tries to create a user with an already existing email record. This will throw an error because the `email` field has the `@unique` attribute applied to it. + +```prisma file=schema.prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? +} +``` + +Use the `Prisma` namespace to access the error type. The [error code](/orm/reference/error-reference#error-codes) can then be checked and a message can be printed. + +```ts +import { PrismaClient, Prisma } from '@prisma/client' + +const client = new PrismaClient() + +try { + await client.user.create({ data: { email: 'alreadyexisting@mail.com' } }) +} catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + // The .code property can be accessed in a type-safe manner + if (e.code === 'P2002') { + console.log( + 'There is a unique constraint violation, a new user cannot be created with this email' + ) + } + } + throw e +} +``` + +See [Errors reference](/orm/reference/error-reference) for a detailed breakdown of the different error types and their codes. + + diff --git a/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/index.mdx b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/index.mdx new file mode 100644 index 0000000000..bb23db2ab7 --- /dev/null +++ b/docs/200-orm/200-prisma-client/700-debugging-and-troubleshooting/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Debugging & troubleshooting' +metaTitle: 'Debugging & troubleshooting' +metaDescription: 'Debugging & troubleshooting' +tocDepth: 3 +--- + + + +Debugging & troubleshooting + + + +## In this section + + diff --git a/docs/200-orm/200-prisma-client/index.mdx b/docs/200-orm/200-prisma-client/index.mdx new file mode 100644 index 0000000000..c7b3fda5f6 --- /dev/null +++ b/docs/200-orm/200-prisma-client/index.mdx @@ -0,0 +1,11 @@ +--- +title: 'Prisma Client' +metaTitle: 'Prisma Client' +metaDescription: 'Prisma Client is an auto-generated, type-safe query builder generated based on the models and attributes of your Prisma schema.' +staticLink: true +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/300-prisma-migrate/050-getting-started.mdx b/docs/200-orm/300-prisma-migrate/050-getting-started.mdx new file mode 100644 index 0000000000..42c0b39665 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/050-getting-started.mdx @@ -0,0 +1,253 @@ +--- +title: 'Getting started' +metaTitle: 'Getting started' +metaDescription: 'Getting started' +tocDepth: 3 +--- + + + +This page explains how to get started with migrating your schema in a development environment using Prisma Migrate. See [Developing with Prisma Migrate](/orm/prisma-migrate) for a more in-depth development workflow. + + + +## Get started with Prisma Migrate from scratch + +To get started with Prisma Migrate in a development environment: + +1. Create a Prisma schema: + + ```prisma file=schema.prisma + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model User { + id Int @id @default(autoincrement()) + name String + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + authorId Int + author User @relation(fields: [authorId], references: [id]) + } + ``` + + :::tip + + You can use [native type mapping attributes](/orm/prisma-migrate/workflows/native-database-types) in your schema to decide which exact database type to create (for example, `String` can map to `varchar(100)` or `text`). + + ::: + + + + 1. Create the first migration: + + + + + + ```terminal + prisma migrate dev --name init + ``` + + + + + + ```sql no-copy + -- CreateTable + CREATE TABLE "User" ( + "id" SERIAL, + "name" TEXT NOT NULL, + + PRIMARY KEY ("id") + ); + -- CreateTable + CREATE TABLE "Post" ( + "id" SERIAL, + "title" TEXT NOT NULL, + "published" BOOLEAN NOT NULL DEFAULT true, + "authorId" INTEGER NOT NULL, + + PRIMARY KEY ("id") + ); + + -- AddForeignKey + ALTER TABLE "Post" ADD FOREIGN KEY("authorId")REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ``` + + + + > **Note**: If you do not provide a `--name`, Prisma CLI will prompt you for a name. + + + + Your Prisma schema is now in sync with your database schema and you have initialized a migration history: + + ``` + migrations/ + └─ 20210313140442_init/ + └─ migration.sql + ``` + +1. Add additional fields to your schema: + + ```prisma highlight=3;add + model User { + id Int @id @default(autoincrement()) + jobTitle String + name String + posts Post[] + } + ``` + +1. Create the second migration: + + + + + + ```terminal + prisma migrate dev --name added_job_title + ``` + + + + + + ```sql no-copy + -- AlterTable + ALTER TABLE "User" ADD COLUMN "jobTitle" TEXT NOT NULL; + ``` + + + + + + Your Prisma schema is once again in sync with your database schema, and your migration history contains two migrations: + + ``` + migrations/ + └─ 20210313140442_init/ + └─ migration.sql + └─ 20210313140442_added_job_title/ + └─ migration.sql + ``` + + + +You now have a migration history that you can [source control](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories#committing-the-migration-history-to-source-control) and use to [deploy changes to test environments and production](/orm/prisma-migrate/workflows/development-and-production#production-and-testing-environments). + +## Adding Prisma Migrate to an existing project + +The steps involved in **adding Prisma Migrate to your existing project** are: + +1. Introspect your database to update your Prisma schema +1. Create a baseline migration +1. Update your schema or migration to workaround features not supported by Prisma Schema Language +1. Apply the baseline migration +1. Commit the migration history and Prisma schema + +### Introspect to create or update your Prisma schema + +Make sure your Prisma schema is in sync with your database schema. This should already be true if you are using a previous version of Prisma Migrate. + +1. Introspect the database to make sure that your Prisma schema is up-to-date: + + ```terminal + prisma db pull + ``` + +### Create a baseline migration + +Baselining is the process of initializing a migration history for a database that: + +- ✔ Existed before you started using Prisma Migrate +- ✔ Contains data that must be maintained (like production), which means that the database cannot be reset + +Baselining tells Prisma Migrate to assume that one or more migrations have **already been applied**. This prevents generated migrations from failing when they try to create tables and fields that already exist. + +To create a baseline migration: + +1. If you have a `prisma/migrations` folder, delete, move, rename, or archive this folder. + +1. Run the following command to create a `migrations` directory inside with your preferred name. This example will use `0_init` for the migration name: + + ```terminal + mkdir -p prisma/migrations/0_init + ``` + + + + The `0_` is important because Prisma Migrate applies migrations in a [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order). You can use a different value such as the current timestamp. + + + +1. Generate a migration and save it to a file using `prisma migrate diff` + + ```terminal no-lines + npx prisma migrate diff \ + --from-empty \ + --to-schema-datamodel prisma/schema.prisma \ + --script > prisma/migrations/0_init/migration.sql + ``` + +1. Review the generated migration + +### Work around features not supported by Prisma Schema Language + +To include [unsupported database features](/orm/prisma-migrate/workflows/unsupported-database-features) that already exist in the database, you must replace or modify the initial migration SQL: + +1. Open the `migration.sql` file generated in the [Create a baseline migration](#create-a-baseline-migration) section. + +1. Modify the generated SQL. For example: + + - If the changes are minor, you can append additional custom SQL to the generated migration - the following example creates a partial index: + + ```sql highlight=3,4;add + /* Generated migration SQL */ + + CREATE UNIQUE INDEX tests_success_constraint ON posts (subject, target) + WHERE success; + ``` + + - If the changes are significant, it can be easier to replace the entire migration file with the result of a database dump ([`mysqldump`](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html), [`pg_dump`](https://www.postgresql.org/docs/12/app-pgdump.html)) + + + Note that the order of the tables matters when creating all of them at once, + since foreign keys are created at the same step. Therefore, either re-order + them or move constraint creation to the last step after all tables are + created, so you won't face `can't create constraint` errors + + +### Apply the initial migrations + +To apply your initial migration(s): + +1. Run the following command against your database: + + ```terminal + npx prisma migrate resolve --applied 0_init + ``` + +1. Review the database schema to ensure the migration leads to the desired end-state (for example, by comparing the schema to the production database). + +The new migration history and the database schema should now be in sync with your Prisma schema. + +### Commit the migration history and Prisma schema + +Commit the following to source control: + +- The entire migration history folder +- The `schema.prisma` file + +## Going further + +- Refer to the [Deploying database changes with Prisma Migrate](/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate) guide for more on deploying migrations to production. +- Refer to the [Production Troubleshooting](/orm/prisma-migrate/workflows/patching-and-hotfixing#fixing-failed-migrations-with-migrate-diff-and-db-execute) guide to learn how to debug and resolve failed migrations in production using `prisma migrate diff`, `prisma db execute` and/ or `prisma migrate resolve`. diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/005-overview.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/005-overview.mdx new file mode 100644 index 0000000000..36b6d6e297 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/005-overview.mdx @@ -0,0 +1,31 @@ +--- +title: 'Overview' +metaTitle: 'Prisma Migrate Overview' +metaDescription: 'Learn everything you need to know about Prisma Migrate.' +--- + + + +**Does not apply for MongoDB**
Instead of `migrate dev` and related commands, use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) for [MongoDB](/orm/overview/databases/mongodb). + +
+ +Prisma Migrate enables you to: + +- Keep your database schema in sync with your [Prisma schema](/orm/prisma-schema) as it evolves _and_ +- Maintain existing data in your database + +Prisma Migrate generates [a history of `.sql` migration files](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories), and plays a role in both [development and production](/orm/prisma-migrate/workflows/development-and-production). + +Prisma Migrate can be considered a _hybrid_ database schema migration tool, meaning it has both of _declarative_ and _imperative_ elements: + +- Declarative: The data model is described in a declarative way in the [Prisma schema](/orm/prisma-schema). Prisma Migrate generates SQL migration files from that data model. +- Imperative: All generated SQL migration files are fully customizable. Prisma Migrate hence provides the flexibility of an imperative migration tool by enabling you to modify what and how migrations are executed (and allows you to run custom SQL to e.g. make use of native database feature, perform data migrations, ...). + +:::tip + +If you are prototyping, consider using the [`db push`](/orm/reference/prisma-cli-reference#db-push) command - see [Schema prototyping with `db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) for examples. + +::: + +See the [Prisma Migrate reference](/orm/reference/prisma-cli-reference#prisma-migrate) for detailed information about the Prisma Migrate CLI commands. diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/010-mental-model.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/010-mental-model.mdx new file mode 100644 index 0000000000..6d22f7564b --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/010-mental-model.mdx @@ -0,0 +1,178 @@ +--- +title: 'Mental model' +metaTitle: 'A mental model for Prisma Migrate' +metaDescription: 'A mental model guide for working with Prisma Migrate in your project' +tocDepth: 4 +--- + + + +This guide provides a conceptual overview of database migrations using Prisma Migrate when working with relational databases. It covers: what database migrations are, their value, and what Prisma Migrate is and how you can evolve your database schema with Prisma Migrate in different environments. + +**If you are working with MongoDB, use `prisma db push` to evolve your schema.** + + + +## What are database migrations? + +Database migrations are a controlled set of changes that modify and evolve the structure of your database schema. Migrations help you transition your database schema from one state to another. For example, within a migration you can create or remove tables and columns, split fields in a table, or add types and constraints to your database. + +### Patterns for evolving database schemas + +This section describes general schema migration patterns for evolving database schemas. + +The two main schema migration patterns are: + +- **Model/Entity-first migration:** with this pattern, you define the structure of the database schema with code and then use a migration tool to generate the SQL, for example, for syncing your application and database schema. + +![](./mental-model-illustrations/entity-first-migration-flow.png) + +- **Database-first migration:** with this pattern, you define the structure of your database and apply it to your database using SQL. You then _introspect_ the database to generate the code that describes the structure of your database to sync your application and database schema. + +![](./mental-model-illustrations/database-first-migration-flow.png) + + + +**Note** + +For simplicity, we chose the terminology above to describe the different patterns for evolving database schemas. Other tools and libraries may use different terminology to describe the different patterns. + + + +The migration files (SQL) should ideally be stored together with your application code. They should also be tracked in version control and shared with the rest of the team working on the application. + +Migrations provide _state management_ which helps you to track the state of the database. + +Migrations also allow you to replicate the state of a database at a specific point in time which is useful when collaborating with other members of the team, e.g. switching between different branches. + +For further information on database migrations, see the [Prisma Data Guide](https://www.prisma.io/dataguide/types/relational/what-are-database-migrations). + +## What is Prisma Migrate? + +Prisma Migrate is a database migration tool that supports the _model/ entity-first_ migration pattern to manage database schemas in your local environment and in production. + +The workflow when using Prisma Migrate in your project would be iterative and look like this: + +**Local development environment (Feature branch)** + +1. Evolve your Prisma schema +1. Use either [`prisma migrate dev`](#track-your-migration-history-with-prisma-migrate-dev) or [`prisma db push`](#prototype-your-schema) to sync your Prisma schema with the database schema of your local development database + +**Preview/ staging environment(Feature pull request)** + +1. Push your changes to the feature pull request +1. Use a CI system (e.g. GitHub Actions) to sync your Prisma schema and migration history with your preview database using `prisma migrate deploy` + +**Production (main branch)** + +1. Merge your application code from the feature branch to your main branch +1. Use a CI system (e.g. GitHub Actions) to sync your Prisma schema and migration history with your production database using `prisma migrate deploy` + +![Prisma Migrate workflow](./mental-model-illustrations/prisma-migrate-lifecycle.png) + +## How Prisma Migrate tracks the migration state + +Prisma Migrate uses the following pieces of state to track the state of your database schema: + +- **Prisma schema**: your source of truth that defines the structure of the database schema. +- **Migrations history**: SQL files in your `prisma/migrations` folder representing the history of changes made to your database schema. +- **Migrations table**: `prisma_migrations` table in the database that stores metadata for migrations that have been applied to the database. +- **Database schema**: the state of the database. + +![Prisma Migrate "state management"](./mental-model-illustrations/prisma-migrate-state-mgt.png) + +## Requirements when working with Prisma Migrate + +- Ideally, you should use one database per environment. For example, you might have a separate database for development, preview, and production environments. +- The databases you use in development environments are disposable — you can easily create, use, and delete databases on demand. +- The database configuration used in each environments should be consistent. This is important to ensure a certain migration that moves across the workflow yields the same changes to the database. +- The Prisma schema serves as the source of truth — describing the shape of your [database schema](https://www.prisma.io/dataguide/intro/database-glossary#schema). + +## Evolve your database schema with Prisma Migrate + +This section describes how you can evolve your database schema in different environments: development, staging, and production, using Prisma Migrate. + +### Prisma Migrate in a development environment (local) + +#### Track your migration history with `prisma migrate dev` + +The [`prisma migrate dev`](/orm/reference/prisma-cli-reference#migrate-dev) command allows you to track the changes you make to your database. The `prisma migrate dev` command automatically generates SQL migration files (saved in `/prisma/migrations`) and applies them to the database. When a migration is applied to the database, the migrations table (`_prisma_migrations`) in your database is also updated. + +![Prisma Migrate dev flow](./mental-model-illustrations/prisma-migrate-dev-flow.png) + +The `prisma migrate dev` command tracks the state of the database using the following pieces of state: + +- the Prisma schema +- the migrations history +- the migrations table +- the database schema + +> **Note**: The pieces of state used to track the state of a migration are the same as the ones described in [how Prisma Migrate tracks the migration state](#how-prisma-migrate-tracks-the-migration-state) section. + +You can customize migrations before you apply them to the database using the `--create-only` flag. For example, you might want to edit a migration if you want to rename columns without incurring any data loss or load database extensions (in PostgreSQL) and database views (currently not supported). + +Under the hood, Prisma Migrate uses a [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database) to detect a [schema drift](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database#detecting-schema-drift) and generate new migrations. + +> **Note**: `prisma migrate dev` is intended to be used only in development with a disposable database. + +If `prisma migrate dev` detects a schema drift or a migration history conflict, you will be prompted to reset (drop and recreate your database) your database to sync the migration history and the database schema. + +
+ + Expand to see the shadow database explained using a cartoon + +![A cartoon that shows how the shadow database works.](shadow-database.png) + +
+ +#### Resolve schema drifts + +A schema drift occurs when the expected database schema is different from what is in the migration history. For example, this can occur when you manually update the database schema without also updating the Prisma schema and `prisma/migrations` accordingly. + +For such instances, you can use the [`prisma migrate diff`](/orm/reference/prisma-cli-reference#migrate-diff) command to compare your migration history and revert changes made to your database schema. + +![Revert database schema with `migrate diff`](./mental-model-illustrations/prisma-migrate-diff-flow.png) + +You can use `migrate diff` to generate the SQL that either: + +- Reverts the changes made in the database schema to synchronize it with the current Prisma schema +- Moves your database schema forward to apply missing changes from the Prisma schema and `/migrations` + +You can then apply the changes to your database using [`prisma db execute`](/orm/reference/prisma-cli-reference#db-execute) command. + +#### Prototype your schema + +The [`prisma db push`](/orm/reference/prisma-cli-reference#db-push) command allows you to sync your Prisma schema and database schema without persisting a migration (`/prisma/migrations`). The `prisma db push` command tracks the state of the database using the following pieces of state: + +- the Prisma schema +- the database schema + +![prisma db push development flow](./mental-model-illustrations/db-push-flow.png) + +The `prisma db push` command is useful when: + +- You want to **quickly prototype and iterate** on schema design locally without the need to deploy these changes to other environments such as other developers, or staging and production environments. +- You are prioritizing reaching a **desired end-state** and not the changes or steps executed to reach that end-state (there is no way to preview changes made by `prisma db push`) +- You do not need to control how schema changes impact data. There is no way to orchestrate schema and data migrations - if `prisma db push` anticipates that changes will result in data loss, you can either accept data loss with the `--accept-data-loss` option or stop the process - there is no way to customize the changes. + +If the `prisma db push` command detects destructive change to your database schema, it will prompt you to reset your database. For example, this will happen when you add a required field to a table with existing content without providing a default value. + +> A [schema drift](/orm/prisma-migrate/workflows/troubleshooting#schema-drift) occurs when your database schema is out of sync with your migrations history and migrations table. + +### Prisma Migrate in a staging and production environment + +#### Sync your migration histories + +The [`prisma migrate deploy`](/orm/reference/prisma-cli-reference#migrate-deploy) command allows you to sync your migration history from your development environment with your database in your **staging or production environment**. + +Under the hood, the `migrate deploy` command: + +1. Compares already applied migrations (captured `_prisma_migrations`) and the migration history (`/prisma/migrations`) +1. Applies pending migrations +1. Updates `_prisma_migrations` table with the new migrations + +![](../300-workflows/deploy-db.png) + +The command should be run in an automated CI/ CD environment, for example GitHub Actions. + +If you don't have a migration history (`/migrations`), i.e using `prisma db push`, you will have to continue using `prisma db push` in your staging and production environments. Beware of the changes being applied to the database schema as some of them might be destructive. For example, `prisma db push` can't tell when you're performing a column rename. It will prompt a database reset (drop and re-creation). diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/070-migration-histories.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/070-migration-histories.mdx new file mode 100644 index 0000000000..b6fe72d92a --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/070-migration-histories.mdx @@ -0,0 +1,90 @@ +--- +title: 'About migration histories' +metaTitle: 'About migration histories' +metaDescription: 'About migration histories' +tocDepth: 3 +--- + + + +This page explains how Prisma uses migration histories to track changes to your schema. + + + +## Migration history + +Your migration history is the story of the changes to your data model, and is represented by: + +- A `prisma/migrations` folder with a sub-folder and `migration.sql` file for each migration: + + ``` + migrations/ + └─ 20210313140442_init/ + └─ migration.sql + └─ 20210313140442_added_job_title/ + └─ migration.sql + ``` + + The `migrations` folder is the **source of truth** for the history of your data model. + +- A `_prisma_migrations` table in the database, which is used to check: + + - If a migration was run against the database + - If an applied migration was deleted + - If an applied migration was changed + + If you change or delete a migration (**not** recommended), the next steps depend on whether you are in a [development environment](/orm/prisma-migrate/workflows/development-and-production#development-environments) (and therefore using `migrate dev`) or a [production / testing environment](/orm/prisma-migrate/workflows/development-and-production#production-and-testing-environments) (and therefore using `migrate deploy`). + +### Do not edit or delete migrations that have been applied + +In general, you **should not edit or delete** a migration that has already been applied. Doing so can lead to inconsistencies between development and production environment migration histories, which may have unforeseen consequences - even if the change does not _appear_ to break anything at first. + +The following scenario simulates a change that creates a seemingly harmless inconsistency: + +1. Modify an **existing migration** that has **already been applied** in a development environment by changing the value of `VARCHAR(550)` to `VARCHAR(560)`: + + ```sql file=./prisma/migrations/20210310143435_default_value/migrations.sql + -- AlterTable + ALTER TABLE "Post" ALTER COLUMN "content" SET DATA TYPE VARCHAR(560); + ``` + + After making this change, the end state of the migration history no longer matches the Prisma schema, which still has `@db.VarChar(550)`. + +1. Run `prisma migrate dev` - Prisma Migrate detects that a migration has changed, and asks to `reset` the database: + + ```bash + ? The migration `20210310143435_change_type` was modified after it was applied. + + We need to reset the PostgreSQL database "migrate-example" at "localhost:5432". + Do you want to continue? All data will be lost. » (y/N) + ``` + +1. If you accept resetting, Prisma Migrate resets the database and replays all migrations, including the migration you edited. + +1. After applying all existing migrations, Prisma Migrate compares the end state of the migration history to the Prisma schema and detects a discrepancy: + + - Prisma schema has `@db.VarChar(550)` + - Database schema has `VARCHAR(560)` + +1. Prisma Migrate generates a new migration to change the value back to `550`, because the end state of the migration history should match the Prisma schema. + +1. From now on, when you use `prisma migrate deploy` to deploy migrations to production and test environments, Prisma Migrate will always **warn you** that migration histories do not match (and continue to warn you each time you run the command ) - even though the schema end states match: + + ```bash + 6 migrations found in prisma/migrations + WARNING The following migrations have been modified since they were applied: + 20210310143435_change_type + ``` + +A change that does not appear to break anything after a `migrate reset` can hide problems - you may end up with a bug in production that you cannot replicate in development, or the other way around - particularly if the change concerns a highly customized migration. + +If Prisma Migrate reports a missing or edited migration that has already been applied, we recommend fixing the **root cause** (restoring the file or reverting the change) rather than resetting. + +### Committing the migration history to source control + +You must commit the entire `prisma/migrations` folder to source control. This includes the `prisma/migrations/migration_lock.toml` file, which is used to detect if you have [attempted to change providers](/orm/prisma-migrate/understanding-prisma-migrate/limitations-and-known-issues#you-cannot-automatically-switch-database-providers). + +Source-controlling the `schema.prisma` file is not enough - you must include your migration history. This is because: + +- As you start to [customize migrations](/orm/prisma-migrate/workflows/development-and-production#customizing-migrations), your migration history contains **information that cannot be represented in the Prisma schema**. For example, you can customize a migration to mitigate data loss that would be caused by a breaking change. +- The `prisma migrate deploy` command, which is used to deploy changes to staging, testing, and production environments, _only_ runs migration files. It does not use the Prisma schema file to fetch the models. diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/200-shadow-database.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/200-shadow-database.mdx new file mode 100644 index 0000000000..c3bdc8ad8a --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/200-shadow-database.mdx @@ -0,0 +1,146 @@ +--- +title: 'About the shadow database' +metaTitle: 'About the shadow database' +metaDescription: 'About the shadow database' +--- + + + +The shadow database is a second, _temporary_ database that is **created and deleted automatically**\* each time you run `prisma migrate dev` and is primarily used to **detect problems** such as schema drift or potential data loss of the generated migration. + +[`migrate diff` command](/orm/reference/prisma-cli-reference#migrate-diff) also requires a shadow database when diffing against a local `migrations` directory with `--from-migrations` or `--to-migrations`. + +:::info + +If your database does not allow creation and deleting of databases (e.g. in a cloud-hosted environment), you need to [create and configure the shadow database manually](#cloud-hosted-shadow-databases-must-be-created-manually). + +::: + +:::info + +The shadow database is **not** required in production, and is not used by production-focused commands such as `prisma migrate resolve` and `prisma migrate deploy`. + +::: + +:::info + +A shadow database is never used for MongoDB as `migrate dev` is not used there. + +::: + + + +## How the shadow database works + +When you run `prisma migrate dev` to create a new migration, Prisma Migrate uses the shadow database to: + +- [Detect schema drift](#detecting-schema-drift), which means checking that no **unexpected changes** have been made to the development database +- [Generate new migrations](#generating-new-migrations) and evaluate if those could lead to **data loss** when applied + +
+ +🎨 Expand to see the shadow database explained as a cartoon. + +![A cartoon that shows how the shadow database works.](shadow-database.png) + +
+ +### Detecting schema drift + +To detect drift in development, Prisma Migrate: + +1. Creates a fresh copy of the shadow database (or performs a soft reset if the shadow database is configured via [`shadowDatabaseUrl`](/orm/reference/prisma-schema-reference#datasource)) +1. Reruns the **current**, existing migration history in the shadow database. +1. **Introspects** the shadow database to generate the 'current state' of your Prisma schema. +1. Compares the end state of the current migration history to the development database. +1. Reports **schema drift** if the end state of the current migration history (via the shadow database) does not match the development database (for example, due to a manual change) + +If Prisma Migrate does not detect schema drift, it moves on to [generating new migrations](#generating-new-migrations). + +> **Note**: The shadow database is not responsible for checking if a migration file has been **edited or deleted**. This is done using the `checksum` field in the `_prisma_migrations` table. + +If Prisma Migrate detects schema drift, it outputs detailed information about which parts of the database have drifted. The following example output could be shown when the development database has been modified manually: The `Color` enum is missing the expected variant `RED` and includes the unexpected variant `TRANSPARENT`: + +``` +[*] Changed the `Color` enum + [+] Added variant `TRANSPARENT` + [-] Removed variant `RED` +``` + +### Generating new migrations + +Assuming Prisma Migrate did not [detect schema drift](#detecting-schema-drift), it moves on to generating new migrations from Prisma schema changes. To generate new migrations, Prisma Migrate: + +1. Calculates the target database schema as a function of the current Prisma schema. +1. Compares the end state of the existing migration history and the target schema, and generates steps to get from one to the other. +1. Renders these steps to a SQL string and saves it in the new migration file. +1. Evaluate data loss caused by the SQL and warns about that. +1. Applies the generated migration to the development database (assuming you have not specified the `--create-only` flag) +1. Drops the shadow database (shadow databases configured via [`shadowDatabaseUrl`](/orm/reference/prisma-schema-reference#datasource) are not dropped, but are reset at the start of the `migrate dev` command) + +## Manually configuring the shadow database + +In some cases it might make sense (e.g. when [creating and dropping databases is not allowed on cloud-hosted databases](#cloud-hosted-shadow-databases-must-be-created-manually)) to manually define the connection string and name of the database that should be used as the shadow database for `migrate dev`. In such a case you can: + +1. Create a dedicated database that should be used as the shadow database +2. Add the connection string of that database your environment variable `SHADOW_DATABASE_URL` (or `.env` file) +3. Add the [`shadowDatabaseUrl`](/orm/reference/prisma-schema-reference#datasource) field reading this environment variable: + +```prisma highlight=4;normal +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") +} +``` + +> **Important**: Do not use the exact same values for `url` and `shadowDatabaseUrl` as that might delete all your database in your database. + +## Cloud-hosted shadow databases must be created manually + +Some cloud providers do not allow you to drop and create databases with SQL. Some require to create or drop the database via an online interface, and some really limit you to 1 database. If you **develop** in such a cloud-hosted environment, you must: + +1. Create a dedicated cloud-hosted shadow database +2. Add the URL to your environment variable `SHADOW_DATABASE_URL` +3. Add the [`shadowDatabaseUrl`](/orm/reference/prisma-schema-reference#datasource) field reading this environment variable: + +```prisma highlight=4;normal +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") +} +``` + +> **Important**: Do not use the same values for `url` and `shadowDatabaseUrl`. + +## Shadow database user permissions + +In order to create and delete the shadow database when using `migrate dev`, Prisma Migrate currently requires that the database user defined in your `datasource` has permission to **create databases**. + +| Database | Database user requirements | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SQLite | No special requirements. | +| MySQL | Database user must have `CREATE, ALTER, DROP, REFERENCES ON *.*` privileges | +| PostgreSQL | The user must be a super user or have `CREATEDB` privilege. See `CREATE ROLE` ([PostgreSQL official documentation](https://www.postgresql.org/docs/12/sql-createrole.html)) | +| Microsoft SQL Server | The user must be a site admin or have the `SERVER` securable. See the [official documentation](https://docs.microsoft.com/en-us/sql/relational-databases/security/permissions-database-engine?view=sql-server-ver15). | + +> If you use a cloud-hosted database for development and can not use these permissions, see: [Cloud-hosted shadow databases](#cloud-hosted-shadow-databases-must-be-created-manually) + +> Note: The automatic creation of shadow databases is disabled on Azure SQL for example. + +Prisma Migrate throws the following error if it cannot create the shadow database with the credentials your connection URL supplied: + +``` +Error: A migration failed when applied to the shadow database +Database error: Error querying the database: db error: ERROR: permission denied to create database +``` + +To resolve this error: + +- If you are working locally, we recommend that you update the database user's privileges. +- If you are developing against a database that does not allow creating and dropping databases (for any reason) see [Manually configuring the shadow database](#manually-configuring-the-shadow-database) +- If you are developing against a cloud-based database (for example, on Heroku, Digital Ocean, or Vercel Postgres) see: [Cloud-hosted shadow databases](#cloud-hosted-shadow-databases-must-be-created-manually). +- If you are developing against a cloud-based database (for example, on Heroku, Digital Ocean, or Vercel Postgres) and are currently **prototyping** such that you don't care about generated migration files and only need to apply your Prisma data model to the database schema, you can run [`prisma db push`](/orm/reference/prisma-cli-reference#db) instead of the `prisma migrate dev` command. + +> **Important**: The shadow database is _only_ required in a development environment (specifically for the `prisma migrate dev` command) - you **do not** need to make any changes to your production environment. diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/300-limitations-and-known-issues.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/300-limitations-and-known-issues.mdx new file mode 100644 index 0000000000..4e9dffeab1 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/300-limitations-and-known-issues.mdx @@ -0,0 +1,77 @@ +--- +title: Limitations and known issues +metaTitle: Limitations and known issues +metaDescriptions: Limitations and known issues +--- + + + +The following limitations apply to Prisma Migrate. + + + +## MongoDB connector not supported + +Prisma Migrate does not currently support the MongoDB connector. + +## You cannot automatically switch database providers + +Prisma Migrate generates SQL files that are specific to your provider. This means that you cannot use the same migration files for PostgreSQL in production and SQLite in development, because the syntax in the migrations will be incompatible. + +In [2.15.0](https://github.com/prisma/prisma/releases/2.15.0) and later, Prisma Migrate detects when the migrations do not match the configured provider and prints a helpful error message. For example, if your migrations are for a PostgreSQL database but you are using a `provider` is set to `mysql`: + +``` +Error: P3014 + +The datasource provider `postgresql` specified in your schema does not match the one specified in the migration_lock.toml, mysql. Please remove your current migration directory and start a new migration history with prisma migrate dev. +``` + +In order to manually switch the database provider, you must: + +- Change the `provider` and `url` parameters in the `datasource` block in your schema +- Archive or remove your existing migration history - there must not be a `./prisma/migrations` folder +- Run `prisma migrate dev` to start a new migration history + +The last step creates a new initial migration that goes from an empty database to your current `schema.prisma`. Be aware that: + +- This migration will _only_ contain what is reflected in your `schema.prisma`. If you manually edited your previous migration files to add custom SQL you will need to again add this yourself. +- The newly created database using the new provider will not contain any data. + +## Data loss when resetting database + +In a development environment, Prisma Migrate sometimes prompts you to reset the database. Resetting drops and recreates the database, which results in data loss. The database is reset when: + +- You call `prisma migrate reset` explicitly +- You call `prisma migrate dev` and Prisma Migrate detects drift in the database or a migration history conflict + +The `prisma migrate dev` and `prisma migrate reset` commands are designed to be used **in development only**, and should not affect production data. + +When the database is reset, if Prisma Migrate detects a seed script in `package.json`, it will trigger seeding. + +> **Note**: For a simple and integrated way to re-create data when the database is reset, check out our [seeding guide](/orm/prisma-migrate/workflows/seeding). + +## Prisma Migrate and PgBouncer + +You might see the following error if you attempt to run Prisma Migrate commands in an environment that uses PgBouncer for connection pooling: + +```bash +Error: undefined: Database error +Error querying the database: db error: ERROR: prepared statement "s0" already exists +``` + +See [Prisma Migrate and PgBouncer workaround](/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer) for further information and a workaround. Follow [GitHub issue #6485](https://github.com/prisma/prisma/issues/6485) for updates. + +## Prisma Migrate in non-interactive environments + +Prisma detects when you run CLI commands in non-interactive environments, such as Docker, from Node scripts or in bash shells. When this happens a warning displays, indicating that the environment is non-interactive and the `migrate dev` command is not supported. + +To ensure the Docker environment picks up the command, run the image in `interactive` mode so that it reacts to the `migrate dev` command. + +```terminal +docker run --interactive --tty +# or +docker -it + +# Example usage +docker run -it node +``` diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/600-legacy-migrate.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/600-legacy-migrate.mdx new file mode 100644 index 0000000000..736cc55ae2 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/600-legacy-migrate.mdx @@ -0,0 +1,466 @@ +--- +title: 'Legacy Prisma Migrate' +metaTitle: 'Legacy Prisma Migrate (Reference)' +metaDescription: 'Legacy Prisma Migrate is a declarative data modeling and schema migration tool that is available via the Prisma CLI.' +tocDepth: 3 +hidePage: true +--- + + + +> **Important!** This page documents legacy Prisma Migrate (Experimental) available in version 2.12.0 and earlier. [Prisma Migrate](.) is available in version [2.13.0](https://github.com/prisma/prisma/releases/tag/2.13.0) and Generally Available in [2.19.0](https://github.com/prisma/prisma/releases/tag/2.19.0). + +Legacy Prisma Migrate is a tool that lets you _change your database schema_, e.g. by creating new tables or adding columns to existing tables. These changes are called _schema migrations_. legacy Prisma Migrate is available as part of the [Prisma CLI](/orm/tools/prisma-cli#installation) via the `legacy Prisma Migrate` command. + + + +## Documentation + +### Legacy Prisma Migrate vs the `db push` command + +If you want to prototype or iterate on a schema design in a development environment, consider the [`db push` command](/orm/reference/prisma-cli-reference#db-push). + +### Legacy Prisma Migrate vs SQL migrations + +Legacy Prisma Migrate is a _declarative_ migration system, as opposed to SQL which can be considered _imperative_: + +- **SQL (imperative)**: Provide the individual _steps_ to get from the current schema to the desired schema. +- **legacy Prisma Migrate (declarative)**: Define the desired schema as a [Prisma data model](/orm/prisma-schema/data-model/models) (legacy Prisma Migrate takes care of generating the necessary _steps_). + +Here's a quick comparison. Assume you have the following scenario: + +1. You need to create the `User` table to store user information (name, email, ...) +1. Create two new tables `Post` and `Profile` with foreign keys to `User` +1. Add a new column with a default value to the `Post` table + +#### SQL + +In SQL, you'd have to send three subsequent SQL statements to account for this scenario: + +##### 1. Create the `User` table to store user information (name, email, ...) + +```sql +CREATE TABLE "User" ( + id SERIAL PRIMARY KEY, + name VARCHAR(255), + email VARCHAR(255) NOT NULL +); +``` + +##### 2. Create two new tables `Post` and `Profile` with foreign keys to `User` + +```sql +CREATE TABLE "Profile" ( + id SERIAL PRIMARY KEY, + bio TEXT NOT NULL, + "user" integer NOT NULL UNIQUE, + FOREIGN KEY ("user") REFERENCES "User"(id) +); +CREATE TABLE "Post" ( + id SERIAL PRIMARY KEY, + title VARCHAR(255) NOT NULL, + author integer NOT NULL, + FOREIGN KEY (author) REFERENCES "User"(id) +); +``` + +##### 3. Add a new column with a default value to the `Post` table + +```sql +ALTER TABLE "Post" +ADD COLUMN published BOOLEAN DEFAULT false; +``` + +#### legacy Prisma Migrate + +With legacy Prisma Migrate, you write the desired database schema in the form of a [Prisma data model](/orm/prisma-schema/data-model/models) inside your [Prisma schema file](/orm/prisma-schema). To map the data model to your database schema, you then have to run these two commands: + +```terminal +prisma migrate save --experimental +prisma migrate up --experimental +``` + +The first command _saves_ a new migration to the `prisma/migrations` directory in the file system of your project and updates the `_Migration` table in your database. Each time you run this command to save a new migration, it creates a dedicated directory inside of `prisma/migrations` for that specific migration, which will have its own `README.md` file containing detailed information about the migration (e.g. the generated SQL statements which will be executed when you run `legacy Prisma Migrate up`). + +The second command _executes_ the migration against your database. + +##### 1. Create the `User` table to store user information (name, email, ...) + +Add the model to your Prisma schema: + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique +} +``` + +Now run the two commands mentioned above: + +```terminal +prisma migrate save --experimental +prisma migrate up --experimental +``` + +##### 2. Create two new tables `Post` and `Profile` with foreign keys to `User` + +Add two models with [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) to your Prisma schema: + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + user User @relation(fields: [userId], references: [id]) + userId Int +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int +} +``` + +Notice that in addition to the [annotated relation fields](/orm/prisma-schema/data-model/relations#annotated-relation-fields) and its relation scalar field (which represent the foreign keys), you must also specify the Prisma-level [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) on the other side of the relation. + +Now run the two commands mentioned above: + +```terminal +prisma migrate save --experimental +prisma migrate up --experimental +``` + +##### 3. Add a new column with default value to the `Post` table + +Add a [field](/orm/prisma-schema/data-model/models#defining-fields) to the `Post` model: + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] + profile Profile? +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + user User @relation(fields: [userId], references: [id]) + userId Int +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(false) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +``` + +Now run the two commands mentioned above: + +```terminal +prisma migrate save --experimental +prisma migrate up --experimental +``` + +### Supported operations + +The following table shows which SQL operations are currently supported by legacy Prisma Migrate. + +| Operation | SQL | Supported | +| :-------------------------------- | :------------------------------ | :-----------------------------------------------------------------------------------: | +| Create a new table | `CREATE TABLE` | ✔️ | +| Rename an existing table | `ALTER TABLE` + `RENAME` | No | +| Delete an existing table | `DROP TABLE` | ✔️ | +| Add a column to an existing table | `ALTER TABLE` + `ADD COLUMN` | ✔️ | +| Rename an existing column | `ALTER TABLE` + `RENAME COLUMN` | No | +| Delete an existing column | `ALTER TABLE` + `DROP COLUMN` | ✔️ | +| Set primary keys (IDs) | `PRIMARY KEY` | ✔️ | +| Define relations (foreign keys) | `FOREIGN KEY` + `REFERENCES` | ✔️ | +| Make columns optional/required | `NOT NULL` | ✔️ | +| Set unique constraints | `UNIQUE` | ✔️ | +| Set default values | `DEFAULT` | ✔️ | +| Define enums | `ENUM` | ✔️ | +| Create indexes | `CREATE INDEX` | ✔️ | +| Cascading deletes | `ON DELETE` | No (workaround: manually add in SQL and introspect) | +| Cascading updates | `ON UPDATE` | No | +| Data validation | `CHECK` | No ([workaround](/orm/more/help-and-troubleshooting/help-articles/check-constraints)) | + +Note that this table assumes that the operation is also supported by the underlying database. For example, `ENUM` is not supported in SQLite. This means that you also can't use `enum` when using legacy Prisma Migrate. + +### Migration history + +legacy Prisma Migrate stores the migration history of your project in two places: + +- A directory called `migrations` on your file system +- A table called `_Migration` in your database + +#### The `migrations` directory + +The `migrations` directory stores information about the migrations that have been or will be executed against your database. You should never make any manual changes to the files in `migrations`. The only way to change the content of this directory should be using the `legacy Prisma Migrate save` command. + +The `migrations` directory should be checked into version control (e.g. Git). + +#### The `_Migration` table + +The `_Migration` table additionally stores information about each migration that was ever executed against the database by legacy Prisma Migrate. + +### Typical workflow + +With **legacy Prisma Migrate**, the workflow looks slightly different: + +1. Manually adjust your [Prisma data model](/orm/prisma-schema/data-model/models) +1. Migrate your database using the `legacy Prisma Migrate` CLI commands +1. (Re-)generate Prisma Client +1. Use Prisma Client in your application code to access your database + +### Troubleshooting + +Since legacy Prisma Migrate is currently Experimental, you might end up in a state where the `migrations` directory and/or the `_Migrations` table are out of sync with the actual state of the database. In these cases, it often helps to "reset" legacy Prisma Migrate by deleting the `migrations` folder and deleting all entries from the `_Migration` table. + +#### Delete the `migrations` directory + +```terminal +rm -rf migrations +``` + +#### Delete all entries from the `_Migration` table + +```sql +TRUNCATE _Migration; +``` + +## CLI Reference + +> Warning: The `migrate` command is still considered Experimental. As such, there are no guarantees about API stability or production-readiness. Access to this command is provided for evaluation and experimentation. To access Experimental commands, you must add the `--experimental` flag. + +The `migrate` command creates and manages database migrations. It can be used to create, apply, and rollback database schema updates in a controlled manner. + +The `migrate` command includes a number of subcommands to specify the desired action. + +### `migrate save` + +Saves a migration that defines the steps necessary to update your current schema. + +#### Prerequisites + +Before using the `migrate save` command, you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +The `migrate save` command recognizes the following options to modify its behavior: + +| Option | Required | Description | Default | +| ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--experimental` | Yes | Enables use of Experimental commands. | | +| `-n`, `--name` | No | The name of the migration. If not provided, `migrate save` will prompt you for a name. | Timestamp `20200618145356` | +| `-c`, `--create-db` | No | Create the database if it does not exist. | | +| `-p`, `--preview` | No | Preview the migration that would be created without writing any changes to the filesystem. | | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | + +#### Generated Assets + +The `migrate save` command generates the following directories and files as necessary: + +- `migrations`: A directory within the current project to store migrations. This directory will be created if it does not exist. +- `migrations/migrate.lock`: A lock file created specifying the current migration applied to the database. This file will be created if it does not exist. +- `migrations/`: A directory for a specific migration. The migration name is derived from the timestamp when it was created followed by a hyphen and the migration name provided by the user. +- `migrations//README.md`: A human-readable description of the migration including metadata like when the migration was created and by who, a list of the actual migration changes and a diff of the changes that are made to the `schema.prisma` file. +- `migrations//schema.prisma`: The schema that will be created if the migration is applied to the project. +- `migrations//steps.json`: An [alternative representation](https://github.com/prisma/specs/tree/master/lift#step) of the migration steps that will be applied. + +#### Examples + +##### Create a new migration + +```terminal +prisma migrate save --experimental +``` + +The command will prompt you for a name for the migration since one was not provided on the command line. After creating the migration, the contents of the generated `schema.prisma` file are displayed. + +##### Create a migration with a specific name + +```terminal +prisma migrate save --name "First migration" --experimental +``` + +##### Create the database if it does not already exist + +```terminal +prisma migrate save --create-db --experimental +``` + +##### Preview the migration that would be created by running the `migrate save` command + +```terminal +prisma migrate save --preview --experimental +``` + +### `migrate up` + +Migrate the database up to a specific state. + +#### Prerequisites + +Before using the `migrate up` command, you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Arguments + +The point to migrate the database up to can be defined in any of the following three ways: + +| Argument | Required | Description | Default | +| --------- | -------- | ---------------------------------------------------------------------------------- | ------- | +| increment | No | Specifies the number of forward migrations to apply. | latest | +| name | No | Specifies where to migrate to using the name of the final migration to apply. | latest | +| timestamp | No | Specifies where to migrate to using the timestamp of the final migration to apply. | latest | + +#### Options + +Additionally, the following options modify the behavior of the `migrate up` command: + +| Option | Required | Description | Default | +| ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--experimental` | Yes | Enables use of Experimental commands | | +| `-c`, `--create-db` | No | Create the database if it does not exist. | | +| `-p`, `--preview` | No | Preview the migration that would be created without writing any changes to the filesystem. | | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | +| `--auto-approve` | No | Skip interactive approval before migrating. | | + +#### Examples + +##### Migrate the database up to the latest available migration + +```terminal +prisma migrate up --experimental +``` + +##### Apply the next two migrations to the database + +```terminal +prisma migrate up 2 --experimental +``` + +##### Apply all migrations necessary up to and including a migration by name + +```terminal +prisma migrate up "First migration" --experimental +``` + +##### Apply all migrations necessary up to and including a migration by timestamp + +```terminal +prisma migrate up 20200223181448 --experimental +``` + +##### Create the database if it does not already exist before applying the migrations + +```terminal +prisma migrate up --create-db --experimental +``` + +##### Preview the migration that would be applied by running the `migrate up` command + +```terminal +prisma migrate up --preview --experimental +``` + +### `migrate down` + +Migrate the database down to a specific state. + +#### Prerequisites + +Before using the `migrate down` command, you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Arguments + +The point to migrate back to can be defined in any of the following three ways: + +| Argument | Required | Description | Default | +| --------- | -------- | --------------------------------------------------------------------------------------- | ------- | +| decrement | No | Specifies the number of backwards migrations to apply. | 1 | +| name | No | Specifies where to migrate back to using the name of the final migration to apply. | +| timestamp | No | Specifies where to migrate back to using the timestamp of the final migration to apply. | + +#### Options + +Additionally, the following options modify the behavior of the `migrate down` command: + +| Option | Required | Description | Default | +| ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--experimental` | Yes | Enables use of Experimental commands | | +| `-p`, `--preview` | No | Preview the migration that would be created without writing any changes to the filesystem. | | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | + +#### Examples + +##### Migrate the database backwards by a single migration + +```terminal +prisma migrate down --experimental +``` + +##### Migrate the database backwards by two migrations + +```terminal +prisma migrate down 2 --experimental +``` + +##### Migrate backwards through all migrations up to and including a migration by name + +```terminal +prisma migrate down "First migration" --experimental +``` + +##### Migrate backwards through all migrations up to and including a migration by timestamp + +```terminal +prisma migrate down 20200223181448 --experimental +``` + +##### Preview the migration that would be applied by running the `migrate down` command + +```terminal +prisma migrate down --preview --experimental +``` diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/index.mdx b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/index.mdx new file mode 100644 index 0000000000..8a246de91a --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Understanding Prisma Migrate' +metaTitle: 'Understanding Prisma Migrate' +metaDescription: 'Learn about the mental model and basic building blocks of Prisma Migrate.' +--- + +## In this section + + diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/database-first-migration-flow.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/database-first-migration-flow.png new file mode 100644 index 0000000000..afaa119535 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/database-first-migration-flow.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/db-push-flow.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/db-push-flow.png new file mode 100644 index 0000000000..ad3830a44f Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/db-push-flow.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/entity-first-migration-flow.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/entity-first-migration-flow.png new file mode 100644 index 0000000000..74ef25a818 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/entity-first-migration-flow.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-dev-flow.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-dev-flow.png new file mode 100644 index 0000000000..84e4b3e0a7 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-dev-flow.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-diff-flow.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-diff-flow.png new file mode 100644 index 0000000000..2377b1b221 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-diff-flow.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-lifecycle.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-lifecycle.png new file mode 100644 index 0000000000..a100554284 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-lifecycle.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-state-mgt.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-state-mgt.png new file mode 100644 index 0000000000..9e9ec5bec1 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/mental-model-illustrations/prisma-migrate-state-mgt.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/migrate-mapping.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/migrate-mapping.png new file mode 100644 index 0000000000..fb388fb77f Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/migrate-mapping.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-database.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-database.png new file mode 100644 index 0000000000..fd23edc017 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-database.png differ diff --git a/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-db.png b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-db.png new file mode 100644 index 0000000000..ef3189a419 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/200-understanding-prisma-migrate/shadow-db.png differ diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/10-seeding.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/10-seeding.mdx new file mode 100644 index 0000000000..8a697e4ffd --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/10-seeding.mdx @@ -0,0 +1,401 @@ +--- +title: Seeding +metaTitle: Seeding +metaDescription: Learn how to seed your database using Prisma's integrated seeding functionality and Prisma Client +tocDepth: 3 +--- + + + +This guide describes how to seed your database using Prisma Client and Prisma's integrated seeding functionality. Seeding allows you to consistently re-create the same data in your database and can be used to: + +- Populate your database with data that is required for your application to start - for example, a default language or a default currency. +- Provide basic data for validating and using your application in a development environment. This is particularly useful if you are using Prisma Migrate, which sometimes requires resetting your development database. + + + +## How to seed your database in Prisma + +Prisma's integrated seeding functionality expects a command in the `"seed"` key in the `"prisma"` key of your `package.json` file. This can be any command, `prisma db seed` will just execute it. In this guide and as a default, we recommend writing a seed script inside your project's `prisma/` folder and starting it with the command. + + + + + +```json +"prisma": { + "seed": "ts-node prisma/seed.ts" +}, +``` + + + +With TypeScript,`ts-node` does transpiling and typechecking by default; typechecking can be disabled with the following flag `--transpile-only`. + +Example: +`"seed": "ts-node --transpile-only prisma/seed.ts"` + +This can be useful to reduce memory usage (RAM) and increase execution speed of the seed script. + + + + + + + +```json +"prisma": { + "seed": "node prisma/seed.js" +}, +``` + + + + + +## Integrated seeding with Prisma Migrate + +Database seeding happens in two ways with Prisma: manually with `prisma db seed` and automatically in `prisma migrate dev` and `prisma migrate reset`. + +With `prisma db seed`, _you_ decide when to invoke the seed command. It can be useful for a test setup or to prepare a new development environment, for example. + +Prisma Migrate also integrates seamlessly with your seeds, assuming you follow the steps in the section below. When Prisma Migrate resets the development database, seeding is triggered automatically if you have a "seed" property in the "prisma" section in your package.json. + +Prisma Migrate resets the database and triggers seeding in the following scenarios: + +- You manually run the `prisma migrate reset` CLI command. +- The database is reset interactively in the context of using `prisma migrate dev` - for example, as a result of migration history conflicts or database schema drift. +- When you want to use `prisma migrate dev` or `prisma migrate reset` without seeding, you can pass the --skip-seed flag. + +## Example seed scripts + +Here we suggest some specific seed scripts for different situations. You are free to customize these in any way, but can also use them as presented here: + +### Seeding your database with TypeScript or JavaScript + + + + + +1. Create a new file named `seed.ts`. This can be placed anywhere within your projects folder structure. The below example places it in the `/prisma` folder. +2. In the `seed.ts` file, import Prisma Client, initialize it and create some records. As an example, take the following Prisma schema with a `User` and `Post` model: + + ```prisma file=schema.prisma + model User { + id Int @id @default(autoincrement()) + email String @unique + name String + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) + title String + content String + published Boolean + user User @relation(fields: [userId], references: [id]) + userId Int + } + ``` + + Create some new users and posts in your `seed.ts` file: + + ```js file=seed.ts + import { PrismaClient } from '@prisma/client' + const prisma = new PrismaClient() + async function main() { + const alice = await prisma.user.upsert({ + where: { email: 'alice@prisma.io' }, + update: {}, + create: { + email: 'alice@prisma.io', + name: 'Alice', + posts: { + create: { + title: 'Check out Prisma with Next.js', + content: 'https://www.prisma.io/nextjs', + published: true, + }, + }, + }, + }) + const bob = await prisma.user.upsert({ + where: { email: 'bob@prisma.io' }, + update: {}, + create: { + email: 'bob@prisma.io', + name: 'Bob', + posts: { + create: [ + { + title: 'Follow Prisma on Twitter', + content: 'https://twitter.com/prisma', + published: true, + }, + { + title: 'Follow Nexus on Twitter', + content: 'https://twitter.com/nexusgql', + published: true, + }, + ], + }, + }, + }) + console.log({ alice, bob }) + } + main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) + ``` + +3. Add `typescript`, `ts-node` and `@types/node` development dependencies: + ``` + npm install -D typescript ts-node @types/node + ``` + + + +4. Add the `prisma.seed` field to your `package.json` file: + + ```json file=package.json highlight=5;normal + { + "name": "my-project", + "version": "1.0.0", + "prisma": { + "seed": "ts-node prisma/seed.ts" + }, + "devDependencies": { + "@types/node": "^14.14.21", + "ts-node": "^9.1.1", + "typescript": "^4.1.3" + } + } + ``` + + Some projects may require you to add compile options. When using Next.js for example, you would setup your seed script like so: + + ```json file=package.json + "prisma": { + "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" + }, + ``` + +5. To seed the database, run the `db seed` CLI command: + + ``` + npx prisma db seed + ``` + + + + + +1. Create a new file named `seed.js`. This can be placed anywhere within your projects folder structure. The below example places it in the `/prisma` folder. +2. In the `seed.js` file, import Prisma Client, initialize it and create some records. As an example, take the following Prisma schema with a `User` and `Post` model: + + ```prisma file=schema.prisma + generator client { + provider = "prisma-client-js" + } + + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model User { + id Int @id @default(autoincrement()) + email String @unique + name String + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) + title String + content String + published Boolean + user User @relation(fields: [userId], references: [id]) + userId Int + } + ``` + + Create some new users and posts in your `seed.js` file: + + ```js file=seed.js + const { PrismaClient } = require('@prisma/client') + const prisma = new PrismaClient() + + async function main() { + const alice = await prisma.user.upsert({ + where: { email: 'alice@prisma.io' }, + update: {}, + create: { + email: 'alice@prisma.io', + name: 'Alice', + posts: { + create: { + title: 'Check out Prisma with Next.js', + content: 'https://www.prisma.io/nextjs', + published: true, + }, + }, + }, + }) + + const bob = await prisma.user.upsert({ + where: { email: 'bob@prisma.io' }, + update: {}, + create: { + email: 'bob@prisma.io', + name: 'Bob', + posts: { + create: [ + { + title: 'Follow Prisma on Twitter', + content: 'https://twitter.com/prisma', + published: true, + }, + { + title: 'Follow Nexus on Twitter', + content: 'https://twitter.com/nexusgql', + published: true, + }, + ], + }, + }, + }) + console.log({ alice, bob }) + } + main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) + ``` + +3. Add the `prisma.seed` to your `package.json` file: + + ```json file=package.json highlight=5;normal + { + "name": "my-project", + "version": "1.0.0", + "prisma": { + "seed": "node prisma/seed.js" + } + } + ``` + +4. To seed the database, run the `db seed` CLI command: + + ``` + npx prisma db seed + ``` + + + + + +### Seeding your database via any language (with a Bash script) + +In addition to TypeScript and JavaScript, you can also use a Bash script (`seed.sh`) to seed your database in another language such as Go, or plain SQL. + + + + + +The following example runs a Go script in the same folder as `seed.sh`: + +```bash file=seed.sh +#!/bin/sh +# -e Exit immediately when a command returns a non-zero status. +# -x Print commands before they are executed +set -ex +# Seeding command +go run ./seed/ +``` + + + + + +The following example uses [psql](https://www.postgresql.org/docs/13/app-psql.html) to run a SQL script in the same folder as `seed.sh`: + +```bash file=seed.sh +#!/bin/sh +# -e Exit immediately when a command returns a non-zero status. +# -x Print commands before they are executed +set -ex +# Seeding command +psql file.sql +``` + + + + + +### User-defined arguments + +> This feature is available from version 4.15.0 and later. + +`prisma db seed` allows you to define custom arguments in your seed file that you can pass to the `prisma db seed` command. For example, you could define your own arguments to seed different data for different environments or partially seeding data in some tables. + +Here is an example seed file that defines a custom argument to seed different data in different environments: + +```js file="seed.js" +import { parseArgs } from 'node:util' + +const options = { + environment: { type: 'string' }, +} + +async function main() { + const { + values: { environment }, + } = parseArgs({ options }) + + switch (environment) { + case 'development': + /** data for your development */ + break + case 'test': + /** data for your test environment */ + break + default: + break + } +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + +You can then provide the `environment` argument when using `prisma db seed` by adding a [delimiter](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) — `--` —, followed by your custom arguments: + +``` +npx prisma db seed -- --environment development +``` + +## Going further + +Here's a non-exhaustive list of other tools you can integrate with Prisma in your development workflow to seed your database: + +- [Replibyte](https://www.replibyte.com/docs/introduction) +- [Snaplet](https://docs.snaplet.dev/recipes/prisma) diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/100-team-development.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/100-team-development.mdx new file mode 100644 index 0000000000..a1fa262121 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/100-team-development.mdx @@ -0,0 +1,200 @@ +--- +title: Team development +metaTitle: Team development +metaDescription: How to use Prisma Migrate when collaborating on a project as a team. +--- + + + +To incorporate changes from collaborators: + +1. Pull the changed Prisma schema and `./prisma/migrations` folder +1. Run the `migrate dev` command to apply new migrations: + + ```terminal + npx prisma migrate dev + ``` + +Migrations are **applied in the same order as they were created**. The creation date is part of the migration subfolder name - for example, `20210316081837-updated-fields` was created on `2021-03-16-08:08:37`. + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate dev`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +
+ +## Example: Incorporating your team's changes + +The following sample scenario demonstrates how a team of three developers share and incorporate changes to the Prisma schema and the migration history. + +The following tabs show the team's Prisma schema before and after a round of changes: + + + + + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + + + + + +```prisma file=schema.prisma highlight=14,15,19-23;add +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + favoriteColor String? // Added by Ania + bestPacmanScore Int? // Added by you + posts Post[] +} + +// Added by Javier +model Tag { + tagName String @id + tagCategory Category +} +``` + + + + +### The team's changes + +Your team members Ania and Javier make additive changes to the schema in their local environment and generate migrations. + +**Ania** makes the following changes: + +1. Adds a model field: + + ```prisma highlight=3;add + model User { + /* ... */ + favoriteColor String? + } + ``` + +1. Generates a migration: + + ```terminal + npx prisma migrate dev --name new-field + ``` + +1. Commits the changed schema and the new migration: + + - `./prisma/schema.prisma` + - `./prisma/migrations/20210316081837-new-field/migration.sql` + +**Javier** makes the following changes: + +1. Adds a new model to the schema: + + ```prisma highlight=1-4;add + model Tag { + tagName String @id + tagCategory Category + } + ``` + +1. Generates a migration: + + ```terminal + npx prisma migrate dev --name new-model + ``` + +1. Commits the changed schema and the new migration: + + - `./prisma/schema.prisma` + - `./prisma/migrations/20210316091837-new-model/migration.sql` + +The migration history now has **two** new migrations: + +![A diagram showing changes by two separate developers converging in a single migration history.](migrate-team-dev.png) + +### Integrating changes + +**You** want to incorporate your team's changes. To do that, you: + +1. Pull the most recent changes from your team, including: + + - Two new migrations: + + - `./prisma/migrations/20210316081837-new-field/migration.sql` + - `./prisma/migrations/20210316091837-new-model/migration.sql` + + - An updated schema file. Git automatically merges the updated schema with _your_ local schema changes (a new `bestPacmanScore` field): + + ```prisma highlight=3,7-11;add + model User { + /* ... */ + favoriteColor String? + bestPacmanScore Int? + } + + model Tag { + tagName String @id + tagCategory Category + posts Post[] + } + ``` + +1. Run the `migrate dev` command: + + ```terminal + npx prisma migrate dev + ``` + + 1. Applies Ania and Javier's migrations to your local database. + + - `./prisma/migrations/20210316081837-new-field/migration.sql` + - `./prisma/migrations/20210316091837-new-model/migration.sql` + + 1. Creates a new migration with your changes, prompts you to name it (`pacman-field`), and applies the new migration to your local database: + + - `./prisma/migrations/20210322081837-pacman-field/migration.sql` + +1. Commit the merged `schema.prisma` and your new migration: `./prisma/migrations/20210322081837-pacman-field/migration.sql` + +Your `schema.prisma` and local database now include your team's changes, and the migration history includes your migration: + +![A migration history with 5 migrations.](migration-history.png) + +## Source control + +You should commit the following files to source control: + +- The contents of the `.prisma/migrations` folder, including the `migration_lock.toml` file +- The Prisma schema file (`schema.prisma`) + +Source-controlling the `schema.prisma` file is not enough - you must include your migration history. This is because: + +- As you start to [customize migrations](customizing-migrations), your migration history contains **information that cannot be represented in the Prisma schema**. For example, you can customize a migration to mitigate data loss that would be caused by a breaking change. +- The `prisma migrate deploy` command, which is used to deploy changes to staging, testing, and production environments, _only_ runs migration files. Prisma Migrate only uses the schema file to read the `url` and `provider` fields, not models and fields. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/110-native-database-types.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/110-native-database-types.mdx new file mode 100644 index 0000000000..86270c085d --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/110-native-database-types.mdx @@ -0,0 +1,104 @@ +--- +title: Native database types +metaTitle: Native database types +metaDescription: Native database types +--- + + + +Prisma Migrate translates the model defined in your [Prisma schema](/orm/prisma-schema) into features in your database. + +![A diagram that shows a Prisma schema on the left (labeled: Prisma schema, models) and a database on the right (labeled: Database, tables). Two parallel arrows connect the schema and the database, showing how '@unique' maps to 'UNIQUE' and '@id' maps to 'PRIMARY KEY'.](../200-understanding-prisma-migrate/migrate-mapping.png) + +Every¹ feature in your [data model](/orm/prisma-schema/data-model/models) maps to a corresponding feature in the underlying database. **If you can define a feature in the Prisma schema, it is supported by Prisma Migrate.** + +For a complete list of Prisma schema features, refer to: + +- [Database features matrix](/orm/reference/database-features) for a list of database features and what they map to in the Prisma schema. +- [Prisma schema reference](/orm/reference/prisma-schema-reference) for a list of all Prisma schema features, including field types, attributes, and functions. + +Prisma Migrate also supports mapping each field to a [specific native type](#mapping-fields-to-a-specific-native-type), and there are ways to [include features without a Prisma schema equivalent in your database](#handling-unsupported-database-features). + +:::note + +Comments and Prisma-level functions (`uuid()` and `cuid()`) do not map to database features. + +::: + + + +## Mapping fields to a specific native type + +Each Prisma type maps to a default underlying database type - for example, the PostgreSQL connector maps `String` to `text` by default. [Native database type attributes](/orm/prisma-schema/data-model/models#native-types-mapping) determines which _specific_ native type should be created in the database. + + + +**Note**: Some Prisma types only map to a single native type. + + + +In the following example, the `name` and `title` fields have a `@db.VarChar(X)` type attribute: + +```prisma highlight=8,14;normal +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + name String @db.VarChar(200) + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String @db.VarChar(150) + published Boolean @default(true) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +``` + +Prisma Migrate uses the specified types when it creates a migration: + +```sql highlight=4,10;normal + -- CreateTable +CREATE TABLE "User" ( + "id" SERIAL, + "name" VARCHAR(200) NOT NULL, + PRIMARY KEY ("id") +); + -- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL, + "title" VARCHAR(150) NOT NULL, + "published" BOOLEAN NOT NULL DEFAULT true, + "authorId" INTEGER NOT NULL, + PRIMARY KEY ("id") +); + + -- AddForeignKey +ALTER TABLE "Post" ADD FOREIGN KEY("authorId")REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; +``` + +### Mappings by Prisma type + +For type mappings organized by Prisma type, refer to the [Prisma schema reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) documentation. + +### Mappings by database provider + +For type mappings organized by database provider, see: + +- [PostgreSQL mappings](/orm/overview/databases/postgresql#type-mapping-between-postgresql-and-prisma-schema) +- [MySQL mappings](/orm/overview/databases/mysql#native-type-mappings) +- [Microsoft SQL Server mappings](/orm/overview/databases/sql-server#type-mapping-between-microsoft-sql-server-to-prisma-schema) +- [SQLite mappings](/orm/overview/databases/sqlite#type-mapping-between-sqlite-to-prisma-schema) + +## Handling unsupported database features + +Prisma Migrate cannot automatically create database features that have no equivalent in Prisma Schema Language (PSL). For example, there is currently no way to define a stored procedure or a partial index in PSL. However, there are ways to add unsupported features to your database with Prisma Migrate: + +- [Handle unsupported field types](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-field-types) (like `circle`) +- [Handle unsupported features](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-database-features), like stored procedures +- [How to use native database functions](/orm/prisma-schema/data-model/unsupported-database-features#native-database-functions) diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/120-native-database-functions.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/120-native-database-functions.mdx new file mode 100644 index 0000000000..b310d48ae2 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/120-native-database-functions.mdx @@ -0,0 +1,81 @@ +--- +title: Native database functions +metaTitle: Native database functions +metaDescription: How to enable PostgreSQL native database functions for projects that use Prisma Migrate. +--- + + + +In PostgreSQL, some [native database functions](/orm/prisma-schema/data-model/unsupported-database-features#native-database-functions) are part of optional extensions. For example, in PostgreSQL versions 12.13 and earlier the `gen_random_uuid()` function is part of the [`pgcrypto`](https://www.postgresql.org/docs/10/pgcrypto.html) extension. + +To use a PostgreSQL extension, you must install it on the file system of your database server and then activate the extension. If you use Prisma Migrate, this must be done as part of a migration. + + + +Do not activate extensions outside a migration file if you use Prisma Migrate. The [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database) requires the same extensions. Prisma Migrate creates and deletes the shadow database automatically, so the only way to activate an extension is to include it in a migration file. + + + +In Prisma versions 4.5.0 and later, you can activate the extension by declaring it in your Prisma schema with the [`postgresqlExtensions` preview feature](/orm/prisma-schema/postgresql-extensions): + +```prisma file=schema.prisma highlight=3,9;add +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [pgcrypto] +} +``` + +You can then apply these changes to your database with Prisma Migrate. See [How to migrate PostgreSQL extensions](/orm/prisma-schema/postgresql-extensions#how-to-migrate-postgresql-extensions) for details. + +In earlier versions of Prisma, you must instead add a SQL command to your migration file to activate the extension. See [How to install a PostgreSQL extension as part of a migration](#how-to-install-a-postgresql-extension-as-part-of-a-migration). + + + +## How to install a PostgreSQL extension as part of a migration + +This section describes how to add a SQL command to a migration file to activate a PostgreSQL extension. If you manage PostgreSQL extensions in your Prisma schema file with the `postgresqlExtensions` preview feature instead, see [How to migrate PostgreSQL extensions](/orm/prisma-schema/postgresql-extensions#how-to-migrate-postgresql-extensions). + +The following example demonstrates how to install the `pgcrypto` extension as part of a migration: + +1. Add the field with the native database function to your schema: + + ```prisma + model User { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + } + ``` + + If you include a cast operator (such as `::TEXT`), you must surround the entire function with parentheses: + + ```prisma + @default(dbgenerated("(gen_random_uuid()::TEXT)")) + ``` + +1. Use the `--create-only` flag to generate a new migration without applying it: + + ```terminal + npx prisma migrate dev --create-only + ``` + +1. Open the generated `migration.sql` file and enable the `pgcrypto` module: + + ```sql + CREATE EXTENSION IF NOT EXISTS pgcrypto; + + ADD COLUMN "id" UUID NOT NULL DEFAULT gen_random_uuid(), + ADD PRIMARY KEY ("id"); + ``` + +1. Apply the migration: + + ```terminal + npx prisma migrate dev + ``` + +Each time you reset the database or add a new member to your team, all required functions are part of the migration history. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/20-prototyping-your-schema.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/20-prototyping-your-schema.mdx new file mode 100644 index 0000000000..24a5adf939 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/20-prototyping-your-schema.mdx @@ -0,0 +1,288 @@ +--- +title: 'Prototyping your schema' +metaTitle: 'Prototyping your schema' +metaDescription: 'Prototyping your schema' +codeStyle: false +--- + + + +The Prisma CLI has a dedicated command for prototyping schemas: [`db push`](/orm/reference/prisma-cli-reference#db-push) + +`db push` uses the same engine as Prisma Migrate to synchronize your Prisma schema with your database schema. The `db push` command: + +1. Introspects the database to infer and executes the changes required to make your database schema reflect the state of your Prisma schema. +2. By default, after changes have been applied to the database schema, generators are triggered (for example, Prisma Client). You do not need to manually invoke `prisma generate`. +3. If `db push` anticipates that the changes could result in data loss, it will: + + - Throw an error + - Require the `--accept-data-loss` option if you still want to make the changes + +> **Notes**: +> +> - `db push` does not interact with or rely on migrations. The migrations table `_prisma_migrations` will not be created or updated, and no migration files will be generated. +> - When working with PlanetScale, we recommend that you use `db push` instead of `migrate`. For details refer to our Getting Started documentation, either [Start from scratch](/getting-started/setup-prisma/start-from-scratch/relational-databases-typescript-planetscale) or [Add to existing project](/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-planetscale) depending on your situation. + + + +## Choosing `db push` or Prisma Migrate + +`db push` works well if: + +- You want to **quickly prototype and iterate** on schema design locally without the need to deploy these changes to other environments such as other developers, or staging and production environments. +- You are prioritizing reaching a **desired end-state** and not the changes or steps executed to reach that end-state (there is no way to preview changes made by `db push`) +- You do not need to control how schema changes impact data. There is no way to orchestrate schema and data migrations—if `db push` anticipates that changes will result in data loss, you can either accept data loss with the `--accept-data-loss` option or stop the process. There is no way to customize the changes. + +See [Schema prototyping with `db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) for an example of how to use `db push` in this way. + +`db push` is **not recommended** if: + +- You want to replicate your schema changes in other environments without losing data. You can use `db push` for prototyping, but you should use migrations to commit the schema changes and apply these in your other environments. +- You want fine-grained control over how the schema changes are executed - for example, [renaming a column instead of dropping it and creating a new one](/orm/prisma-migrate/workflows/customizing-migrations#example-rename-a-field). +- You want to keep track of changes made to the database schema over time. `db push` does not create any artifacts that allow you to keep track of these changes. +- You want the schema changes to be reversible. You can use `db push` again to revert to the original state, but this might result in data loss. + +## Can I use Prisma Migrate and `db push` together? + +Yes, you can [use `db push` and Prisma Migrate together in your development workflow](/orm/prisma-migrate/workflows/prototyping-your-schema) . For example, you can: + +- Use `db push` to prototype a schema at the start of a project and initialize a migration history when you are happy with the first draft +- Use `db push` to prototype a change to an existing schema, then run `prisma migrate dev` to generate a migration from your changes (you will be asked to reset) + +## Prototyping a new schema + +The following scenario demonstrates how to use `db push` to synchronize a new schema with an empty database, and evolve that schema - including what happens when `db push` detects that a change will result in data loss. + +1. Create a first draft of your schema: + + ```prisma + generator client { + provider = "prisma-client-js" + } + + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model User { + id Int @id @default(autoincrement()) + name String + jobTitle String + posts Post[] + profile Profile? + } + + model Profile { + id Int @id @default(autoincrement()) + biograpy String // Intentional typo! + userId Int @unique + user User @relation(fields: [userId], references: [id]) + } + + model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + content String @db.VarChar(500) + authorId Int + author User @relation(fields: [authorId], references: [id]) + categories Category[] + } + + model Category { + id Int @id @default(autoincrement()) + name String @db.VarChar(50) + posts Post[] + + @@unique([name]) + } + ``` + +2. Use `db push` to push the initial schema to the database: + + ```terminal + npx prisma db push + ``` + +3. Create some example content: + + ```ts + const add = await prisma.user.create({ + data: { + name: 'Eloise', + jobTitle: 'Programmer', + posts: { + create: { + title: 'How to create a MySQL database', + content: 'Some content', + }, + }, + }, + }) + ``` + +4. Make an additive change - for example, create a new required field: + + ```prisma highlight=6;add + // ... // + + model Post { + id Int @id @default(autoincrement()) + title String + description String + published Boolean @default(true) + content String @db.VarChar(500) + authorId Int + author User @relation(fields: [authorId], references: [id]) + categories Category[] + } + + // ... // + ``` + +5. Push the changes: + + ```terminal + npx prisma db push + ``` + + `db push` will prompt you to reset because you cannot add a required field to a table with existing content unless you provide a default value: + + ```bash + ⚠️ We found changes that cannot be executed: + + • Added the required column `description` to the `Post` table without a default value. There are 2 rows in this table, it is not possible to execute this. + + ? To apply this step we need to reset the database, do you want to continue? All data will be lost. » (y/N) + ``` + +:::tip + +Use the `--accept-data-loss` flag to skip this warning, or `--force-reset` to ignore all warnings. + +::: + +6. Confirm data loss and apply changes to your database (or revisit your schema): + + ```bash + There might be data loss when applying the changes: + + • Added the required column `description` to the `Post` table without a default value. + + ? Do you want to ignore the warning(s)? Some data will be lost. » (y/N) + ``` + + > **Note**: Unlike Prisma Migrate, `db push` does not generate migrations that you can modify to preserve data, and is therefore best suited for prototyping in a development environment. + +7. Continue to evolve your schema until it reaches a relatively stable state. + +8. Initialize a migration history: + + ```terminal + npx prisma migrate dev --name initial-state + ``` + + The steps taken to reach the initial prototype are not preserved - `db push` does not generate a history. + +9. Push your migration history and Prisma schema to source control (e.g. Git). + +At this point, the final draft of your prototyping is preserved in a migration and can be pushed to other environments (testing, production, or other members of your team). + +## Prototyping with an existing migration history + +The following scenario demonstrates how to use `db push` to prototype a change to a Prisma schema where a migration history already exists. + +1. Check out the latest Prisma schema and migration history: + + ```prisma + generator client { + provider = "prisma-client-js" + } + + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model User { + id Int @id @default(autoincrement()) + name String + jobTitle String + posts Post[] + profile Profile? + } + + model Profile { + id Int @id @default(autoincrement()) + biograpy String // Intentional typo! + userId Int @unique + user User @relation(fields: [userId], references: [id]) + } + + model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + content String @db.VarChar(500) + authorId Int + author User @relation(fields: [authorId], references: [id]) + categories Category[] + } + + model Category { + id Int @id @default(autoincrement()) + name String @db.VarChar(50) + posts Post[] + + @@unique([name]) + } + ``` + +2. Prototype your new feature, which can involve any number of steps. For example, you might: + + - Create a `tags String[]` field, then run `db push` + - Change the field type to `tags Tag[]` and add a new model named `Tag`, then run `db push` + - Change your mind and restore the original `tags String[]` field, then call `db push` + - Make a manual change to the `tags` field in the database - for example, adding a constraint + + After experimenting with several solutions, the final schema change looks like this: + + ```prisma + model Post { + id Int @id @default(autoincrement()) + title String + description String + published Boolean @default(true) + content String @db.VarChar(500) + authorId Int + author User @relation(fields: [authorId], references: [id]) + categories Category[] + tags String[] + } + ``` + +3. To create a migration that adds the new `tags` field, run the `migrate dev` command: + + ```terminal + npx prisma migrate dev --name added-tags + ``` + + Prisma Migrate will prompt you to reset because the changes you made manually and with `db push` while prototyping are not part of the migration history: + + ```bash + √ Drift detected: Your database schema is not in sync with your migration history. + + We need to reset the PostgreSQL database "prototyping" at "localhost:5432". + Do you want to continue? All data will be lost. ... yes + ``` + +4. Prisma Migrate replays the existing migration history, generates a new migration based on your schema changes, and applies those changes to the database. + +:::tip + +When using `migrate dev`, if your schema changes mean that seed scripts will no longer work, you can use the `--skip-seed` flag to ignore seed scripts. + +::: + +At this point, the final result of your prototyping is preserved in a migration, and can be pushed to other environments (testing, production, or other members of your team). diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/200-troubleshooting.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/200-troubleshooting.mdx new file mode 100644 index 0000000000..6d54b61c3e --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/200-troubleshooting.mdx @@ -0,0 +1,138 @@ +--- +title: Troubleshooting +metaTitle: Troubleshooting +metaDescription: Troubleshooting issues with Prisma Migrate in a development environment. +--- + + + +This guide describes how to resolve issues with Prisma Migrate in a development environment, which often involves resetting your database. For production-focused troubleshooting, see: + +- [Production troubleshooting](/orm/prisma-migrate/workflows/patching-and-hotfixing) +- [Patching / hotfixing production databases](/orm/prisma-migrate/workflows/patching-and-hotfixing) + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate dev`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +
+ +## Handling migration history conflicts + +A migration history conflict occurs when there are discrepancies between the **migrations folder in the file system** and the **`_prisma_migrations` table in the database**. + +#### Causes of migration history conflict in a development environment + +- A migration that has already been applied is later modified +- A migration that has already been applied is missing from the file system + +In a development environment, switching between feature branches can result in a history conflict because the `_prisma_migrations` table contains migrations from `branch-1`, and switching to `branch-2` might cause some of those migrations to disappear. + +> **Note**: You should [never purposefully delete or edit a migration](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories#do-not-edit-or-delete-migrations-that-have-been-applied), as this might result in discrepancies between development and production. + +#### Fixing a migration history conflict in a development environment + +If Prisma Migrate detects a migration history conflict when you run `prisma migrate dev`, the CLI will ask to reset the database and reapply the migration history. + +## Schema drift + +Database schema drift occurs when your database schema is out of sync with your migration history - the database schema has 'drifted away' from the source of truth. + +#### Causes of schema drift in a development environment + +Schema drift can occur if: + +- The database schema was changed _without_ using migrations - for example, by using [`prisma db push`](/orm/reference/prisma-cli-reference#db-push) or manually changing the database schema. + +> **Note**: The [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database) is required to detect schema drift, and can therefore only be done in a development environment. + +#### Fixing schema drift in a development environment + +If you made manual changes to the database that you do not want to keep, or can easily replicate in the Prisma schema: + +1. Reset your database: + + ```terminal + npx prisma migrate reset + ``` + +1. Replicate the changes in the Prisma schema and generate a new migration: + + ```terminal + npx prisma migrate dev + ``` + +If you made manual changes to the database that you want to keep, you can: + +1. Introspect the database: + + ```terminal + npx prisma db pull + ``` + + Prisma will update your schema with the changes made directly in the database. + +1. Generate a new migration to include the introspected changes in your migration history: + + ```terminal + npx prisma migrate dev --name introspected_change + ``` + + Prisma Migrate will prompt you to reset, then applies all existing migrations and a new migration based on the introspected changes. Your database and migration history are now in sync, including your manual changes. + +## Failed migrations + +#### Causes of failed migrations in a development environment + +A migration might fail if: + +- You [modify a migration before running it](customizing-migrations) and introduce a syntax error +- You add a mandatory (`NOT NULL`) column to a table that already has data +- The migration process stopped unexpectedly +- The database shut down in the middle of the migration process + +Each migration in the `_prisma_migrations` table has a `logs` column that stores the error. + +#### Fixing failed migrations in a development environment + +The easiest way to handle a failed migration in a developer environment is to address the root cause and reset the database. For example: + +- If you introduced a SQL syntax error by manually editing the database, update the `migration.sql` file that failed and reset the database: + + ```terminal + prisma migrate reset + ``` + +- If you introduced a change in the Prisma schema that cannot be applied to a database with data (for example, a mandatory column in a table with data): + + 1. Delete the `migration.sql` file. + + 2. Modify the schema - for example, add a default value to the mandatory field. + + 3. Migrate: + + ```terminal + prisma migrate dev + ``` + + Prisma Migrate will prompt you to reset the database and re-apply all migrations. + +- If something interrupted the migration process, reset the database: + + ```terminal + prisma migrate reset + ``` + +## Prisma Migrate and PgBouncer + +You might see the following error if you attempt to run Prisma Migrate commands in an environment that uses PgBouncer for connection pooling: + +```bash +Error: undefined: Database error +Error querying the database: db error: ERROR: prepared statement "s0" already exists +``` + +See [Prisma Migrate and PgBouncer workaround](/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer) for further information and a workaround. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/30-baselining.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/30-baselining.mdx new file mode 100644 index 0000000000..aa0c72ca6a --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/30-baselining.mdx @@ -0,0 +1,92 @@ +--- +title: Baselining a database +metaDescription: How to initialize a migration history for an existing database that contains important data. +--- + + + +Baselining is the process of initializing a migration history for a database that: + +- ✔ Existed before you started using Prisma Migrate +- ✔ Contains data that must be maintained (like production), which means that the database cannot be reset + +Baselining tells Prisma Migrate to assume that one or more migrations have **already been applied**. This prevents generated migrations from failing when they try to create tables and fields that already exist. + +> **Note**: We assume it is acceptable to reset and seed development databases. + +Baselining is part of [adding Prisma Migrate to a project with an existing database](/orm/prisma-migrate/getting-started#adding-prisma-migrate-to-an-existing-project). + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate deploy`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +
+ +## Why you need to baseline + +When you add Prisma Migrate to an existing project, your initial migration contains all the SQL required to recreate the state of the database **before you started using Prisma Migrate**: + +![The image shows a database labelled 'Existing database', and a list of existing database features next to it - 24 tables, 13 relationships, 92 fields, 3 indexes. An arrow labelled 'represented by' connects the database features list to a box that represents a migration. The existing databases's features are represented by a single migration.](existing-database.png) + + + +:::tip + +You can edit the initial migration to include schema elements that cannot be represented in the Prisma schema - such as stored procedures or triggers. + +::: + + + +You need this initial migration to create and reset **development environments**: + +![The image shows a migration history with three migrations. Each migration is represented by a file icon and a name, and all migrations are surrounded by a box labelled 'migration history'. The first migration has an additional label: "State of database before Prisma Migrate", and the two remaining migrations are labelled "Generated as part of the Prisma Migrate workflow". An arrow labelled "prisma migrate dev" connects the migration history box to a database labelled "new development database", signifying that all three migrations are applied to the development database - none are skipped.](new-dev-db.png) + +However, when you `prisma migrate deploy` your migrations to databases that already exist and _cannot_ be reset - such as production - you **do not want to include the initial migrations**. + +The target database already contains the tables and columns created by the initial migration, and attempting to create these elements again will most likely result in an error. + +![A migration history represented by three migration files (file icon and name), surrounded by a a box labelled 'migration history'. The first migration is marked 'do not apply', and the second two migrations are marked 'apply'. An arrow labelled with the command 'prisma migrate deploy' points from the migration history to a database labelled 'production'.](deploy-db.png) + +Baselining solves this problem by telling Prisma Migrate to pretend that the initial migration(s) **have already been applied**. + +## Baselining a database + +To create a baseline migration: + +1. If you have a `prisma/migrations` folder, delete, move, rename, or archive this folder. + +1. Run the following command to create a `migrations` directory inside with your preferred name. This example will use `0_init` for the migration name: + + ```terminal + mkdir -p prisma/migrations/0_init + ``` + + + + Then `0_` is important because Prisma Migrate applies migrations in a [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order). You can use a different value such as the current timestamp. + + + +1. Generate a migration and save it to a file using `prisma migrate diff` + + ```terminal no-lines + npx prisma migrate diff \ + --from-empty \ + --to-schema-datamodel prisma/schema.prisma \ + --script > prisma/migrations/0_init/migration.sql + ``` + +1. Run the `prisma migrate resolve` command for each migration that should be ignored: + + ```terminal wrap + npx prisma migrate resolve --applied 0_init + ``` + +This command adds the target migration to the `_prisma_migrations` table and marks it as applied. When you run `prisma migrate deploy` to apply new migrations, Prisma Migrate: + +1. Skips all migrations marked as 'applied', including the baseline migration +1. Applies any new migrations that come _after_ the baseline migration diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/40-customizing-migrations.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/40-customizing-migrations.mdx new file mode 100644 index 0000000000..2e25b95532 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/40-customizing-migrations.mdx @@ -0,0 +1,282 @@ +--- +title: Customizing migrations +metaTitle: Customizing migrations +metaDescription: How to edit a migration file before applying it to avoid data loss in production. +tocDepth: 3 +--- + + + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate dev`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +In some scenarios, you need to edit a migration file before you apply it. For example, to [change the direction of a 1-1 relation](#example-change-the-direction-of-a-1-1-relation) (moving the foreign key from one side to another) without data loss, you need to move data as part of the migration - this SQL is not part of the default migration, and must be written by hand. + +This guide explains how to edit migration files and gives some examples of use cases where you may want to do this. + +
+ +## How to edit a migration file + +To edit a migration file before applying it, the general procedure is the following: + +1. Make a schema change that requires custom SQL (for example, to preserve existing data) +1. Create a draft migration using: + + ```terminal + npx prisma migrate dev --create-only + ``` + +1. Modify the generated SQL file. +1. Apply the modified SQL by running: + + ```terminal + npx prisma migrate dev + ``` + +### Example: Rename a field + +By default, renaming a field in the schema results in a migration that will: + +- `CREATE` a new column (for example, `fullname`) +- `DROP` the existing column (for example, `name`) and the data in that column + +To actually **rename** a field and avoid data loss when you run the migration in production, you need to modify the generated migration SQL before applying it to the database. Consider the following schema fragment - the `biograpy` field is spelled wrong. + +```prisma highlight=3;normal; +model Profile { + id Int @id @default(autoincrement()) + biograpy String + userId Int @unique + user User @relation(fields: [userId], references: [id]) +} +``` + +To rename the `biograpy` field to `biography`: + +1. Rename the field in the schema: + + ```prisma highlight=3;delete|4;add; + model Profile { + id Int @id @default(autoincrement()) + biograpy String + biography String + userId Int @unique + user User @relation(fields: [userId], references: [id]) + } + ``` + +1. Run the following command to create a **draft migration** that you can edit before applying to the database: + + ```terminal + npx prisma migrate dev --name rename-migration --create-only + ``` + +1. Edit the draft migration as shown, changing `DROP` / `DELETE` to a single `RENAME COLUMN`: + + + + + + ```sql file=./prisma/migrations/20210308092620_rename_migration/migration.sql + ALTER TABLE "Profile" DROP COLUMN "biograpy", + ADD COLUMN "biography" TEXT NOT NULL; + ``` + + + + + + ```sql file=./prisma/migrations/20210308092620_rename_migration/migration.sql + ALTER TABLE "Profile" + RENAME COLUMN "biograpy" TO "biography"; + ``` + + + + + +1. Save and apply the migration: + + ```terminal + npx prisma migrate dev + ``` + +You can use the same technique to rename a `model` - edit the generated SQL to _rename_ the table rather than drop and re-create it. + +### Example: Use the expand and contract pattern to evolve the schema without downtime + +Making schema changes to existing fields, e.g., renaming a field can lead to downtime. It happens in the time frame between applying a migration that modifies an existing field, and deploying a new version of the application code which uses the modified field. + +You can prevent downtime by breaking down the steps required to alter a field into a series of discrete steps designed to introduce the change gradually. This pattern is known as the _expand and contract pattern_. + +The pattern involves two components: your application code accessing the database and the database schema you intend to alter. + +With the _expand and contract_ pattern, renaming the field `bio` to `biography` would look as follows with Prisma: + +1. Add the new `biography` field to your Prisma schema and create a migration + + ```prisma highlight=4;add; + model Profile { + id Int @id @default(autoincrement()) + bio String + biography String + userId Int @unique + user User @relation(fields: [userId], references: [id]) + } + ``` + +2. _Expand_: update the application code and write to both the `bio` and `biography` fields, but continue reading from the `bio` field, and deploy the code +3. Create an empty migration and copy existing data from the `bio` to the `biography` field + + ```terminal + npx prisma migrate dev --name copy_biography --create-only + ``` + + ```sql file=prisma/migrations/20210420000000_copy_biography/migration.sql + UPDATE "Profile" SET biography = bio; + ``` + +4. Verify the integrity of the `biography` field in the database +5. Update application code to **read** from the new `biography` field +6. Update application code to **stop writing** to the `bio` field +7. _Contract_: remove the `bio` from the Prisma schema, and create a migration to remove the `bio` field + + ```prisma highlight=3;delete; + model Profile { + id Int @id @default(autoincrement()) + bio String + biography String + userId Int @unique + user User @relation(fields: [userId], references: [id]) + } + ``` + + ```terminal + npx prisma migrate dev --name remove_bio + ``` + +By using this approach, you avoid potential downtime that altering existing fields that are used in the application code are prone to, and reduce the amount of coordination required between applying the migration and deploying the updated application code. + +Note that this pattern is applicable in any situation involving a change to a column that has data and is in use by the application code. Examples include combining two fields into one, or transforming a `1:n` relation to a `m:n` relation. + +To learn more, check out the Data Guide article on [the expand and contract pattern](https://www.prisma.io/dataguide/types/relational/expand-and-contract-pattern) + +### Example: Change the direction of a 1-1 relation + +To change the direction of a 1-1 relation: + +1. Make the change in the schema: + + ```prisma + model User { + id Int @id @default(autoincrement()) + name String + posts Post[] + profile Profile? @relation(fields: [profileId], references: [id]) + profileId Int @unique + } + + model Profile { + id Int @id @default(autoincrement()) + biography String + user User + } + ``` + +1. Run the following command to create a **draft migration** that you can edit before applying to the database: + + + + + + ```terminal + npx prisma migrate dev --name rename-migration --create-only + ``` + + + + + + ```code no-copy + ⚠️ There will be data loss when applying the migration: + + • The migration will add a unique constraint covering the columns `[profileId]` on the table `User`. If there are existing duplicate values, the migration will fail. + ``` + + + + + +1. Edit the draft migration as shown: + + + + + + ```sql + + -- DropForeignKey + ALTER TABLE "Profile" DROP CONSTRAINT "Profile_userId_fkey"; + + -- DropIndex + DROP INDEX "Profile_userId_unique"; + + -- AlterTable + ALTER TABLE "Profile" DROP COLUMN "userId"; + + -- AlterTable + ALTER TABLE "User" ADD COLUMN "profileId" INTEGER NOT NULL; + + -- CreateIndex + CREATE UNIQUE INDEX "User_profileId_unique" ON "User"("profileId"); + + -- AddForeignKey + ALTER TABLE "User" ADD FOREIGN KEY ("profileId") REFERENCES "Profile"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ``` + + + + + + ```sql + + -- DropForeignKey + ALTER TABLE "Profile" DROP CONSTRAINT "Profile_userId_fkey"; + + -- DropIndex + DROP INDEX "Profile_userId_unique"; + + -- AlterTable + ALTER TABLE "User" ADD COLUMN "profileId" INTEGER; + + UPDATE "User" + SET "profileId" = "Profile".id + FROM "Profile" + WHERE "User".id = "Profile"."userId"; + + ALTER TABLE "User" ALTER COLUMN "profileId" SET NOT NULL; + + -- AlterTable + ALTER TABLE "Profile" DROP COLUMN "userId"; + + -- CreateIndex + CREATE UNIQUE INDEX "User_profileId_unique" ON "User"("profileId"); + + -- AddForeignKey + ALTER TABLE "User" ADD FOREIGN KEY ("profileId") REFERENCES "Profile"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ``` + + + + + +1. Save and apply the migration: + + ```terminal + npx prisma migrate dev + ``` diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/45-data-migration.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/45-data-migration.mdx new file mode 100644 index 0000000000..5640babf88 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/45-data-migration.mdx @@ -0,0 +1,258 @@ +--- +title: Data migrations +metaDescription: How to migrate data using Prisma using the expand and contract pattern. +tocDepth: 3 +--- + + + +Prisma does not yet natively support data migrations, but you can use the [expand and contract pattern](https://www.prisma.io/dataguide/types/relational/expand-and-contract-pattern) to migrate your data. For example from one column into another. + +This guide covers how you can use Prisma with the expand and contract pattern to: + +- Expand your schema with a new column +- Create and run the data migration +- Contract your schema by dropping the old column + + + +## Overview of the steps + +This tutorial will walk you through the following steps: + +1. Expand your schema with a new column +1. Create and run the data migration file +1. Contract your schema by dropping the old column + +It also makes the following assumptions: + +- The production database is accessible from the development machine +- `prisma migrate dev` is only run against development database +- The expanding and contracting steps are handled in separate branches + +For this guide, you will modify the following schema by replacing the `published` boolean field with a `status` enum: + +```prisma file=prisma/schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) +} +``` + +## Expand your schema with a new column + +Checkout to a new branch from your `main` branch: + +```terminal +git checkout -b create-status-field +``` + +Make the following updates to your Prisma schema: + +- Create a `Status` enum with the following values: `Unknown`, `Draft`, `InReview`, and `Published` +- Add a `status` column to the `Post` model +- Mark the `published` field as optional + +```prisma file=prisma/schema.prisma highlight=5,6,9-15;edit +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + status Status +} + +enum Status { + Unknown + Draft + InProgress + InReview + Published +} +``` + +Create a new migration to sync the Prisma schema with the database schema: + +```terminal +npx prisma migrate dev --name add-status-column +``` + +Prisma Migrate will give you the following warning because the field being added to the database is non-nullable, and the database contains existing data which require a default value. + + + + + +```no-copy +Prisma schema loaded from prisma/schema.prisma +Datasource "db": PostgreSQL database "data-migration", schema "public" at "localhost:5401" + +Error: +⚠️ We found changes that cannot be executed: + + • Step 1 Added the required column `status` to the `Post` table without a default value. There are 4 rows in this table, it is not possible to execute this step. + +You can use prisma migrate dev --create-only to create the migration file, and manually modify it to address the underlying issue(s). +Then run prisma migrate dev to apply it and verify it works. +``` + + + + +Exit from the migration step and update the schema by adding a default value for the `status` field by adding the `@default()` attribute function. + +```prisma file=prisma/schema.prisma highlight=6;edit +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + status Status @default(Unknown) +} + +enum Status { + Unknown + Draft + InProgress + InReview + Published +} +``` + +Generate and execute the migration using the following command: + +```terminal +npx prisma migrate dev --name add-default +``` + +## Create and run the data migration file + +### Create a data migration file + +Inside the generated migration folder from the previous step, create a file called `data-migration.ts` file. This file will contain a data migration which will be implemented using Prisma Client. + +Add the following code to migrate the data from the `published` field to the `status` field in the file you just created: + +Update your `package.json` file to include the data migration file: + +```ts file=prisma/migrations/20230417131956_add-status-column/data-migration.ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + await prisma.$transaction(async (tx) => { + const posts = await tx.post.findMany() + for (const post of posts) { + await tx.post.update({ + where: { id: post.id }, + data: { + status: post.published ? 'Published' : 'Unknown', + }, + }) + } + }) +} + +main() + .catch(async (e) => { + console.error(e) + process.exit(1) + }) + .finally(async () => await prisma.$disconnect()) +``` + +The data migration is wrapped in a transaction to ensure that the query is rolled back, allowing you to iterate on your data migration file + +Next steps: + +1. Push your changes to a remote origin and create a new pull request. +1. Once you’re happy with the changes, merge the changes to your `main` branch. + +To apply the changes to your production database, add `prisma migrate deploy` as part of your deployment/ build step in CI + +### Run the data migration + +Update the `package.json` file with the script to execute the data-migration file. Be sure to update the `20230417131956_add-status-column` with the name of your migration file. + +```json file=package.json +"scripts": { + "dev": "ts-node ./script.ts", + "data-migration:add-status-column": "ts-node ./prisma/migrations/20230417131956_add-status-column/data-migration.ts" + }, +``` + +Next steps: + +1. Push your changes to a remote origin and create a new pull request. +1. Once you’re happy with the changes, merge the changes to your “main” branch. + +To apply the changes to your production database, add `prisma migrate deploy` as part of your deployment/ build step in CI. + +### Run the data migration + +Update the `DATABASE_URL` environment variable with your production database's URL. Run the data migration script: + +```terminal +npm run data-migration:add-status-column +``` + +## Contract your schema by dropping the old column + +Checkout to a separate branch on your development machine: + +```terminal +git checkout -b drop-published-column +``` + +Delete the `published` field from your schema and generate a new migration: + +```prisma highlight=5;delete +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + status Status @default(Unknown) +} + +enum Status { + Draft + InProgress + InReview + Published +} +``` + +Generate a new migration: + +```terminal +npx prisma migrate dev --name drop-published-column +``` + +Next steps: + +1. Push your changes to a remote origin and create a new pull request. +1. Once you’re happy with the changes, merge the changes to your `main` branch. + +To apply the changes to your production database, add `prisma migrate deploy` as part of your deployment/ build step in CI + +```terminal +npx prisma migrate deploy +``` + +You have successfully: + +- Migrated data from the `published` to `status` column +- Dropped the `published` column from your schema diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/50-squashing-migrations.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/50-squashing-migrations.mdx new file mode 100644 index 0000000000..32e26652da --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/50-squashing-migrations.mdx @@ -0,0 +1,129 @@ +--- +title: Squashing migrations +metaTitle: Squashing migrations +metaDescription: How to squash multiple migration files into a single migration +--- + + + +This guide describes how to squash multiple [migration files](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories) into a single migration. + + + +## About squashing migrations + +It is sometimes useful to squash either some or all migration files into a single migration. This guide will describe two scenarios where you may want to do this: + +- [Migrating cleanly from a development environment](#migrating-cleanly-from-a-development-environment) by squashing your local migrations into one before merging +- [Creating a clean history in a production environment](#creating-a-clean-history-in-a-production-environment) by squashing all migrations into a single file + +In both cases, Prisma Migrate provides the tools for doing this, by using the [`migrate diff`](/orm/reference/prisma-cli-reference#migrate-diff) command to compare two database schemas and output a single SQL file that takes you from one to the other. The rest of this guide gives detailed instructions on how to carry this out in these two scenarios. + +### Migrating cleanly from a development environment + +Squashing migrations can be useful when developing with a branch-based workflow. During a large local development effort on a feature branch you might generate multiple migrations using `migrate dev`. After the feature is finished, the migration history might contain unnecessary intermediate steps that are unwanted in the final migration history that will be pushed to the `main` branch. + +There could be important reasons to avoid applying the intermediate steps in production — they might lose data or be extremely slow / disruptive). Even when this is not the case, you may want to avoid clutter in your production environment's migrations history. + +For detailed steps on how to achieve this using `migrate dev`, see the section on [how to migrate cleanly from a development environment](#how-to-migrate-cleanly-from-a-development-environment). + +### Creating a clean history in a production environment + +Squashing migrations can also be used in a production environment to squash all migration files into one. This can be useful when the production environment has accumulated a longer migration history, and replaying it in new environments has become a burden due to intermediate steps requiring extra time. Since the team is not deriving value from the migration steps (and could get them back from version control history in a pinch) the decision is made to squash the whole history into a single migration. + +For detailed steps on how to achieve this using `migrate diff` and `migrate resolve` see the section on [how to create a clean history in a production environment](#how-to-create-a-clean-history-in-a-production-environment). + +## Considerations when squashing migrations + + + +When squashing migrations, be aware that any manually changed or added SQL in your `migration.sql` files will not be retained. If you have migration files with custom additions such as a view or a trigger, ensure to re-add them after your migrations were squashed. + + + +## How to squash migrations + +This section provides step-by-step instructions on how to squash migrations in the two scenarios discussed above: + +- [Migrating cleanly from a development environment](#how-to-migrate-cleanly-from-a-development-environment) +- [Creating a clean history in a production environment](#how-to-create-a-clean-history-in-a-production-environment) + +### How to migrate cleanly from a development environment + +Before squashing your migrations, make sure you have the following starting conditions: + +- The contents of the migrations to be squashed are not yet applied on the production database +- All migrations applied to production are part of the local migration history already +- There is no custom SQL in any of the new migration files that you have added to your branch + + + +If the migration history on the production database has diverged after you created your feature branch, then you would need to first merge the migrations history and the datamodel changes from production into your local history. + + + +Then follow these steps: + +1. Reset the contents of your local `./prisma/migrations` folder to match the migration history on the `main` branch + +2. Create a new migration: + + ```terminal + npx prisma migrate dev --name squashed_migrations + ``` + + This creates a single migration that takes you: + + - from the state of the `main` branch as described in your reset migration history + - to the state of your local feature as described in your `./prisma/schema.prisma` file + - and outputs this to a new `migration.sql` file in a new directory ending with `squashed_migrations` (specified with the `--name` flag) + +This single migration file can now be applied to production using `migrate deploy`. + +### How to create a clean history in a production environment + +Before squashing your migrations, make sure you have the following starting conditions: + +- All migrations in the migration history are applied on the production database +- The datamodel matches the migration history +- The datamodel and the migration history are in sync + +Then follow these steps, either on your `main` branch or on a newly checked out branch that gets merged back to `main` before anything else changes there: + +1. Delete all contents of the `./prisma/migrations` directory + +2. Create a new empty directory in the `./prisma/migrations` directory. In this guide this will be called `000000000000_squashed_migrations`. Inside this, add a new empty `migration.sql` file. + + + + We name the migration `000000000000_squashed_migrations` with all the leading zeroes because we want it to be the first migration in the migrations directory. Migrate runs the migrations in the directory in lexicographic (alphabetical) order. This is why it generates migrations with the date and time as a prefix when you use `migrate dev`. You can give the migration another name, as long as it it sorts lower than later migrations, for example `0_squashed` or `202207180000_squashed`. + + + +3. Create a single migration that takes you: + + - from an empty database + - to the current state of the production database schema as described in your `./prisma/schema.prisma` file + - and outputs this to the `migration.sql` file created above + + You can do this using the `migrate diff` command. From the root directory of your project, run the following command: + + ```terminal + npx prisma migrate diff \ + --from-empty \ + --to-schema-datamodel ./prisma/schema.prisma \ + --script > ./prisma/migrations/000000000000_squashed_migrations/migration.sql + ``` + +4. Mark this migration as having been applied on production, to prevent it from being run there: + + You can do this using the [`migrate resolve`](/orm/reference/prisma-cli-reference#migrate-resolve) command to mark the migration in the `000000000000_squashed_migrations` directory as already applied: + + ```terminal + npx prisma migrate resolve \ + --applied 000000000000_squashed_migrations + ``` + +You should now have a single migration file that is marked as having been applied on production. New checkouts only get one single migration taking them to the state of the production database schema. + +The production database still contains the history of applied migrations in the migrations table. The history of the migrations folder and data models is also still available in source control. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/60-generating-down-migrations.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/60-generating-down-migrations.mdx new file mode 100644 index 0000000000..f21838bc72 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/60-generating-down-migrations.mdx @@ -0,0 +1,150 @@ +--- +title: Generating down migrations +metaTitle: Generating down migrations +metaDescription: How to generate down migrations +tocDepth: 3 +--- + + + +This guide describes how to generate a down migration SQL file that reverses a given [migration file](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories). + + + +## About down migrations + +When generating a migration SQL file, you may wish to also create a "down migration" SQL file that reverses the schema changes in the corresponding "up migration" file. Note that "down migrations" are also sometimes called "migration rollbacks". + +This guide explains how to use Prisma Migrate's [`migrate diff` command](/orm/reference/prisma-cli-reference#migrate-diff) to create a down migration, and how to apply it to your production database with the [`db execute`](/orm/reference/prisma-cli-reference#db-execute) command in the case of a failed up migration. + + + +This guide applies to generating SQL down migrations for relational databases only. It does not apply to MongoDB. + + + + + +The `migrate diff` and `db execute` commands are available in Preview in versions `3.9.0` and later, and are generally available in versions `3.13.0` and later. + + + +## Considerations when generating down migrations + +When generating a down migration file, there are some considerations to be aware of: + +- The down migration can be used to revert your database schema after a failed migration using the steps in [How to apply your down migration to a failed migration](#how-to-apply-your-down-migration-to-a-failed-migration). This requires the use of the `migrate resolve` command, which can only be used on failed migrations. If your up migration was successful and you want to revert it, you will instead need to revert your `schema.prisma` file to its state before the up migration, and generate a new migration with the `migrate dev` command. +- The down migration will revert your database schema, but other changes to data and application code that are carried out as part of the up migration will not be reverted. For example, if you have a script that changes data during the migration, this data will not be changed back when you run the down migration. +- You will not be able to use `migrate diff` to revert manually changed or added SQL in your migration files. If you have any custom additions, such as a view or trigger, you will need to: + - Create the down migration following [the instructions below](#how-to-generate-and-run-down-migrations) + - Create the up migration using [`migrate dev --create-only`](/orm/reference/prisma-cli-reference#options-6), so that it can be edited before it is applied to the database + - Manually add your custom SQL to the up migration (e.g. adding a view) + - Manually add the inverted custom SQL to the down migration (e.g. dropping the view) + +## How to generate and run down migrations + +This section describes how to generate a down migration SQL file along with the corresponding up migration, and then run it to revert your database schema after a failed up migration on production. + +As an example, take the following Prisma schema with a `User` and `Post` model as a starting point: + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + title String @db.VarChar(255) + content String? + author User @relation(fields: [authorId], references: [id]) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + name String? + posts Post[] +} +``` + +You will need to create the down migration first, before creating the corresponding up migration. + +### Generating the migrations + +1. Edit your Prisma schema to make the changes you require for your up migration. In this example, you will add a new `Profile` model: + + ```prisma file=schema.prisma highlight=8-14;add|20;add + model Post { + id Int @id @default(autoincrement()) + title String @db.VarChar(255) + content String? + author User @relation(fields: [authorId], references: [id]) + authorId Int + } + + model Profile { + id Int @id @default(autoincrement()) + bio String? + user User @relation(fields: [userId], references: [id]) + userId Int @unique + } + + model User { + id Int @id @default(autoincrement()) + name String? + posts Post[] + profile Profile? + } + ``` + +2. Generate the SQL file for the down migration. To do this, you will use `migrate diff` to make a comparison: + + - from the newly edited schema + - to the state of the schema after the last migration + + and output this to a SQL script, `down.sql`. + + There are two potential options for specifying the 'to' state: + + - Using `--to-migrations`: this makes a comparison to the state of the migrations given in the migrations directory. This is the preferred option, as it is more robust, but it requires a [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database). To use this option, run: + + ```terminal wrap + npx prisma migrate diff \ + --from-schema-datamodel prisma/schema.prisma \ + --to-migrations prisma/migrations \ + --shadow-database-url $SHADOW_DATABASE_URL \ + --script > down.sql + ``` + + - Using `--to-schema-datasource`: this makes a comparison to the state of the database. This does not require a shadow database, but it does rely on the database having an up-to-date schema. To use this option, run: + + ```terminal wrap + npx prisma migrate diff \ + --from-schema-datamodel prisma/schema.prisma \ + --to-schema-datasource prisma/schema.prisma \ + --script > down.sql + ``` + +3. Generate and apply the up migration with a name of `add_profile`: + + ```terminal + npx prisma migrate dev --name add_profile + ``` + + This will create a new `_add_profile` directory inside the `prisma/migrations` directory, with your new `migration.sql` up migration file inside. + +4. Copy your `down.sql` file into the new directory along with the up migration file. + +### How to apply your down migration to a failed migration + +If your previous up migration failed, you can apply your down migration on your production database with the following steps: + +To apply the down migration on your production database after a failed up migration: + +1. Use `db execute` to run your `down.sql` file on the database server: + + ```terminal + npx prisma db execute --file ./down.sql --schema prisma/schema.prisma + ``` + +2. Use `migrate resolve` to record that you rolled back the up migration named `add_profile`: + + ```terminal + npx prisma migrate resolve --rolled-back add_profile + ``` diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/70-patching-and-hotfixing.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/70-patching-and-hotfixing.mdx new file mode 100644 index 0000000000..aae6a3f807 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/70-patching-and-hotfixing.mdx @@ -0,0 +1,273 @@ +--- +title: Patching & hotfixing +navTitle: Patching & hotfixing +metaDescription: How to reconcile the migration history after applying a hotfix or patch to a production environment. +--- + + + +Patching or hotfixing a database involves making an often time critical change directly in production. For example, you might add an index directly to a production database to resolve an issue with a slow-running query. + +Patching the production database directly results in **schema drift**: your database schema has 'drifted away' from the source of truth, and is out of sync with your migration history. You can use the `prisma migrate resolve` command to reconcile your migration history _without_ having to remove and re-apply the hotfix with `prisma migrate deploy`. + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate dev`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +
+ +## Reconciling your migration history with a patch or hotfix + +The following scenario assumes that you made a manual change in production and want to propagate that change to your migration history and other databases. + +To reconcile your migration history and database schema in production: + + + +1. Replicate the change you made in production in the schema - for example, add an `@@index` to a particular model. +1. Generate a new migration and take note of the full migration name, including a timestamp, which is written to the CLI:(`20210316150542_retroactively_add_index`): + + + + + ```terminal + npx prisma migrate dev --name retroactively-add-index + ``` + + + + + ```bash no-copy + migrations/ + └─ 20210316150542_retroactively_add_index/ + └─ migration.sql + + Your database is now in sync with your schema. + + ✔ Generated Prisma Client (2.19.0-dev.29) to .\node_modules\@prisma\client in 190ms + ``` + + + + +1. Push the migration to production **without running `migrate deploy`**. Instead, mark the migration created in the previous step as 'already applied' so that Prisma Migrate does not attempt to apply your hotfix a second time: + + ```terminal + prisma migrate resolve --applied "20201127134938-retroactively-add-index" + ``` + + This command adds the migration to the migration history table without running the actual SQL. + +1. Repeat the previous step for other databases that were patched - for example, if you applied the patch to a staging database. + +1. Propagate the migration to other databases that were not patched - for example, by committing the migration to source control and allowing your CI/CD pipeline to apply it to all databases. + + + +> **Note**: The migration will not be applied to databases where it has been marked as already applied by the `prisma migrate resolve` command. + +## Failed migration + +A migration might fail if: + +- You [modify a migration before running it](/orm/prisma-migrate/workflows/customizing-migrations) and introduce a syntax error +- You add a mandatory (`NOT NULL`) column to a table that already has data +- The migration process stopped unexpectedly +- The database shut down in the middle of the migration process + +Each migration in the `_prisma_migrations` table has a `logs` column that stores the error. + +There are two ways to deal with failed migrations in a production environment: + +- Roll back, optionally fix issues, and re-deploy +- Manually complete the migration steps and resolve the migration + +### Option 1: Mark the migration as rolled back and re-deploy + +The following example demonstrates how to roll back a migration, optionally make changes to fix the issue, and re-deploy: + +1. Mark the migration as rolled back - this updates the migration record in the `_prisma_migrations` table to register it as rolled back, allowing it to be applied again: + + ```terminal + prisma migrate resolve --rolled-back "20201127134938_added_bio_index" + ``` + +1. If the migration was partially run, you can either: + + - Modify the migration to check if a step was already completed (for example: `CREATE TABLE ... IF NOT EXISTS`) _OR_ + - Manually revert the steps that were completed (for example, delete created tables) + + > If you modify the migration, make sure you copy it back to source control to ensure that state of your production database is reflected exactly in development. + +1. Fix the root cause of the failed migration, if relevant - for example, if the migration failed due to an issue with the SQL script itself. Make sure that you copy any changed migrations back to source control. + +1. Re-deploy the migration: + + ```terminal + prisma migrate deploy + ``` + +### Option 2: Manually complete migration and resolve as applied + +The following example demonstrates how to manually complete the steps of a migration and mark that migration as applied. + +1. Manually complete the migration steps on the production database. Make sure that any manual steps exactly match the steps in the migration file, and copy any changes back to source control. + +1. Resolve the migration as applied - this tells Prisma Migrate to consider the migration successfully applied: + + ```terminal + prisma migrate resolve --applied "20201127134938_my_migration" + ``` + +## Fixing failed migrations with `migrate diff` and `db execute` + +To help with fixing a failed migration, Prisma provides the following commands for creating and executing a migration file: + +- [`prisma migrate diff`](/orm/reference/prisma-cli-reference#migrate-diff) which diffs two database schema sources to create a migration taking one to the state of the second. You can output either a summary of the difference or a sql script. The script can be output into a file via `> file_name.sql` or be piped to the `db execute --stdin` command. +- [`prisma db execute`](/orm/reference/prisma-cli-reference#db-execute) which applies a SQL script to the database without interacting with the Prisma migrations table. + +These commands are available in Preview in versions `3.9.0` and later (with the `--preview-feature` CLI flag), and generally available in versions `3.13.0` and later. + +This section gives an example scenario of a failed migration, and explains how to use `migrate diff` and `db execute` to fix it. + +### Example of a failed migration + +Imagine that you have the following `User` model in your schema, in both your local development environment and your production environment: + +```prisma file=schema.prisma +model User { + id Int @id + name String +} +``` + +At this point, your schemas are in sync, but the data in the two environments is different. + +You then decide to make changes to your data model, adding another `Post` model and making the `name` field on `User` unique: + +```prisma file=schema.prisma +model User { + id Int @id + name String @unique + email String? +} + +model Post { + id Int @id + title String +} +``` + +You create a migration called 'Unique' with the command `prisma migrate dev -n Unique` which is saved in your local migrations history. Applying the migration succeeds in your dev environment and now it is time to release to production. + +Unfortunately this migration can only be partially executed. Creating the `Post` model and adding the `email` column succeeds, but making the `name` field unique fails with the following error: + +```bash +ERROR 1062 (23000): Duplicate entry 'paul' for key 'User_name_key' +``` + +This is because there is non-unique data in your production database (e.g. two users with the same name). + +You now need to recover manually from the partially executed migration. Until you recover from the failed state, further migrations using `prisma migrate deploy` are impossible. + +At this point there are two options, depending on what you decide to do with the non-unique data: + +- You realize that non-unique data is valid and you cannot move forward with your current development work. You want to roll back the complete migration. To do this, see [Moving backwards and reverting all changes](#moving-backwards-and-reverting-all-changes) +- The existence of non-unique data in your database is unintentional and you want to fix that. After fixing, you want to go ahead with the rest of the migration. To do this, see [Moving forwards and applying missing changes](#moving-forwards-and-applying-missing-changes) + +#### Moving backwards and reverting all changes + +In this case, you need to create a migration that takes your production database to the state of your data model before the last migration. + +- First you need your migration history at the time before the failed migration. You can either get this from your git history, or locally delete the folder of the last failed migration in your migration history. +- You now want to take your production environment from its current failed state back to the state specified in your local migrations history: + + - Run the following `prisma migrate diff` command: + + ```terminal wrap + npx prisma migrate diff \ + --from-url "$DATABASE_URL_PROD" \ + --to-migrations ./prisma/migrations \ + --shadow-database-url $SHADOW_DATABASE_URL \ + --script > backward.sql + ``` + + This will create a SQL script file containing all changes necessary to take your production environment from its current failed state to the target state defined by your migrations history. + Note that because we're using `--to-migrations`, the command requires a [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database). + + - Run the following `prisma db execute` command: + + ```bash + npx prisma db execute --url "$DATABASE_URL_PROD" --file backward.sql + ``` + + This applies the changes in the SQL script against the target database without interacting with the migrations table. + + - Run the following `prisma migrate resolve` command: + + ```bash + npx prisma migrate resolve --rolled-back Unique + ``` + + This will mark the failed migration called 'Unique' in the migrations table on your production environment as rolled back. + +Your local migration history now yields the same result as the state your production database is in. You can now modify the datamodel again to create a migration that suits your new understanding of the feature you're working on (with non-unique names). + +#### Moving forwards and applying missing changes + +In this case, you need to fix the non-unique data and then go ahead with the rest of the migration as planned: + +- The error message from trying to deploy the migration to production already told you there was duplicate data in the column `name`. You need to either alter or delete the offending rows. +- Continue applying the rest of the failed migration to get to the data model defined in your `schema.prisma` file: + + - Run the following `prisma migrate diff` command: + + ```bash + + npx prisma migrate diff --from-url "$DATABASE_URL_PROD" --to-schema-datamodel schema.prisma --script > forward.sql + + ``` + + This will create a SQL script file containing all changes necessary to take your production environment from its current failed state to the target state defined in your `schema.prisma` file. + + - Run the following `prisma db execute` command: + + ```bash + npx prisma db execute --url "$DATABASE_URL_PROD" --file forward.sql + ``` + + This applies the changes in the SQL script against the target database without interacting with the migrations table. + + - Run the following `prisma migrate resolve` command: + + ```bash + npx prisma migrate resolve --applied Unique + ``` + + This will mark the failed migration called 'Unique' in the migrations table on your production environment as applied. + +Your local migration history now yields the same result as the state your production environment is in. You can now continue using the already known `migrate dev` /`migrate deploy` workflow. + +## Migration history conflicts + + + +This does not apply from version [3.12.0](https://github.com/prisma/prisma/releases/tag/3.12.0) upwards. + + + +`prisma migrate deploy` issues a warning if an already applied migration has been edited - however, it does not stop the migration process. To remove the warnings, restore the original migration from source control. + +## Prisma Migrate and PgBouncer + +You might see the following error if you attempt to run Prisma Migrate commands in an environment that uses PgBouncer for connection pooling: + +```bash +Error: undefined: Database error +Error querying the database: db error: ERROR: prepared statement "s0" already exists +``` + +See [Prisma Migrate and PgBouncer workaround](/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer) for further information and a workaround. Follow [GitHub issue #6485](https://github.com/prisma/prisma/issues/6485) for updates. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/80-unsupported-database-features.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/80-unsupported-database-features.mdx new file mode 100644 index 0000000000..23a387bc28 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/80-unsupported-database-features.mdx @@ -0,0 +1,57 @@ +--- +title: Unsupported database features +metaTitle: Unsupported database features +metaDescription: How to include unsupported database features for projects that use Prisma Migrate. +--- + + + +Prisma Migrate uses the Prisma schema to determine what features to create in the database. However, some database features [cannot be represented in the Prisma schema](/orm/prisma-schema/data-model/unsupported-database-features) , including but not limited to: + +- Stored procedures +- Triggers +- Views +- Partial indexes + +To add an unsupported feature to your database, you must [customize a migration](/orm/prisma-migrate/workflows/customizing-migrations) to include that feature before you apply it. + +:::tip + +The Prisma schema is able to represent [unsupported field types](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-field-types) and [native database functions](/orm/prisma-migrate/workflows/native-database-functions). + +::: + + + +This guide **does not apply for MongoDB**.
+Instead of `migrate dev`, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +
+ +## Customize a migration to include an unsupported feature + +To customize a migration to include an unsupported feature: + +1. Use the `--create-only` flag to generate a new migration without applying it: + + ```terminal + npx prisma migrate dev --create-only + ``` + +1. Open the generated `migration.sql` file and add the unsupported feature - for example, a partial index: + + ```sql + CREATE UNIQUE INDEX tests_success_constraint + ON posts (subject, target) + WHERE success; + ``` + +1. Apply the migration: + + ```terminal + npx prisma migrate dev + ``` + +1. Commit the modified migration to source control. diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/90-development-and-production.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/90-development-and-production.mdx new file mode 100644 index 0000000000..b84c774059 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/90-development-and-production.mdx @@ -0,0 +1,142 @@ +--- +title: 'Development and production' +metaTitle: 'Development and production' +metaDescription: 'Development and production' +tocDepth: 3 +--- + + + +This page explains how to use Prisma Migrate commands in development and production environments. + + + +## Development environments + +In a development environment, use the `migrate dev` command to generate and apply migrations: + +```terminal +npx prisma migrate dev +``` + +### Create and apply migrations + + + +`migrate dev` is a development command and should never be used in a production environment. + + + +This command: + +1. Reruns the existing migration history in the [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database) in order to detect schema drift (edited or deleted migration file, or a manual changes to the database schema) +1. Applies pending migrations to the shadow database (for example, new migrations created by colleagues) +1. If it detects changes to the Prisma schema, it generates a new migration from these changes +1. Applies all unapplied migrations to the development database and updates the `_prisma_migrations` table +1. Triggers the generation of artifacts (for example, Prisma Client) + +The `migrate dev` command will prompt you to reset the database in the following scenarios: + +- Migration history conflicts caused by [modified or missing migrations](/orm/prisma-migrate/understanding-prisma-migrate/migration-histories#do-not-edit-or-delete-migrations-that-have-been-applied) +- The database schema has drifted away from the end-state of the migration history + +### Reset the development database + +You can also `reset` the database yourself to undo manual changes or `db push` experiments by running: + +```terminal +npx prisma migrate reset +``` + + + +`migrate reset` is a development command and should never be used in a production environment. + + + +This command: + +1. Drops the database/schema¹ if possible, or performs a soft reset if the environment does not allow deleting databases/schemas¹ +1. Creates a new database/schema¹ with the same name if the database/schema¹ was dropped +1. Applies all migrations +1. Runs seed scripts + +¹ For MySQL and MongoDB this refers to the database, for PostgreSQL and SQL Server to the schema, and for SQLite to the database file. + +> **Note**: For a simple and integrated way to re-create data in your development database as often as needed, check out our [seeding guide](/orm/prisma-migrate/workflows/seeding). + +### Customizing migrations + +Sometimes, you need to modify a migration **before applying it**. For example: + +- You want to introduce a significant refactor, such as changing blog post tags from a `String[]` to a `Tag[]` +- You want to [rename a field](/orm/prisma-migrate/workflows/customizing-migrations#example-rename-a-field) (by default, Prisma Migrate will drop the existing field) +- You want to [change the direction of a 1-1 relationship](/orm/prisma-migrate/workflows/customizing-migrations#example-change-the-direction-of-a-1-1-relation) +- You want to add features that cannot be represented in Prisma Schema Language - such as a partial index or a stored procedure. + +The `--create-only` command allows you to create a migration without applying it: + +```terminal +npx prisma migrate dev --create-only +``` + +To apply the edited migration, run `prisma migrate dev` again. + +Refer to [Customizing migrations](/orm/prisma-migrate/workflows/customizing-migrations) for examples. + +### Team development + +See: [Team development with Prisma Migrate](/orm/prisma-migrate/workflows/team-development) . + +## Production and testing environments + +In production and testing environments, use the `migrate deploy` command to apply migrations: + +```terminal +npx prisma migrate deploy +``` + +> **Note**: `migrate deploy` should generally be part of an automated CI/CD pipeline, and we do not recommend running this command locally to deploy changes to a production database. + +This command: + +1. Compares applied migrations against the migration history and **warns** if any migrations have been modified: + + ```bash + WARNING The following migrations have been modified since they were applied: + 20210313140442_favorite_colors + ``` + +1. Applies pending migrations + +The `migrate deploy` command: + +- **Does not** issue a warning if an already applied migration is _missing_ from migration history +- **Does not** detect drift (production database schema differs from migration history end state - for example, due to a hotfix +- **Does not** reset the database or generate artifacts (such as Prisma Client) +- **Does not** rely on a shadow database + +See also: + +- [Prisma Migrate in deployment](/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate) +- [Production troubleshooting](/orm/prisma-migrate/workflows/patching-and-hotfixing) + +### Advisory locking + +Prisma Migrate makes use of advisory locking when you run production commands such as: + +- `prisma migrate deploy` +- `prisma migrate dev` +- `prisma migrate resolve` + +This safeguard ensures that multiple commands cannot run at the same time - for example, if you merge two pull requests in quick succession. + +Advisory locking has a **10 second timeout** (not configurable), and uses the default advisory locking mechanism available in the underlying provider: + +- [PostgreSQL](https://www.postgresql.org/docs/9.4/explicit-locking.html#ADVISORY-LOCKS) +- [MySQL](https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html) +- [Microsoft SQL server](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver15) + +Prisma Migrate's implementation of advisory locking is purely to avoid catastrophic errors - if your command times out, you will need to run it again. + +Since `5.3.0`, the advisory locking can be disabled using the [`PRISMA_SCHEMA_DISABLE_ADVISORY_LOCK` environment variable](/orm/reference/environment-variables-reference#prisma_schema_disable_advisory_lock) diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/deploy-db.png b/docs/200-orm/300-prisma-migrate/300-workflows/deploy-db.png new file mode 100644 index 0000000000..90f6b2a049 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/300-workflows/deploy-db.png differ diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/existing-database.png b/docs/200-orm/300-prisma-migrate/300-workflows/existing-database.png new file mode 100644 index 0000000000..c91a0b6b86 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/300-workflows/existing-database.png differ diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/index.mdx b/docs/200-orm/300-prisma-migrate/300-workflows/index.mdx new file mode 100644 index 0000000000..fcdb1e427e --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/300-workflows/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Workflows' +metaTitle: 'Workflows' +metaDescription: 'Learn about important Prisma Migrate workflows, like seeding, prototyping, baselining, & more.' +--- + +## In this section + + diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/migrate-team-dev.png b/docs/200-orm/300-prisma-migrate/300-workflows/migrate-team-dev.png new file mode 100644 index 0000000000..c350446336 Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/300-workflows/migrate-team-dev.png differ diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/migration-history.png b/docs/200-orm/300-prisma-migrate/300-workflows/migration-history.png new file mode 100644 index 0000000000..7fedefbd2b Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/300-workflows/migration-history.png differ diff --git a/docs/200-orm/300-prisma-migrate/300-workflows/new-dev-db.png b/docs/200-orm/300-prisma-migrate/300-workflows/new-dev-db.png new file mode 100644 index 0000000000..661a40cc7f Binary files /dev/null and b/docs/200-orm/300-prisma-migrate/300-workflows/new-dev-db.png differ diff --git a/docs/200-orm/300-prisma-migrate/index.mdx b/docs/200-orm/300-prisma-migrate/index.mdx new file mode 100644 index 0000000000..2dae4d4947 --- /dev/null +++ b/docs/200-orm/300-prisma-migrate/index.mdx @@ -0,0 +1,11 @@ +--- +title: 'Prisma Migrate' +metaTitle: 'Prisma Migrate | Database, Schema, SQL Migration Tool' +metaDescription: 'Prisma Migrate is a database migration tool available via the Prisma CLI that integrates with Prisma schema for data modeling.' +staticLink: true +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/400-tools/05-prisma-cli.mdx b/docs/200-orm/400-tools/05-prisma-cli.mdx new file mode 100644 index 0000000000..7010a28f38 --- /dev/null +++ b/docs/200-orm/400-tools/05-prisma-cli.mdx @@ -0,0 +1,261 @@ +--- +title: 'Prisma CLI' +metaTitle: 'Prisma CLI' +metaDescription: 'The Prisma command line interface (CLI) is the primary way to interact with your Prisma project from the command line.' +toc: true +--- + + + +The Prisma command line interface (CLI) is the primary way to interact with your Prisma project from the command line. It can initialize new project assets, generate Prisma Client, and analyze existing database structures through introspection to automatically create your application models. + + + +## Command reference + +See [Prisma CLI command reference](/orm/reference/prisma-cli-reference) for a complete list of commands. + +## Installation + +The Prisma CLI is typically installed locally as a **development dependency**, that's why the `--save-dev` (npm) and `--dev` (Yarn) options are used in the commands below. + + + +We **recommend that you install the Prisma CLI locally** in your project's `package.json` to avoid version conflicts that can happen with a global installation. + + + +### npm + +Install with [npm](https://www.npmjs.com/): + +``` +npm install prisma --save-dev +``` + +### Yarn + +Install with [yarn](https://yarnpkg.dev/): + +``` +yarn add prisma --dev +``` + +### pnpm + +Install with [pnpm](https://pnpm.io/): + +``` +pnpm install prisma --save-dev +``` + +### Bun + +Install with [Bun](https://bun.sh/): + +``` +bun add prisma +``` + +
+Global installation (Not recommended) + +- **npm** + +Install with npm: + +``` +npm install -g prisma +``` + +- **Yarn** + +Install with Yarn: + +``` +yarn global add prisma +``` + +- **pnpm** + +Install with pnpm: + +``` +pnpm install prisma --global +``` + +- **Bun** + +Install with [Bun](https://bun.sh): + +``` +bun add --global prisma +``` + +
+ +## Usage + +If you installed Prisma as a development dependency, you need to prefix the `prisma` command with your package runner. + +### npm + +``` +npx prisma +``` + +### Yarn + +``` +yarn prisma +``` + +### pnpm + +``` +pnpm dlx prisma +``` + +### Bun + +``` +bunx prisma +``` + +## Synopsis + +The `prisma` command can be called from command line once installed. When called without arguments, it will display its command usage and help document: + + + + + +```terminal +prisma +``` + + + + + +```code no-copy +Prisma is a modern DB toolkit to query, migrate and model your database (https://www.prisma.io) + +Usage + + $ prisma [command] + +Commands + + init Setup Prisma for your app + generate Generate artifacts (e.g. Prisma Client) + db Manage your database schema and lifecycle + migrate Migrate your database + studio Browse your data with Prisma Studio + validate Validate your Prisma schema + format Format your Prisma schema + +Flags + + --preview-feature Run Preview Prisma commands + +Examples + + Setup a new Prisma project + $ prisma init + + Generate artifacts (e.g. Prisma Client) + $ prisma generate + + Browse your data + $ prisma studio + + Create migrations from your Prisma schema, apply them to the database, generate artifacts (e.g. Prisma Client) + $ prisma migrate dev + + Pull the schema from an existing database, updating the Prisma schema + $ prisma db pull + + Push the Prisma schema state to the database + $ prisma db push +``` + + + + + +You can get additional help on any of the `prisma` commands by adding the `--help` flag after the command. + +## Exit codes + +All `prisma` CLI commands return the following codes when they exit: + +- exit code 0 when a command runs successfully +- exit code 1 when a command errors +- exit code 130 when the CLI receives a signal interrupt (SIGINT) message or if the user cancels a prompt. This exit code is available in Prisma versions 4.3.0 and later. + +## Telemetry + +The term **telemetry** refers to the collection of certain usage data to help _improve the quality of a piece of software_. Prisma uses telemetry in two contexts: + +- when it collects CLI usage data +- when it submits CLI error reports + +This page describes the overall telemetry approach for Prisma, what kind of data is collected and how to opt-out of data collection. + +### Why does Prisma collect metrics? + +Telemetry helps us better understand _how many users_ are using our products and _how often_ they are using our products. Unlike many telemetry services, our telemetry implementation is intentionally limited in scope and is actually useful for the developer: + +- **Limited in scope**: We use telemetry to answer one question: how many monthly active developers are using Prisma CLI? +- **Provides value**: Our telemetry service also checks for version updates and offers security notices. + +### When is data collected? + +Data is collected in two scenarios that are described below. + +#### Usage data + +Invocations of the `prisma` CLI and general usage of Studio results in data being sent to the telemetry server at https://checkpoint.prisma.io. Note that: + +- The data does **not** include your schema or the data in your database +- Prisma only sends information after you execute a CLI command + +Here is an overview of the data that's being submitted: + +| Field | Attributes | Description | +| -------------: | :--------: | :------------------------------------------------------------------------------------- | +| `product` | _string_ | Name of the product (e.g. `prisma`) | +| `version` | _string_ | Currently installed version of the product (e.g. `1.0.0-rc0`) | +| `arch` | _string_ | Client's operating system architecture (e.g. `amd64`). | +| `os` | _string_ | Client's operating system (e.g. `darwin`). | +| `node_version` | _string_ | Client's node version (e.g. `v12.12.0`). | +| `signature` | _string_ | Random, non-identifiable signature UUID (e.g. `91b014df3-9dda-4a27-a8a7-15474fd899f8`) | +| `user_agent` | _string_ | User agent of the checkpoint client (e.g. `prisma/js-checkpoint`) | +| `timestamp` | _string_ | When the request was made in RFC3339 format (e.g. `2019-12-12T17:45:56Z`) | + +You can opt-out of this behavior by setting the `CHECKPOINT_DISABLE` environment variable to `1`, e.g.: + +```terminal +export CHECKPOINT_DISABLE=1 +``` + +#### Error reporting + +Prisma potentially collects error data when there is a crash in the CLI. + +Before an error report is submitted, there will _always_ be a prompt asking you to confirm or deny the submission of the error report! Error reports are never submitted without your explicit consent! + +### How to opt-out of data collection? + +#### Usage data + +You can opt-out of usage data collection by setting the `CHECKPOINT_DISABLE` environment variable to `1`, e.g.: + +```terminal +export CHECKPOINT_DISABLE=1 +``` + +#### Error reporting + +You can opt-out of data collection by responding to the interactive prompt with _no_. diff --git a/docs/200-orm/400-tools/06-prisma-studio.mdx b/docs/200-orm/400-tools/06-prisma-studio.mdx new file mode 100644 index 0000000000..d86da69998 --- /dev/null +++ b/docs/200-orm/400-tools/06-prisma-studio.mdx @@ -0,0 +1,286 @@ +--- +title: 'Prisma Studio' +metaTitle: 'Prisma Studio' +metaDescription: 'Prisma Studio is a visual database editor.' +--- + + + +Prisma Studio is a visual editor for the data in your database. Note that Prisma Studio is not open source but you can still create issues in the [`prisma/studio`](https://github.com/prisma/studio) repo. + +Run `npx prisma studio` in your terminal. + + + +## Models (tables or collections) + +When you first open Prisma Studio, you see a list of all models defined in your Prisma schema file. + + + +**What is a model?**

+ +The term **model** refers to the data model definitions that you add to the Prisma schema file. Depending on the database that you use, a model definition, such as `model User`, refers to a **table** in a relational database (PostgreSQL, MySQL, SQL Server, SQLite, CockroachDB) or a **collection** in MongoDB.

+For more information, see [Defining models](/orm/prisma-schema/data-model/models#defining-models). + +
+ +You can select a model and its data opens in a new tab. + +Prisma Studio - Models view + +### Open and close models + +To open another model, click the **+** button. + +To close a model, click the the **x** button in the model tab. + +Prisma Studio - Open and close models + +### Icons of data types in models + +The data type for each field is indicated with an icon in the header. + +The table below lists all data types and their identifying icon. + +| Field data type | Description | +| :-------------------------------------------------------------------------------: | ----------------------------------------------------------------------------------------------------------- | +| String type | Text | +| Number type | Integer | +| Datetime type | Date-time

| +| Boolean type | Boolean
| +| Enum type | Pre-defined list of values (`enum` data type) | +| Array type | List of related records from another model | +| Object type | The `{}` symbol can refer to one of the two types of fields.

• Relation field
• JSON field | + +### Keyboard shortcuts in models + +When you open a model, a number of keyboard shortcuts are available to browse and manipulate the data in the model. + + + +**Note**

+With Prisma Studio open, you can open the keyboard shortcuts modal by pressing Cmd ⌘+/ on macOS or Ctrl+/ on Windows. + +
+ +Prisma Studio - Keyboard shortcuts + +## Edit data + +In the model view, you can edit data directly in the model cells. You can copy and paste values in cells. You can add new records as well as delete existing records. + +You must confirm every edit operation (add, edit, or delete). You confirm added and edited records with the **Save change** button. When you select records and click **Delete records**, you confirm the deletion in a dialog box. + +You can accumulate multiple added records and edited cells, which you can then finalize with the **Save changes** button. + +You can select multiple records and delete them at once with the **Delete records** button. When you delete multiple records, the operation completes immediately (after you confirm it). + +In addition, if you have any accumulated added or edited records and then decide to delete records, the deletion also force-saves the accumulated edits. + +You can discard any accumulated changes with the **Discard changes** button. + +### Copy and paste + +You can copy the value of any table cell using: + +- Cmd ⌘ + C   on macOS +- Ctrl + C     on Windows + +To paste in another cell, first double-click the cell to enter edit mode, and then use: + +- Cmd ⌘ + V   on macOS +- Ctrl + V     on Windows + +### Add a record + +1. In the model view, click **Add record**. +2. Based on the data allowed in each field, type the data for the record. + + | Field data type | Description | + | :-------------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | String type | Text | + | Number type | Integer

If such a field has `autoincrement()` pre-filled, do not edit the cell and do not add a number manually. | + | Datetime type | Date-time

Date-time fields contain a long string of numbers, letters, and others. As a best practice, copy the value of another date-time cell and modify it as necessary before pasting in the field. | + | Boolean type | Boolean

Select `true` or `false`. | + | Enum type | Pre-defined list

Double-click a cell in the field and select one of the pre-defined options. | + | Array type | List of related records from another model

It typically refers to a list of records that exist in another model in the database. If you are adding a new record and records from the related model do not yet exist, you do not need to enter anything in the current model. | + | Object type | The `{}` symbol can refer to one of the two types of fields.

• Relation field
• JSON field

**Relation with a model defined separately in the database**

Typically, you need to select the same value as any of the previous records
Click the name of the model to see the list of values which you can then select for the related field.

**JSON field**

Double-click the field to edit the JSON data. As a best practice, validate the edited JSON data in a validator and paste it back in the cell. | + +3. (Optional) If you are unhappy with your changes, click **Discard changes** and start over. +4. Click **Save 1 change**. + +### Edit a record + +1. Double-click a cell with existing data to edit. +2. (Optional) If you are unhappy with your changes, click **Discard changes** and start over. +3. Click **Save 1 change**. + +### Delete a record + +1. From the left column, select the check box for the record you want to delete. +2. Click **Delete 1 record**. +3. Click **Delete** in the confirmation dialog. + +### Edit multiple records at once + +You can add multiple records, edit multiple cells and, thus, accumulate multiple edits. + +In the end, click **Save changes** to finalize them. + + + +**Warning**

+Deleting a record is a separate operation that cannot be accumulated. If you delete a record while having unsaved edits, the delete operation first force-saves the unsaved edits and then completes. + +
+ +Prisma Studio - Save multiple data edits + +## Filters + +### Filter data + +Use the **Filters** menu to filter data in the model by adding conditions. + +In the **Filters** menu, the first condition that you add is the `where` clause. + +When you add multiple conditions, Prisma Studio filters the results so that all conditions apply in combination. Each new condition indicates this with the `and` operator, which appears in front. + +**Steps** + +1. Click **Filters** to open the **Filters** menu. + + + + **Note**

+ Click **Filters** again if you want to hide the menu. + +
+ +2. Click **Add a new filter**. +3. Configure the condition. + 1. Select the field by which you want to filter. + 2. Select a comparison operator. + - **equals** + - **in** + - **notin** + - **lt** + - **lte** + - **gt** + - **gte** + - **not** + 3. Type the value you want to use for the condition.
+ **Step result**: **Prisma Studio** updates the data in the model immediately, based on the condition. +4. To add a new filter, click **Add a new filter** and repeat the steps above. +5. To remove a filter, click the **x** button on the right. + Prisma Studio - add and remove filters +6. To remove all filters, click **Clear all**. + +**Result** + +- The data in the model is filtered based on the combination of all conditions you add. +- In the **Filters** menu, the default value of **None** changes to display the number of filters you add. + +### Show and hide fields + +You can select which fields to view or hide by using the **Fields** menu. + + + +**What is a field?**

+ +A **field** is a property of a model which you add in the data model definitions in the Prisma schema file. Depending on the database that you use, a field, such as the `title` field in `model User { title String }`, refers to a **column** in a relational database (PostgreSQL, MySQL, SQL Server, SQLite, CockroachDB) or a **document field** in MongoDB.

+For more information, see [Defining fields](/orm/prisma-schema/data-model/models#defining-fields). + +
+ +**Steps** + +1. Click the **Fields** menu. +2. Select only the fields you want to see and deselect any fields you want to hide. + Prisma Studio - show and hide fields + +**Result** + +The model is immediately filtered to hide the data from any fields you have deselected. + +Also, the **Fields** menu shows the number of fields that are currently selected. + +### Show and hide records + +You can also select to show or skip a specific number of records in the model view. + + + +**What is a record?**

+ +A **record** refers to a **row of data in a table** in a relational database (PostgreSQL, MySQL, SQL Server, SQLite, CockroachDB) or a **document** in MongoDB. + +
+ +**Steps** + +1. Click the **Showing** menu. +2. In the **Take** box, specify the maximum number of records that you want the model view to show. +3. In the **Skip** box, specify how many of the first records you want to hide. + Prisma Studio - Show and hide records + +**Result** + +The model is immediately filtered to show or hide records based on your selection. + +The **Showing** menu indicates how many records are shown out of how many available records are in the model. + +## Sort data + +Click a field title to sort by the field data. + +The first click sorts the data in ascending order, the second - in descending order. + +Prisma Studio - Sort data + +## Troubleshooting + +### Terminal: Failed to run script / Error in Prisma Client request + +Caching issues may cause Prisma Studio to use an older version of the query engine. You may see the following error: + +``` +Error in request: PrismaClientKnownRequestError: Failed to validate the query Error occurred during query validation & transformation +``` + +To resolve, delete the following folders: + +- `~/.cache/prisma` on macOS and Linux +- `%AppData%/Prisma/Studio` on Windows diff --git a/docs/200-orm/400-tools/images/prisma-studio/01-models-view.png b/docs/200-orm/400-tools/images/prisma-studio/01-models-view.png new file mode 100644 index 0000000000..0d8b312332 Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/01-models-view.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/02-open-close-models.png b/docs/200-orm/400-tools/images/prisma-studio/02-open-close-models.png new file mode 100644 index 0000000000..21d1f990f0 Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/02-open-close-models.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/03-model-view-keyboard-shortcuts.png b/docs/200-orm/400-tools/images/prisma-studio/03-model-view-keyboard-shortcuts.png new file mode 100644 index 0000000000..7780c5fd13 Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/03-model-view-keyboard-shortcuts.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/04-save-multiple-changes.png b/docs/200-orm/400-tools/images/prisma-studio/04-save-multiple-changes.png new file mode 100644 index 0000000000..cfe5a4002d Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/04-save-multiple-changes.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/05-add-remove-filters.png b/docs/200-orm/400-tools/images/prisma-studio/05-add-remove-filters.png new file mode 100644 index 0000000000..546555eb36 Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/05-add-remove-filters.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/06-show-hide-fields.png b/docs/200-orm/400-tools/images/prisma-studio/06-show-hide-fields.png new file mode 100644 index 0000000000..e137653a1b Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/06-show-hide-fields.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/07-show-hide-records.png b/docs/200-orm/400-tools/images/prisma-studio/07-show-hide-records.png new file mode 100644 index 0000000000..3c58c2b9cf Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/07-show-hide-records.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/08-model-sort.png b/docs/200-orm/400-tools/images/prisma-studio/08-model-sort.png new file mode 100644 index 0000000000..6bac97e3ba Binary files /dev/null and b/docs/200-orm/400-tools/images/prisma-studio/08-model-sort.png differ diff --git a/docs/200-orm/400-tools/images/prisma-studio/array.svg b/docs/200-orm/400-tools/images/prisma-studio/array.svg new file mode 100644 index 0000000000..79e97b3ea7 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/array.svg @@ -0,0 +1,4 @@ + + + diff --git a/docs/200-orm/400-tools/images/prisma-studio/boolean.svg b/docs/200-orm/400-tools/images/prisma-studio/boolean.svg new file mode 100644 index 0000000000..786dee492e --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/boolean.svg @@ -0,0 +1,4 @@ + + + diff --git a/docs/200-orm/400-tools/images/prisma-studio/database.svg b/docs/200-orm/400-tools/images/prisma-studio/database.svg new file mode 100644 index 0000000000..01719ab8b8 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/database.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/docs/200-orm/400-tools/images/prisma-studio/datetime.svg b/docs/200-orm/400-tools/images/prisma-studio/datetime.svg new file mode 100644 index 0000000000..02ff3e3001 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/datetime.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/200-orm/400-tools/images/prisma-studio/enum.svg b/docs/200-orm/400-tools/images/prisma-studio/enum.svg new file mode 100644 index 0000000000..a77c3a6baf --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/enum.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/docs/200-orm/400-tools/images/prisma-studio/number.svg b/docs/200-orm/400-tools/images/prisma-studio/number.svg new file mode 100644 index 0000000000..63b2359278 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/number.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/docs/200-orm/400-tools/images/prisma-studio/object.svg b/docs/200-orm/400-tools/images/prisma-studio/object.svg new file mode 100644 index 0000000000..c356985d94 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/object.svg @@ -0,0 +1,5 @@ + + + + diff --git a/docs/200-orm/400-tools/images/prisma-studio/string.svg b/docs/200-orm/400-tools/images/prisma-studio/string.svg new file mode 100644 index 0000000000..69e8115317 --- /dev/null +++ b/docs/200-orm/400-tools/images/prisma-studio/string.svg @@ -0,0 +1,4 @@ + + + diff --git a/docs/200-orm/400-tools/index.mdx b/docs/200-orm/400-tools/index.mdx new file mode 100644 index 0000000000..28dc9f58de --- /dev/null +++ b/docs/200-orm/400-tools/index.mdx @@ -0,0 +1,11 @@ +--- +title: 'Tools' +metaTitle: 'Tools' +metaDescription: 'Learn about important Prisma tools.' +staticLink: true +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/500-reference/050-prisma-client-reference.mdx b/docs/200-orm/500-reference/050-prisma-client-reference.mdx new file mode 100644 index 0000000000..cd371c2c72 --- /dev/null +++ b/docs/200-orm/500-reference/050-prisma-client-reference.mdx @@ -0,0 +1,5577 @@ +--- +title: 'Prisma Client API reference' +navTitle: 'Prisma Client API' +metaTitle: 'Prisma Client API' +metaDescription: 'API reference documentation for Prisma Client.' +tocDepth: 3 +toc: true +--- + + + +The Prisma Client API reference documentation is based on the following schema: + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + profileViews Int @default(0) + role Role @default(USER) + coinflips Boolean[] + posts Post[] + city String + country String + profile ExtendedProfile? + pets Json +} + +model ExtendedProfile { + id Int @id @default(autoincrement()) + userId Int? @unique + bio String? + User User? @relation(fields: [userId], references: [id]) +} + +model Post { + id Int @id @default(autoincrement()) + title String + published Boolean @default(true) + author User @relation(fields: [authorId], references: [id]) + authorId Int + comments Json + views Int @default(0) + likes Int @default(0) +} + +enum Role { + USER + ADMIN +} +``` + +All example generated types (such as `UserSelect` and `UserWhereUniqueInput`) are based on the `User` model. + + + +## `PrismaClient` + +This section describes the `PrismaClient` constructor and its parameters. + +### Remarks + +- Parameters are validated at runtime. + +### `datasources` + +Programmatically overrides properties of the `datasource` block in the `schema.prisma` file - for example, as part of an integration test. See also: [Data sources](/orm/prisma-schema/overview/data-sources) + +From version 5.2.0 and upwards, you can also use the [`datasourceUrl`](#datasourceurl) property to programmatically override the database connection string. + +#### Properties + +| Example property | Example value | Description | +| ---------------- | ----------------------------- | -------------------------------------------------------------- | +| `db` | `{ url: 'file:./dev_qa.db' }` | The database [connection URL](/orm/reference/connection-urls). | + +#### Remarks + +- You must re-generate Prisma Client each time you add or rename a data source. Datasource names are included in the generated client. +- If you named your `datasource` block something else in the schema, replace `db` with the name of your `datasource` block. + +#### Examples + +##### Programmatically override a datasource `url` + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ + datasources: { + db: { + url: 'file:./dev_qa.db', + }, + }, +}) +``` + +Based on the following `datasource` block: + +```prisma +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} +``` + +### `datasourceUrl` + +Programmatically overrides the [`datasource`](#datasources) block in the `schema.prisma` file. + +#### Property + +| Option | Example value | Description | +| -------------------------- | -------------------- | -------------------------------------------------------------- | +| Database connection string | `'file:./dev_qa.db'` | The database [connection URL](/orm/reference/connection-urls). | + +#### Examples + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ + datasourceUrl: 'postgresql://johndoe:randompassword@localhost:5432/mydb', +}) +``` + +### `log` + +Determines the type and level of logging. See also: [Logging](/orm/prisma-client/observability-and-logging/logging) + +#### Options + +| Option | Example | Description | +| ------------------------ | ------------------------------------------------------------------------ | ----------- | +| Array of log levels | `[ "info", "query" ]` | | +| Array of log definitions | `[ { level: "info", emit: "event" }, { level: "warn", emit: "stdout" }]` | | + +##### Log levels + +| Name | Example | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `query` | Logs all queries run by Prisma.

For relational databases this logs all SQL queries. Example:
`prisma:query SELECT "public"."User"."id", "public"."User"."email" FROM "public"."User" WHERE ("public"."User"."id") IN (SELECT "t0"."id" FROM "public"."User" AS "t0" INNER JOIN "public"."Post" AS "j0" ON ("j0"."authorId") = ("t0"."id") WHERE ("j0"."views" > $1 AND "t0"."id" IS NOT NULL)) OFFSET $2`
For MongoDB this logs queries using the [`mongosh` shell](https://docs.mongodb.com/mongodb-shell/#mongodb-binary-bin.mongosh) format. Example:
`prisma:query db.User.deleteMany({ _id: ( $in: [ “6221ce49f756b0721fc00542”, ], }, })` | +| `info` | Example:
`prisma:info Started http server on http://127.0.0.1:58471` | +| `warn` | Warnings. | +| `error` | Errors. | + +##### Emit formats + +| Name | Description | +| -------- | ------------------------------------------------------------- | +| `stdout` | See: [stdout](https://en.wikipedia.org/wiki/Standard_streams) | +| `event` | Raises an event that you can subscribe to. | + +##### Event types + +The `query` event type: + +```ts file=index.d.ts +export type QueryEvent = { + timestamp: Date + query: string // Query sent to the database + params: string // Query parameters + duration: number // Time elapsed (in milliseconds) between client issuing query and database responding - not only time taken to run query + target: string +} +``` + +Note that for MongoDB, the `params` and `duration` fields will be undefined. + +All other log level event types: + +```ts file=index.d.ts +export type LogEvent = { + timestamp: Date + message: string + target: string +} +``` + +#### Examples + +##### Log `query` and `info` to `stdout` + + + + + +```ts highlight=3;normal; +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ log: ['query', 'info'] }) + +async function main() { + const countUsers = await prisma.user.count({}) +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + + + + + +```code no-copy +prisma:info Starting a postgresql pool with 13 connections. +prisma:info Started http server +prisma:query SELECT COUNT(*) FROM (SELECT "public"."User"."id" FROM "public"."User" WHERE 1=1 ORDER BY "public"."User"."coinflips" ASC OFFSET $1) AS "sub" +``` + + + + + +##### Log a `query` event to console + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ + log: [{ level: 'query', emit: 'event' }], +}) + +prisma.$on('query', (e) => { + console.log(e) +}) + +async function main() { + const countUsers = await prisma.user.count({}) +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + + + + + +```js no-copy +{ + timestamp: 2020-11-17T10:32:10.898Z, + query: 'SELECT COUNT(*) FROM (SELECT "public"."User"."id" FROM "public"."User" WHERE 1=1 OFFSET $1) AS "sub"', + params: '[0]', + duration: 5, + target: 'quaint::connector::metrics' +} +``` + + + + + +##### Log `info`, `warn`, and `error` events to console + + + + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ + log: [ + { level: 'warn', emit: 'event' }, + { level: 'info', emit: 'event' }, + { level: 'error', emit: 'event' }, + ], +}) + +prisma.$on('warn', (e) => { + console.log(e) +}) + +prisma.$on('info', (e) => { + console.log(e) +}) + +prisma.$on('error', (e) => { + console.log(e) +}) + +async function main() { + const countUsers = await prisma.user.count({}) +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + + + + +```js no-copy +{ + timestamp: 2020-11-17T10:33:24.592Z, + message: 'Starting a postgresql pool with 13 connections.', + target: 'quaint::pooled' +} +{ + timestamp: 2020-11-17T10:33:24.637Z, + message: 'Started http server', + target: 'query_engine::server' +} +``` + + + + +### `errorFormat` + +Determines the level and formatting of errors returned by Prisma. + +#### Error formats + +| Name | Description | +| --------------------- | ---------------------------------------------- | +| `undefined` | If it's not defined, the default is colorless. | +| `pretty` | Enables pretty error formatting. | +| `colorless` (default) | Enables colorless error formatting. | +| `minimal` | Enables minimal error formatting. | + +#### Examples + +##### No error formatting + +```ts +const prisma = new PrismaClient({ + // Defaults to colorless +}) +``` + +##### `pretty` error formatting + +```ts +const prisma = new PrismaClient({ + errorFormat: 'pretty', +}) +``` + +##### `colorless` error formatting + +```ts +const prisma = new PrismaClient({ + errorFormat: 'colorless', +}) +``` + +##### `minimal` error formatting + +```ts +const prisma = new PrismaClient({ + errorFormat: 'minimal', +}) +``` + +### `adapter` + +Defines an instance of a [driver adapter](/orm/overview/databases/database-drivers#driver-adapters). See also [Database drivers](/orm/overview/databases/database-drivers) . + + + +This is available from version 5.4.0 and newer behind the `driverAdapters` feature flag. + + + +#### Example + +The example below uses the [Neon driver adapter](/orm/overview/databases/neon#how-to-use-neons-serverless-driver-with-prisma-preview) + +```ts +import { Pool, neonConfig } from '@neondatabase/serverless' +import { PrismaNeon } from '@prisma/adapter-neon' +import { PrismaClient } from '@prisma/client' +import dotenv from 'dotenv' +import ws from 'ws' + +dotenv.config() +neonConfig.webSocketConstructor = ws +const connectionString = `${process.env.DATABASE_URL}` + +const pool = new Pool({ connectionString }) +const adapter = new PrismaNeon(pool) +const prisma = new PrismaClient({ adapter }) +``` + +### `rejectOnNotFound` + + + +**Note**: `rejectOnNotFound` was removed in v5.0.0. + +**Deprecated:** `rejectOnNotFound` is deprecated in v4.0.0. From v4.0.0, use the queries [`findUniqueOrThrow`](#finduniqueorthrow) or [`findFirstOrThrow`](#findfirstorthrow). + + + +Use the `rejectOnNotFound` parameter to configure `findUnique` and/or `findFirst` to throw an error if the record was not found. By default, both operations return `null` if the record is not found. + +#### Remarks + +- You can configure `rejectOnNotFound` on a per-request level for both [`findUnique`](#findunique) and [`findFirst`](#findfirst) + +#### Options + +| Option | Description | +| -------------------- | ------------------------------------------------------------------------------------------- | +| `RejectOnNotFound` | Enable globally (`true` / `false`) _or_ throw a custom error. | +| `RejectPerOperation` | Enable per operation (`true` / `false`) _or_ throw a custom error per operation, per model. | + +#### Examples + +##### Enable globally for `findUnique` and `findFirst` + +```ts +const prisma = new PrismaClient({ + rejectOnNotFound: true, +}) +``` + +##### Enable globally for a specific operation + +```ts +const prisma = new PrismaClient({ + rejectOnNotFound: { + findUnique: true, + }, +}) +``` + +##### Throw a custom error per model and operation if record is not found + +```ts +const prisma = new PrismaClient({ + rejectOnNotFound: { + findFirst: { + User: (err) => new Error('User error'), + Post: (err) => new Error('Post error!'), + }, + findUnique: { + User: (err) => new Error('User error'), + Post: (err) => new Error('Post error!'), + }, + }, +}) +``` + +## Model queries + +Use model queries to perform CRUD operations on your models. See also: [CRUD](/orm/prisma-client/queries/crud) + +### `findUnique()` + +`findUnique` query lets you retrieve a single database record: + +- By _ID_ +- By a _unique_ attribute + +`findUnique` replaced `findOne` in version [2.12.0](https://github.com/prisma/prisma/releases/tag/2.12.0). + +#### Remarks + +- Prisma's dataloader [automatically batches `findUnique` queries](/orm/prisma-client/queries/query-optimization-performance#solving-n1-in-graphql-with-findunique-and-prismas-dataloader) with the same `select` and `where` parameters. +- If you want the query to throw an error if the record is not found, then consider using [`findUniqueOrThrow`](#finduniqueorthrow) instead. +- You cannot use [filter conditions](#filter-conditions-and-operators) (e.g. `equals`, `contains`, `not`) to filter fields of the [JSON](/orm/reference/prisma-schema-reference#json) data type. Using filter conditions will likely result in a `null` response for that field. + +#### Options + +| Name | Example type (`User`) | Required | Description | +| ------------------------------- | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `where` | `UserWhereUniqueInput` | **Yes** | Wraps all _unique_ fields of a model so that individual records can be selected.

From version 4.5.0, this type wraps all fields of a model. [Learn more](#filter-on-non-unique-fields-with-userwhereuniqueinput) | +| `select` | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| `include` | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | +| `rejectOnNotFound` (deprecated) | `RejectOnNotFound` | No | If true, throw a `NotFoundError: No User found error`. You can also [configure `rejectOnNotFound` globally](#rejectonnotfound).

**Note:** `rejectOnNotFound`is deprecated in v4.0.0. From v4.0.0, use [`findUniqueOrThrow`](#finduniqueorthrow) instead. | + +#### Return type + +| Return type | Example | Description | +| ------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| JavaScript object (typed) | `User` | | +| JavaScript object (plain) | `{ title: "Hello world" }` | Use `select` and `include` to determine which fields to return. | +| `null` | `null` | Record not found | +| Error | | If `rejectOnNotFound` is true, `findUnique` throws an error (`NotFoundError` by default, [customizable globally](#rejectonnotfound)) instead of returning `null`. | + +#### Examples + +##### Get the `User` record with an `id` of `42` + +```ts +const result = await prisma.user.findUnique({ + where: { + id: 42, + }, +}) +``` + +##### Get the `User` record with an `email` of `alice@prisma.io` + +```ts +const result = await prisma.user.findUnique({ + where: { + email: 'alice@prisma.io', + }, +}) +``` + +##### Get the `User` record with `firstName` of `Alice` and `lastName` of `Smith` (`@@unique`) + +
+ +Expand for example User model with a @@unique block + +```prisma +model User { + firstName String + lastName String + + @@unique(fields: [firstName, lastName], name: "fullname") +} +``` + +
+ +```ts +const result = await prisma.user.findUnique({ + where: { + fullname: { + // name property of @@unique attribute - default is firstname_lastname + firstName: 'Alice', + lastName: 'Smith', + }, + }, +}) +``` + +##### Get the `User` record with `firstName` of `Alice` and `lastName` of `Smith` (`@@id`) + +
+ +Expand for example User model with an @@id block + +```prisma +model User { + firstName String + lastName String + + @@id([firstName, lastName]) +} +``` + +
+ +```ts +const result = await prisma.user.findUnique({ + where: { + firstName_lastName: { + firstName: 'Alice', + lastName: 'Smith', + }, + }, +}) +``` + +### `findUniqueOrThrow()` + + + +We introduced `findUniqueOrThrow` in v4.0.0. It replaces the [`rejectOnNotFound`](#rejectonnotfound) option. `rejectOnNotFound` is deprecated in v4.0.0. + + + +`findUniqueOrThrow` retrieves a single data record in the same way as [`findUnique`](#findunique). However, if the query does not find a record, it returns `NotFoundError: No User found error`. + +`findUniqueOrThrow` differs from `findUnique` as follows: + +- Its return type is non-nullable. For example, `post.findUnique()` can return `post` or `null`, but `post.findUniqueOrThrow()` always returns `post`. +- It is not compatible with sequential operations in the [`$transaction` API](/orm/prisma-client/queries/transactions#the-transaction-api). If the query returns `NotFoundError`, then the API will not roll back any operations in the array of calls. As a workaround, you can use interactive transactions with the `$transaction` API, as follows: + + ```ts + $transaction(async (prisma) => { + await prisma.model.create({ data: { ... }); + await prisma.model.findUniqueOrThrow(); + }) + ``` + +### `findFirst()` + +`findFirst` returns the first record in a list that matches your criteria. + +#### Remarks + +- If you want the query to throw an error if the record is not found, then consider using [`findFirstOrThrow`](#findfirstorthrow) instead. + +#### Options + +| Name | Example type (`User`) | Required | Description | +| ------------------------------- | ------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| `select` | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| `include` | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | +| `where` | `UserWhereInput` | No | Wraps _all_ model fields in a type so that the list can be filtered by any property. | +| `orderBy` | `XOR`OrderByInput>,UserOrderByInput>` | No | Lets you order the returned list by any property. | +| `cursor` | `UserWhereUniqueInput` | No | Specifies the position for the list (the value typically specifies an `id` or another unique value). | +| `take` | `number` | No | Specifies how many objects should be returned in the list. When used with `findFirst`, `take` is implicitly `1` or `-1`. `findFirst` is only affected by whether the value is positive or negative - any negative value reverses the list. | +| `skip` | `number` | No | Specifies how many of the returned objects in the list should be skipped. | +| `distinct` | `Enumerable`FieldEnum>` | No | Lets you filter out duplicate rows by a specific field - for example, return only distinct `Post` titles. | +| `rejectOnNotFound` (deprecated) | `RejectOnNotFound` | No | If true, throw a `NotFoundError: No User found error`. You can also [configure `rejectOnNotFound` globally](#rejectonnotfound).

**Note:** `rejectOnNotFound`is deprecated in v4.0.0. From v4.0.0, use [`findFirstOrThrow`](#findfirstorthrow) instead. | | + +#### Return type + +| Return type | Example | Description | +| ------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| JavaScript object (typed) | `User` | Specifies which properties to include on the returned object. | +| JavaScript object (plain) | `{ title: "Hello world" }` | Use `select` and `include` to determine which fields to return. | +| `null` | `null` | Record not found | +| Error | | If `rejectOnNotFound` is true, `findUnique` throws an error (`NotFoundError` by default, [customizable globally](#rejectonnotfound)) instead of returning `null`. | + +#### Remarks + +- `findFirst` calls `findMany` behind the scenes and accepts the same query options. +- Passing in a negative `take` value when you use a `findFirst` query reverses the order of the list. + +#### Examples + +See [Filter conditions and operators](#filter-conditions-and-operators) for examples of how to filter results. + +##### Get the first `User` record where the `name` is `Alice` + +```ts +const user = await prisma.user.findFirst({ + where: { name: 'Alice' }, +}) +``` + +##### Get the first `Post` record where the `title` starts with `A test`, reverse the list with `take` + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({}) + +async function main() { + const a = await prisma.post.create({ + data: { + title: 'A test 1', + }, + }) + + const b = await prisma.post.create({ + data: { + title: 'A test 2', + }, + }) + + const c = await prisma.post.findFirst({ + where: { + title: { + startsWith: 'A test', + }, + }, + orderBy: { + title: 'asc', + }, + take: -1, // Reverse the list + }) +} + +main() +``` + +### `findFirstOrThrow()` + + + +We introduced `findFirstOrThrow` in v4.0.0. It replaces the [`rejectOnNotFound`](#rejectonnotfound) option. `rejectOnNotFound` is deprecated in v4.0.0. + + + +`findFirstOrThrow` retrieves the first record in a list in the same way as [`findFirst`](#findfirst). However, if the query does not find a record, it returns `NotFoundError: No User found error`. + +`findFirstOrThrow` differs from `findFirst` as follows: + +- Its return type is non-nullable. For example, `post.findFirst()` can return `post` or `null`, but `post.findFirstOrThrow` always returns `post`. +- It is not compatible with sequential operations in the [`$transaction` API](/orm/prisma-client/queries/transactions#the-transaction-api). If the query returns `NotFoundError`, then the API will not roll back any operations in the array of calls. As a workaround, you can use interactive transactions with the `$transaction` API, as follows: + + ```ts + prisma.$transaction(async (tx) => { + await tx.model.create({ data: { ... }); + await tx.model.findFirstOrThrow(); + }) + ``` + +### `findMany()` + +`findMany` returns a list of records. + +#### Options + +| Name | Type | Required | Description | +| ---------------------- | ------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `select` | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| `include` | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | +| `where` | `UserWhereInput` | No | Wraps _all_ model fields in a type so that the list can be filtered by any property. | +| `orderBy` | `XOR`ByInput>, PostOrderByInput>` | No | Lets you order the returned list by any property. | +| `cursor` | `UserWhereUniqueInput` | No | Specifies the position for the list (the value typically specifies an `id` or another unique value). | +| `take` | `number` | No | Specifies how many objects should be returned in the list (as seen from the _beginning_ (positive value) or _end_ (negative value) **either** of the list **or** from the `cursor` position if mentioned) | +| `skip` | `number` | No | Specifies how many of the returned objects in the list should be skipped. | +| `distinct` | `Enumerable` | No | Lets you filter out duplicate rows by a specific field - for example, return only distinct `Post` titles. | + +#### Return type + +| Return type | Example | Description | +| ------------------------------- | ---------------------------- | --------------------------------------------------------------- | +| JavaScript array object (typed) | `User[]` | | +| JavaScript array object (plain) | `[{ title: "Hello world" }]` | Use `select` and `include` to determine which fields to return. | +| Empty array | `[]` | No matching records found. | + +#### Examples + +See [Filter conditions and operators](#filter-conditions-and-operators) for examples of how to filter results. + +##### Get all `User` records where the `name` is `Alice` + +```ts +const user = await prisma.user.findMany({ + where: { name: 'Alice' }, +}) +``` + +### `create()` + +`create` creates a new database record. + +#### Options + +| Name | Type | Required | Description | +| ---------------------- | -------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `XOR`UserUncheckedCreateInput>` | **Yes** | Wraps all the model fields in a type so that they can be provided when creating new records. It also includes relation fields which lets you perform (transactional) nested inserts. Fields that are marked as optional or have default values in the datamodel are optional. | +| [`select`](#select) | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| [`include`](#include) | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | +| | + +#### Return type + +| Return type | Example | Description | +| ------------------------- | ------------------------------ | --------------------------------------------------------------- | +| JavaScript object (typed) | `User` | | +| JavaScript object (plain) | `{ name: "Alice Wonderland" }` | Use `select` and `include` to determine which fields to return. | + +#### Remarks + +- You can also perform a nested [`create`](#create-1) - for example, add a `User` and two `Post` records at the same time. + +#### Examples + +##### Create a single new record with the only required field `email` + +```ts +const user = await prisma.user.create({ + data: { email: 'alice@prisma.io' }, +}) +``` + +##### Create multiple new records + +In most cases, you can carry out batch inserts with the [`createMany`](#createmany) query. However, [there are scenarios where `create` is the best option to insert multiple records](#remarks-10). + +The following example results in **two** `INSERT` statements: + + + + + +```ts +import { Prisma, PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient({ log: ['query'] }) + +async function main() { + let users: Prisma.UserCreateInput[] = [ + { + email: 'ariana@prisma.io', + name: 'Ari', + profileViews: 20, + coinflips: [true, false, false], + role: 'ADMIN', + }, + { + email: 'elsa@prisma.io', + name: 'Elsa', + profileViews: 20, + coinflips: [true, false, false], + role: 'ADMIN', + }, + ] + + await Promise.all( + users.map(async (user) => { + await prisma.user.create({ + data: user, + }) + }) + ) +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + + + + + +```sql no-copy +prisma:query BEGIN +prisma:query INSERT INTO "public"."User" ("name","email","profileViews","role","coinflips") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."User"."id" +prisma:query SELECT "public"."User"."id", "public"."User"."name", "public"."User"."email", "public"."User"."profileViews", "public"."User"."role", "public"."User"."coinflips" FROM "public"."User" WHERE "public"."User"."id" = $1 LIMIT $2 OFFSET $3 +prisma:query INSERT INTO "public"."User" ("name","email","profileViews","role","coinflips") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."User"."id" +prisma:query COMMIT +prisma:query SELECT "public"."User"."id", "public"."User"."name", "public"."User"."email", "public"."User"."profileViews", "public"."User"."role", "public"."User"."coinflips" FROM "public"."User" WHERE "public"."User"."id" = $1 LIMIT $2 OFFSET $3 +prisma:query COMMIT +``` + + + + + +### `update()` + +`update` updates an existing database record. + +#### Options + +| Name | Type | Required | Description | +| ---------------------- | ------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `XOR`UserUncheckedUpdateInput>` | **Yes** | Wraps all the fields of the model so that they can be provided when updating an existing record. Fields that are marked as optional or have default values in the datamodel are optional. | +| `where` | `UserWhereUniqueInput` | **Yes** | Wraps all _unique_ fields of a model so that individual records can be selected.

From version 4.5.0, this type wraps all fields of a model. [Learn more](#filter-on-non-unique-fields-with-userwhereuniqueinput) | +| [`select`](#select) | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| [`include`](#include) | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | + +#### Return type + +| Return type | Example | Description | +| -------------------------- | ------------------------------ | --------------------------------------------------------------- | +| JavaScript object (typed) | `User` | | +| JavaScript object (plain) | `{ name: "Alice Wonderland" }` | Use `select` and `include` to determine which fields to return. | +| `RecordNotFound` exception | | Exception is thrown if record does not exist. | + +#### Remarks + +- To perform arithmetic operations on update (add, subtract, multiply, divide), use [atomic updates](#atomic-number-operations) to prevent race conditions. +- You can also perform a nested [`update`](#update-1) - for example, update a user and that user's posts at the same time. + +#### Examples + +##### Update the `email` of the `User` record with `id` of `1` to `alice@prisma.io` + +```ts +const user = await prisma.user.update({ + where: { id: 1 }, + data: { email: 'alice@prisma.io' }, +}) +``` + +### `upsert()` + + + +This section covers the usage of the `upsert()` operation. To learn about using [nested upsert queries](#upsert-1) within `update()`, reference the linked documentation. + + + +`upsert` does the following: + +- If an existing database record satisfies the `where` condition, it updates that record +- If no database record satisfies the `where` condition, it creates a new database record + +#### Options + +| Name | Type | Required | Description | +| ---------------------- | ------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `create` | `XOR`UserUncheckedCreateInput>` | **Yes** | Wraps all the fields of the model so that they can be provided when creating new records. It also includes relation fields which lets you perform (transactional) nested inserts. Fields that are marked as optional or have default values in the datamodel are optional. | +| `update` | `XOR`UserUncheckedUpdateInput>` | **Yes** | Wraps all the fields of the model so that they can be provided when updating an existing record. Fields that are marked as optional or have default values in the datamodel are optional. | +| `where` | `UserWhereUniqueInput` | **Yes** | Wraps all _unique_ fields of a model so that individual records can be selected.

From version 4.5.0, this type wraps all fields of a model. [Learn more](#filter-on-non-unique-fields-with-userwhereuniqueinput) | +| [`select`](#select) | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| [`include`](#include) | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | + +#### Return type + +| Return type | Example | Description | +| ------------------------- | ------------------------------ | --------------------------------------------------------------- | +| JavaScript object (typed) | `User` | | +| JavaScript object (plain) | `{ name: "Alice Wonderland" }` | Use `select` and `include` to determine which fields to return. | + +#### Remarks + +- To perform arithmetic operations on update (add, subtract, multiply, divide), use [atomic updates](#atomic-number-operations) to prevent race conditions. +- If two or more upsert operations happen at the same time and the record doesn't already exist, then a race condition might happen. As a result, one or more of the upsert operations might throw a unique key constraint error. Your application code can catch this error and retry the operation. [Learn more](#unique-key-constraint-errors-on-upserts). +- From version 4.6.0, Prisma hands over upsert queries to the database where possible. [Learn more](#database-upserts). + +#### Examples + +##### Update (if exists) or create a new `User` record with an `email` of `alice@prisma.io` + +```ts +const user = await prisma.user.upsert({ + where: { id: 1 }, + update: { email: 'alice@prisma.io' }, + create: { email: 'alice@prisma.io' }, +}) +``` + +#### Unique key constraint errors on upserts + +##### Problem + +If multiple upsert operations happen at the same time and the record doesn't already exist, then one or more of the operations might return a [unique key constraint error](/orm/reference/error-reference#p2002). + +##### Cause + +When Prisma does an upsert, it first checks whether that record already exists in the database. To make this check, Prisma performs a read operation with the `where` clause from the upsert operation. This has two possible outcomes, as follows: + +- If the record does not exist, then Prisma creates that record. +- If the record exists, then Prisma updates it. + +When your application tries to perform two or more concurrent upsert operations, then a race condition might happen where two or more operations do not find the record and therefore try to create that record. In this situation, one of the operations successfully creates the new record but the other operations fail and return a unique key constraint error. + +##### Solution + +Handle the P2002 error in your application code. When it occurs, retry the upsert operation to update the row. + +#### Database upserts + +Where possible, Prisma Client hands over an `upsert` query to the database. This is called a _database upsert_. + +Database upserts have the following advantages: + +- They are faster than upserts handled by Prisma +- [Unique key constraint errors](#unique-key-constraint-errors-on-upserts) cannot happen + +Prisma Client uses a database upsert automatically when [specific criteria](#database-upsert-query-criteria) are met. When these criteria are not met, Prisma Client handles the `upsert`. + +To use a database upsert, Prisma Client sends the SQL construction [`INSERT ... ON CONFLICT SET .. WHERE`](https://www.prisma.io/dataguide/postgresql/inserting-and-modifying-data/insert-on-conflict) to the database. + +##### Database upsert prerequisites + +Prisma Client can use database upserts if your stack meets the following criteria: + +- You use Prisma version 4.6.0 or later +- Your application uses a CockroachDB, PostgreSQL, or SQLite data source + +##### Database upsert query criteria + +Prisma Client uses a database upsert for an `upsert` query when the query meets the following criteria: + +- There are no nested queries in the `upsert`'s `create` and `update` [options](#options-7) +- The query does _not_ include a selection that uses a [nested read](/orm/prisma-client/queries/relation-queries#nested-reads) +- The query modifies only one model +- There is only one unique field in the `upsert`'s `where` option +- The unique field in the `where` option and the unique field in the `create` option have the same value + +If your query does not meet these criteria, then Prisma Client handles the upsert itself. + +##### Database upsert examples + +The following examples use this schema: + +```prisma +model User { + id Int @id + profileViews Int + userName String @unique + email String + + @@unique([id, profileViews]) +} +``` + +The following `upsert` query meets all of the criteria, so Prisma Client uses a database upsert. + +```ts +prisma.user.upsert({ + where: { + userName: 'Alice', + }, + create: { + id: 1, + profileViews: 1, + userName: 'Alice', + email: 'alice@prisma.io', + }, + update: { + email: 'updated@example.com', + }, +}) +``` + +In this situation, Prisma uses the following SQL query: + +```sql +INSERT INTO "public"."User" ("id","profileViews","userName","email") VALUES ($1,$2,$3,$4) +ON CONFLICT ("userName") DO UPDATE +SET "email" = $5 WHERE ("public"."User"."userName" = $6 AND 1=1) RETURNING "public"."User"."id", "public"."User"."profileViews", "public"."User"."userName", "public"."User"."email" +``` + +The following query has multiple unique values in the `where` clause, so Prisma Client does _not_ use a database upsert: + +```ts +prisma.User.upsert({ + where: { + userName: 'Alice', + profileViews: 1, + id: 1, + }, + create: { + id: 1, + profileViews: 1, + userName: 'Alice', + email: 'alice@prisma.io', + }, + update: { + email: 'updated@example.com', + }, +}) +``` + +In the following query, the values for `userName` in the `where` and `create` options are different, so Prisma Client does _not_ use a database upsert. + +```ts +prisma.User.upsert({ + where: { + userName: 'Alice', + }, + create: { + id: 1, + profileViews: 1, + userName: 'AliceS', + email: 'alice@prisma.io', + }, + update: { + email: 'updated@example.com', + }, +}) +``` + +In the following query, the selection on the `title` field in `posts` is a nested read, so Prisma Client does _not_ use a database upsert. + +```ts +prisma.user.upsert({ + select: { + email: true, + id: true, + posts: { + select: { + title: true, + }, + }, + }, + where: { + userName: 'Alice', + }, + + create: { + id: 1, + profileViews: 1, + userName: 'Alice', + email: 'alice@prisma.io', + }, + update: { + email: 'updated@example.com', + }, +}) +``` + +### `delete()` + +`delete` deletes an existing database record. You can delete a record: + +- By _ID_ +- By a _unique_ attribute + +To delete records that match a certain criteria, use [`deleteMany`](#deletemany) with a filter. + +#### Options + +| Name | Type | Required | Description | +| ---------------------- | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `where` | `UserWhereUniqueInput` | **Yes** | Wraps all _unique_ fields of a model so that individual records can be selected.

From version 4.5.0, this type wraps all fields of a model. [Learn more](#filter-on-non-unique-fields-with-userwhereuniqueinput) | +| [`select`](#select) | `XOR` | No | [Specifies which properties to include](/orm/prisma-client/queries/select-fields) on the returned object. | +| [`include`](#include) | `XOR` | No | [Specifies which relations should be eagerly loaded](/orm/prisma-client/queries/relation-queries) on the returned object. | +| `relationLoadStrategy` | `'join'` or `'query'` | No | **Default: `join`**. Specifies the [load strategy](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview) for a relation query. Only available in combination with `include` (or `select` on a relation field). In [Preview](/orm/more/releases#preview) since 5.9.0. | + +#### Return type + +| Return type | Example | Description | +| -------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------- | +| JavaScript object (typed) | `User` | The `User` record that was deleted. | +| JavaScript object (plain) | `{ name: "Alice Wonderland" }` | Data from the `User` record that was deleted. Use `select` and `include` to determine which fields to return. | +| `RecordNotFound` exception | | Throws an exception if record does not exist. | + +#### Remarks + +- To delete multiple records based on some criteria (for example, all `User` records with a `prisma.io` email address, use `deleteMany`) + +#### Examples + +##### Delete the `User` record with an `id` of `1` + +```ts +const user = await prisma.user.delete({ + where: { id: 1 }, +}) +``` + +##### Delete the `User` record where `email` equals `else@prisma.io` + +The following query deletes a specific user record and uses `select` to return the `name` and `email` of the deleted user: + + + + + +```ts +const deleteUser = await prisma.user.delete({ + where: { + email: 'elsa@prisma.io', + }, + select: { + email: true, + name: true, + }, +}) +``` + + + + + +```json no-copy +{ "email": "elsa@prisma.io", "name": "Elsa" } +``` + + + + + +### `createMany()` + +`createMany` creates multiple records in a transaction. + +#### Options + +| Name | Type | Required | Description | +| ----------------- | --------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `Enumerable` | **Yes** | Wraps all the model fields in a type so that they can be provided when creating new records. Fields that are marked as optional or have default values in the datamodel are optional. | +| `skipDuplicates?` | `boolean` | No | Do not insert records with unique fields or ID fields that already exist. Only supported by databases that support [`ON CONFLICT DO NOTHING`](https://www.postgresql.org/docs/9.5/sql-insert.html#SQL-ON-CONFLICT). This excludes MongoDB and SQLServer | + +#### Return type + +| Return type | Example | Description | +| -------------- | -------------- | ----------------------------------------- | +| `BatchPayload` | `{ count: 3 }` | A count of the number of records created. | + +#### Remarks + +- `createMany` is not supported by SQLite. +- The `skipDuplicates` option is not supported by MongoDB and SQLServer. +- You **cannot** create or connect relations - you cannot nest `create`, `createMany`, `connect`, `connectOrCreate` inside a top-level `createMany` +- You can nest a [`createMany`](#createmany-1) inside an `update` or `create` query - for example, add a `User` and two `Post` records at the same time. + +#### Examples + +##### Create several new users + +```ts +const users = await prisma.user.createMany({ + data: [ + { name: 'Sonali', email: 'sonali@prisma.io' }, + { name: 'Alex', email: 'alex@prisma.io' }, + ], +}) +``` + +### `updateMany()` + +`updateMany` updates a batch of existing database records in bulk and returns the number of updated records. + +#### Options + +| Name | Type | Required | Description | +| ------- | ----------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `data` | `XOR`UserUncheckedUpdateManyInput>` | **Yes** | Wraps all the fields of the model so that they can be provided when updating an existing record. Fields that are marked as optional or have default values in the datamodel are optional on `data`. | +| `where` | `UserWhereInput` | No | Wraps _all_ fields of a model so that the list can be filtered by any property. If you do not filter the list, all records will be updated. | + +#### Return type + +| Return type | Example | Description | +| -------------- | -------------- | ----------------------------- | +| `BatchPayload` | `{ count: 4 }` | The count of updated records. | + +```ts +export type BatchPayload = { + count: number +} +``` + +#### Examples + +##### Update all `User` records where the `name` is `Alice` to `ALICE` + +```ts +const updatedUserCount = await prisma.user.updateMany({ + where: { name: 'Alice' }, + data: { name: 'ALICE' }, +}) +``` + +##### Update all `User` records where the `email` contains `prisma.io` and at least one related `Post` has more than 10 likes + +```ts +const deleteUser = await prisma.user.updateMany({ + where: { + email: { + contains: 'prisma.io', + }, + posts: { + some: { + likes: { + gt: 10, + }, + }, + }, + }, + data: { + role: 'USER', + }, +}) +``` + +### `deleteMany()` + +`deleteMany` deletes multiple records in a transaction. + +#### Options + +| Name | Type | Required | Description | +| ------- | ---------------- | -------- | ---------------------------------------------------------------------------- | +| `where` | `UserWhereInput` | No | Wraps _all_ fields of a model so that the list can be filtered by any field. | + +#### Return type + +| Return type | Example | Description | +| -------------- | -------------- | ----------------------------- | +| `BatchPayload` | `{ count: 4 }` | The count of deleted records. | + +```ts +export type BatchPayload = { + count: number +} +``` + +#### Examples + +##### Delete all `User` records + +```ts +const deletedUserCount = await prisma.user.deleteMany({}) +``` + +##### Delete all `User` records where the `name` is `Alice` + +```ts +const deletedUserCount = await prisma.user.deleteMany({ + where: { name: 'Alice' }, +}) +``` + +See [Filter conditions and operators](#filter-conditions-and-operators) for examples of how to filter the records to delete. + +### `count()` + +#### Options + +| Name | Type | Required | Description | +| --------- | ------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `where` | `UserWhereInput` | No | Wraps _all_ model fields in a type so that the list can be filtered by any property. | +| `orderBy` | `XOR`ByInput>, PostOrderByInput>` | No | Lets you order the returned list by any property. | +| `cursor` | `UserWhereUniqueInput` | No | Specifies the position for the list (the value typically specifies an `id` or another unique value). | +| `take` | `number` | No | Specifies how many objects should be returned in the list (as seen from the _beginning_ (positive value) or _end_ (negative value) **either** of the list **or** from the `cursor` position if mentioned) | +| `skip` | `number` | No | Specifies how many of the returned objects in the list should be skipped. | + +#### Return type + +| Return type | Example | Description | +| ------------------------------ | ------------------------ | ----------------------------- | +| `number` | `29` | The count of records. | +| `UserCountAggregateOutputType` | `{ _all: 27, name: 10 }` | Returned if `select` is used. | + +#### Examples + +##### Count all `User` records + +```ts +const result = await prisma.user.count() +``` + +##### Count all `User` records with at least one published `Post` + +```ts +const result = await prisma.user.count({ + where: { + post: { + some: { + published: true, + }, + }, + }, +}) +``` + +##### Use `select` to perform three separate counts + +The following query returns: + +- A count of all records (`_all`) +- A count of all records with non-`null` `name` fields +- A count of all records with non-`null` `city` fields + +```ts +const c = await prisma.user.count({ + select: { + _all: true, + city: true, + name: true, + }, +}) +``` + +### `aggregate()` + +See also: [Aggregation, grouping, and summarizing](/orm/prisma-client/queries/aggregation-grouping-summarizing#aggregate) + +#### Options + +| Name | Type | Required | Description | +| --------- | ------------------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `where` | `UserWhereInput` | No | Wraps _all_ model fields in a type so that the list can be filtered by any property. | +| `orderBy` | `XOR,`
`UserOrderByInput>` | No | Lets you order the returned list by any property. | +| `cursor` | `UserWhereUniqueInput` | No | Specifies the position for the list (the value typically specifies an `id` or another unique value). | +| `take` | `number` | No | Specifies how many objects should be returned in the list (as seen from the _beginning_ (positive value) or _end_ (negative value) **either** of the list **or** from the `cursor` position if mentioned) | +| `skip` | `number` | No | Specifies how many of the returned objects in the list should be skipped. | +| `_count` | `true` | No | Returns a count of matching records or non-`null` fields. | +| `_avg` | `UserAvgAggregateInputType` | No | Returns an average of all values of the specified field. | +| `_sum` | `UserSumAggregateInputType` | No | Returns the sum of all values of the specified field. | +| `_min` | `UserMinAggregateInputType` | No | Returns the smallest available value of the specified field. | +| `_max` | `UserMaxAggregateInputType` | No | Returns the largest available value of the specified field. | + +#### Examples + +##### Return `_min`, `_max`, and `_count` of `profileViews` of all `User` records + + + + +```ts +const minMaxAge = await prisma.user.aggregate({ + _count: { + _all: true, + }, + _max: { + profileViews: true, + }, + _min: { + profileViews: true, + }, +}) +``` + + + + +```js no-copy +{ + _count: { _all: 29 }, + _max: { profileViews: 90 }, + _min: { profileViews: 0 } +} +``` + + + + +##### Return `_sum` of all `profileViews` for all `User` records + + + + +```ts +const setValue = await prisma.user.aggregate({ + _sum: { + profileViews: true, + }, +}) +``` + + + + +```js no-copy +{ + "_sum": { + "profileViews": 9493 + } +} +``` + + + + +### `groupBy()` + +See also: [Aggregation, grouping, and summarizing](/orm/prisma-client/queries/aggregation-grouping-summarizing#group-by) + +#### Options + +| Name | Type | Required | Description | +| --------- | ------------------------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `where` | `UserWhereInput` | No | Wraps _all_ model fields in a type so that the list can be filtered by any property. | +| `orderBy` | `XOR,`
`UserOrderByInput>` | No | Lets you order the returned list by any property that is also present in `by`. | +| `by` | `Array` \| `string` | No | Specifies the field or combination of fields to group records by. | +| `having` | `UserScalarWhereWithAggregatesInput` | No | Allows you to filter groups by an aggregate value - for example, only return groups _having_ an average age less than 50. | +| `take` | `number` | No | Specifies how many objects should be returned in the list (as seen from the _beginning_ (positive value) or _end_ (negative value) **either** of the list **or** from the `cursor` position if mentioned) | +| `skip` | `number` | No | Specifies how many of the returned objects in the list should be skipped. | +| `_count` | `true` \| `UserCountAggregateInputType` | No | Returns a count of matching records or non-`null` fields. | +| `_avg` | `UserAvgAggregateInputType` | No | Returns an average of all values of the specified field. | +| `_sum` | `UserSumAggregateInputType` | No | Returns the sum of all values of the specified field. | +| `_min` | `UserMinAggregateInputType` | No | Returns the smallest available value of the specified field. | +| `_max` | `UserMaxAggregateInputType` | No | Returns the largest available value of the specified field. | + +#### Examples + +##### Group by `country`/`city` where the average `profileViews` is greater than `200`, and return the `_sum` of `profileViews` for each group + +The query also returns a count of `_all` records in each group, and all records with non-`null` `city` field values in each group. + + + + +```ts +const groupUsers = await prisma.user.groupBy({ + by: ['country', 'city'], + _count: { + _all: true, + city: true, + }, + _sum: { + profileViews: true, + }, + orderBy: { + country: 'desc', + }, + having: { + profileViews: { + _avg: { + gt: 200, + }, + }, + }, +}) +``` + + + + +```js no-copy +;[ + { + country: 'Denmark', + city: 'Copenhagen', + _sum: { profileViews: 490 }, + _count: { + _all: 70, + city: 8, + }, + }, + { + country: 'Sweden', + city: 'Stockholm', + _sum: { profileViews: 500 }, + _count: { + _all: 50, + city: 3, + }, + }, +] +``` + + + + +## Model query options + +### `select` + +`select` defines which fields are included in the object that Prisma Client returns. See: [Select fields and include relations](/orm/prisma-client/queries/select-fields) . + +#### Remarks + +- You cannot combine `select` and `include` on the same level. +- In [3.0.1](https://github.com/prisma/prisma/releases/3.0.1) and later, you can [select a `_count` of relations](#select-a-_count-of-relations). + +#### Examples + +##### Select the `name` and `profileViews` fields of a single `User` record + + + + +```ts +const result = await prisma.user.findUnique({ + where: { id: 1 }, + select: { + name: true, + profileViews: true, + }, +}) +``` + + + + +```js no-copy +{ + name: "Alice", + profileViews: 0 +} +``` + + + + +##### Select the `email` and `role` fields of a multiple `User` records + + + + +```ts +const result = await prisma.user.findMany({ + select: { + email: true, + role: true, + }, +}) +``` + + + + +```js no-copy +;[ + { + email: 'alice@prisma.io', + role: 'ADMIN', + }, + { + email: 'bob@prisma.io', + role: 'USER', + }, +] +``` + + + + +##### Select a `_count` of relations + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + select: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```js no-copy +{ + _count: { + posts: 3 + } +} +``` + + + + +##### Select the 'id' and 'title' fields of related `Post` records + + + + +```ts +const result = await prisma.user.findMany({ + select: { + id: true, + name: true, + posts: { + select: { + id: true, + title: true, + }, + }, + }, +}) +``` + + + + +```ts no-copy +;[ + { + id: 1, + name: 'Alice', + posts: [ + { id: 1, title: 'Hello World' }, + { id: 2, title: 'Bye bye' }, + ], + }, + { + id: 2, + name: 'Bob', + posts: [], + }, +] +``` + + + + +##### `include` inside `select` + + + + +```ts +const result = await prisma.user.findMany({ + select: { + id: true, + name: true, + posts: { + include: { + author: true, + }, + }, + }, +}) +``` + + + + +```js no-copy +;[ + { + id: 1, + name: 'Alice', + posts: [ + { + id: 1, + title: 'Hello World', + published: true, + author: { + id: 1, + name: 'Alice', + email: 'alice@prisma.io', + role: 'ADMIN', + coinflips: [true, false], + profileViews: 0, + }, + }, + { + id: 2, + title: 'Bye bye', + published: false, + author: { + id: 1, + name: 'Alice', + email: 'alice@prisma.io', + role: 'USER', + coinflips: [], + profileViews: 0, + }, + }, + ], + }, +] +``` + + + + +#### Generated types for `select` + +The following examples demonstrate how to use the [`validator`](/orm/prisma-client/type-safety/prisma-validator) with `select`: + +- `SelectAndInclude` +- `UserSelect` + +```ts +// SelectAndInclude +const selectNameIncludeEmail = Prisma.validator()({ + select: { + name: true, + }, + include: { + email: true, + }, +}) + +// UserSelect +const selectNameEmailNotPosts = Prisma.validator()({ + name: true, + email: true, + posts: false, +}) +``` + +### `include` + +`include` defines which relations are included in the result that Prisma Client returns. See: [Select fields and include relations](/orm/prisma-client/queries/select-fields) . + +#### Remarks + +- In [3.0.1](https://github.com/prisma/prisma/releases/3.0.1) and later, you can [`include` a `_count` of relations](#include-a-_count-of-relations) + +#### Examples + +##### Include the `posts` and `profile` relation when loading `User` records + +```ts +const users = await prisma.user.findMany({ + include: { + posts: true, // Returns all fields for all posts + profile: true, // Returns all Profile fields + }, +}) +``` + +##### Include the `posts` relation on the returned objects when creating a new `User` record with two `Post` records + +```ts +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + posts: { + create: [ + { title: 'This is my first post' }, + { title: 'Here comes a second post' }, + ], + }, + }, + include: { posts: true }, // Returns all fields for all posts +}) +``` + +#### Generated types for `include` + +The following examples demonstrate how to use the [`validator`](/orm/prisma-client/type-safety/prisma-validator) with `include`: + +- `SelectAndInclude` +- `UserInclude` + +```ts +// SelectAndInclude +const selectNameIncludeEmail = Prisma.validator()({ + select: { + name: true, + }, + include: { + email: true, + }, +}) + +// UserInclude +const includePosts = Prisma.validator()({ + posts: true, +}) +``` + +##### Include a `_count` of relations + + + + +```ts +const usersWithCount = await prisma.user.findMany({ + include: { + _count: { + select: { posts: true }, + }, + }, +}) +``` + + + + +```js no-copy +{ id: 1, name: "Bob", email: "bob@prisma.io", _count: { posts: 3 } }, +{ id: 2, name: "Enya", email: "enya@prisma.io", _count: { posts: 2 } } +``` + + + + +### `relationLoadStrategy` (Preview) + +`relationLoadStrategy` specifies how a relation should be loaded from the database. It has two possible values: + +- `join` (default): Uses a database-level `LATERAL JOIN` and fetches all data with a single query to the database. +- `query`: Sends multiple queries to the database (one per table) and joins them on the application level. + +> **Note**: Once `relationLoadStrategy` moves from [Preview](/orm/more/releases#preview) into [General Availability](/orm/more/releases/#generally-available-ga), `join` will universally become the default for all relation queries. + +You can learn more about join strategies [here](/orm/prisma-client/queries/relation-queries#relation-load-strategies-preview). + +Because the `relationLoadStrategy` option is currently in Preview, you need to enable it via the `relationJoins` preview feature flag in your Prisma schema file: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["relationJoins"] +} +``` + +After adding this flag, you need to run `prisma generate` again to re-generate Prisma Client. Also note that this feature is currently only available on PostgreSQL and CockroachDB, support for other DBs is coming soon. + +#### Remarks + +- In most situations, the default `join` strategy will be more effective. Use `query` if you want to save resources on your database server or if you profiling shows that the application-level join is more performant. +- You can only specify the `relationLoadStrategy` on the top-level in your query. The top-level choice will affect all nested sub-queries. + +#### Examples + +##### Load the `posts` relation via a database-level JOIN when using `include` + +```ts +const users = await prisma.user.findMany({ + relationLoadStrategy: 'join', + include: { + posts: true, + }, +}) +``` + +##### Load the `posts` relation via a database-level JOIN when using `select` + +```ts +const users = await prisma.user.findMany({ + relationLoadStrategy: 'join', + select: { + posts: true, + }, +}) +``` + +### `where` + +`where` defines one or more [filters](#filter-conditions-and-operators), and can be used to filter on record properties (like a user's email address) or related record properties (like a user's top 10 most recent post titles). + +#### Examples + +```ts +const results = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + }, + }, +}) +``` + +#### Generated types for `where` + +The following examples demonstrate how to use the [`validator`](/orm/prisma-client/type-safety/prisma-validator) with `where`: + +- `UserWhereInput` + + ```ts + // UserWhereInput + const whereNameIs = Prisma.validator()({ + name: 'Rich', + }) + + // It can be combined with conditional operators too + const whereNameIs = Prisma.validator()({ + name: 'Rich', + AND: [ + { + email: { + contains: 'rich@boop.com', + }, + }, + ], + }) + ``` + +- `UserWhereUniqueInput` This type works by exposing any unique fields on the model. A field assigned `@id` is considered unique, + as is one assigned `@unique`. + + From version 4.5.0, this type exposes all fields on the model. This means that when you filter for a single record based on a unique field, you can check additional non-unique and unique fields at the same time. [Learn more](#filter-on-non-unique-fields-with-userwhereuniqueinput). + +```ts +// UserWhereUniqueInput +const whereEmailIsUnique = Prisma.validator()({ + email: 'rich@boop.com', +}) +``` + +- `PostScalarWhereInput` + + ```ts + const whereScalarTitleIs = Prisma.validator()({ + title: 'boop', + }) + ``` + +- `PostUpdateWithWhereUniqueWithoutAuthorInput` - This type accepts a unique `where` field (an `@id` or another assigned `@unique`) + and updates any field on the `Post` model except the `Author`. The `Author` is the scalar field on the `Post` model. + + ```ts + const updatePostByIdWithoutAuthor = + Prisma.validator()({ + where: { + id: 1, + }, + data: { + content: 'This is some updated content', + published: true, + title: 'This is a new title', + }, + }) + ``` + +- `PostUpsertWithWhereUniqueWithoutAuthorInput` - This type will update the `Post` records title field where the id matches, if it doesn't exist it will create it instead. + + ```ts + const updatePostTitleOrCreateIfNotExist = + Prisma.validator()({ + where: { + id: 1, + }, + update: { + title: 'This is a new title', + }, + create: { + id: 1, + title: 'If the title doesnt exist, then create one with this text', + }, + }) + ``` + +- `PostUpdateManyWithWhereWithoutAuthorInput` - This type will update all `Post` records where published is set to false. + + ```ts + const publishAllPosts = + Prisma.validator()({ + where: { + published: { + equals: false, + }, + }, + data: { + published: true, + }, + }) + ``` + +### `orderBy` + +Sorts a list of records. See also: [Sorting](/orm/prisma-client/queries/filtering-and-sorting) + +#### Remarks + +- In [2.16.0](https://github.com/prisma/prisma/releases/2.16.0) and later, you can [order by relation fields](#sort-post-by-the-related-user-records-name) - for example, order posts by the author's name. + +- In [3.5.0](https://github.com/prisma/prisma/releases/3.5.0) and later, in PostgreSQL you can [order by relevance](#sort-post-by-relevance-of-the-title). For details, see [Sort by relevance](/orm/prisma-client/queries/filtering-and-sorting#sort-by-relevance-postgresql). + +- In [4.1.0](https://github.com/prisma/prisma/releases/4.1.0) and later, you can [sort `null` records first or last](#sort-post-by-the-related-user-records-name-with-null-records-first). For details, see [Sort with nulls first or last](/orm/prisma-client/queries/filtering-and-sorting#sort-with-null-records-first-or-last). + +#### Inputs for `sort` argument + +| Name | Description | +| ------ | ---------------------------- | +| `asc` | Sort ascending (A → Z) | +| `desc` | Sort descending (Z → A) | + +#### Inputs for `nulls` argument + +Note: + +- This argument is optional. +- It is for use on optional [scalar](/orm/prisma-schema/data-model/models#scalar-fields) fields only. If you try to sort by nulls on a required or [relation](/orm/prisma-schema/data-model/models#relation-fields) field, Prisma Client throws a [P2009 error](/orm/reference/error-reference#p2009). +- It is available in version 4.1.0 and later, as a preview feature. See [sort with nulls first or last](/orm/prisma-client/queries/filtering-and-sorting#sort-with-null-records-first-or-last) for details of how to enable the feature. + +| Name | Description | +| ------- | ------------------------------ | +| `first` | Sort with `null` values first. | +| `last` | Sort with `null` values last. | + +#### Examples + +##### Sort `User` by `email` field + +The following example returns all `User` records sorted by `email` ascending: + +```ts +const users = await prisma.user.findMany({ + orderBy: { + email: 'asc', + }, +}) +``` + +The following example returns all `User` records sorted by `email` descending: + +```ts +const users = await prisma.user.findMany({ + orderBy: { + email: 'desc', + }, +}) +``` + +#### Sort `Post` by the related `User` record's `name` + +The following query orders posts by user name: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + author: { + name: 'asc', + }, + }, +}) +``` + +#### Sort `Post` by the related `User` record's `name`, with `null` records first + +The following query orders posts by user name, with `null` records first: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + author: { + name: { sort: 'asc', nulls: 'first' }, + }, + }, +}) +``` + +#### Sort `Post` by relevance of the title + + + +This feature is available from version 3.5.0 onwards in PostgreSQL and MySQL only. You'll need to use the `fullTextSearch` preview flag to enable this feature. + + + +The following query orders posts by relevance of the search term `'database'` to the title: + +```ts +const posts = await prisma.post.findMany({ + orderBy: { + _relevance: { + fields: ['title'], + search: 'database', + sort: 'asc' + }, +}) +``` + +#### Sort `User` by the `posts` count + +The following query orders users by post count: + +```ts +const getActiveusers = await prisma.user.findMany({ + orderBy: { + posts: { + count: 'desc', + }, + }, +}) +``` + +##### Sort `User` by multiple fields - `email` _and_ `role` + +The following example sorts users by two fields - first `email`, then `role`: + + + + + +```ts +const users = await prisma.user.findMany({ + select: { + email: true, + role: true, + }, + orderBy: [ + { + email: 'desc', + }, + { + role: 'desc', + }, + ], +}) +``` + + + + + +```json no-copy +[ + { + "email": "yuki@prisma.io", + "role": "USER" + }, + { + "email": "nora@prisma.io", + "role": "USER" + }, + { + "email": "mary@prisma.io", + "role": "MODERATOR" + }, + { + "email": "elsa@prisma.io", + "role": "MODERATOR" + }, + { + "email": "eloise@prisma.io", + "role": "USER" + }, + { + "email": "coco@prisma.io", + "role": "ADMIN" + }, + { + "email": "anna@prisma.io", + "role": "USER" + }, + { + "email": "alice@prisma.io", + "role": "USER" + } +] +``` + + + + + +The order of sorting parameters matters - the following query sorts by `role`, then `email`. Not the difference in the results: + + + + + +```ts +const users = await prisma.user.findMany({ + select: { + email: true, + role: true, + }, + orderBy: [ + { + role: 'desc', + }, + { + email: 'desc', + }, + ], +}) +``` + + + + + +```json no-copy +[ + { + "email": "mary@prisma.io", + "role": "MODERATOR" + }, + { + "email": "elsa@prisma.io", + "role": "MODERATOR" + }, + { + "email": "yuki@prisma.io", + "role": "USER" + }, + { + "email": "nora@prisma.io", + "role": "USER" + }, + { + "email": "eloise@prisma.io", + "role": "USER" + }, + { + "email": "anna@prisma.io", + "role": "USER" + }, + { + "email": "alice@prisma.io", + "role": "USER" + }, + { + "email": "coco@prisma.io", + "role": "ADMIN" + } +] +``` + + + + + +##### Sort `User` by `email`, select `name` and `email` + +The following example returns all the `name` and `email` fields of all `User` records, sorted by `email`: + + + + + +```ts +const users3 = await prisma.user.findMany({ + orderBy: { + email: 'asc', + }, + select: { + name: true, + email: true, + }, +}) +``` + + + + + +```js no-copy +;[ + { + name: 'Alice', + email: 'alice@prisma.io', + }, + { + name: 'Ariadne', + email: 'ariadne@prisma.io', + }, + { + name: 'Bob', + email: 'bob@prisma.io', + }, +] +``` + + + + + +##### Sort `User` records by `email` and sort nested `Post` records by `title` + +The following example: + +- Returns all `User` records sorted by `email` +- For each `User` record, returns the `title` field of all nested `Post` records sorted by `title` + + + + + +```ts +const usersWithPosts = await prisma.user.findMany({ + orderBy: { + email: 'asc', + }, + include: { + posts: { + select: { + title: true, + }, + orderBy: { + title: 'asc', + }, + }, + }, +}) +``` + + + + + +```json no-copy +[ + { + "id": 2, + "email": "alice@prisma.io", + "name": "Alice", + "posts": [ + { + "title": "Watch the talks from Prisma Day 2019" + } + ] + }, + { + "id": 3, + "email": "ariadne@prisma.io", + "name": "Ariadne", + "posts": [ + { + "title": "How to connect to a SQLite database" + }, + { + "title": "My first day at Prisma" + } + ] + }, + { + "id": 1, + "email": "bob@prisma.io", + "name": "Bob", + "posts": [ + { + "title": "Follow Prisma on Twitter" + }, + { + "title": "Subscribe to GraphQL Weekly for community news " + } + ] + } +] +``` + + + + + +##### Sort one user's nested list of `Post` records + +The following example retrieves a single `User` record by ID, as well as a list of nested `Post` records sorted by `title`: + + + + + +```ts +const userWithPosts = await prisma.user.findUnique({ + where: { + id: 1, + }, + include: { + posts: { + orderBy: { + title: 'desc', + }, + select: { + title: true, + published: true, + }, + }, + }, +}) +``` + + + + + +```json no-copy +{ + "email": "sarah@prisma.io", + "id": 1, + "name": "Sarah", + "extendedProfile": null, + "role": "USER", + "posts": [ + { + "title": "Prisma Day 2020", + "published": false + }, + { + "title": "My first post", + "published": false + }, + { + "title": "All about databases", + "published": true + } + ] +} +``` + + + + + +##### Sort by `enum` + +The following sorts all `User` records by `role` (an `enum`): + + + + + +```ts +const sort = await prisma.user.findMany({ + orderBy: { + role: 'desc', + }, + select: { + email: true, + role: true, + }, +}) +``` + + + + + +```json no-copy +[ + { + "email": "emma@prisma.io", + + "role": "USER" + }, + { + "email": "suma@prisma.io", + "role": "ADMIN" + }, + { + "email": "kwame@prisma.io", + "role": "ADMIN" + }, + { + "email": "pearl@prisma.io", + "role": "ADMIN" + } +] +``` + + + + + +#### Generated types for `orderBy` + +The following examples demonstrate how to use the [`validator`](/orm/prisma-client/type-safety/prisma-validator) with `orderBy`: + +- `UserOrderByInput` + ```ts + const orderEmailsByDescending = Prisma.validator()({ + email: 'desc', + }) + ``` + +### `distinct` + +Deduplicate a list of records from [`findMany`](#findmany) or [`findFirst`](#findfirst). See also: [Aggregation, grouping, and summarizing](/orm/prisma-client/queries/aggregation-grouping-summarizing#select-distinct) + +#### Examples + +##### Select distinct on a single field + +The following example returns all distinct `city` fields, and selects only the `city` and `country` fields: + + + + + +```ts +const distinctCities = await prisma.user.findMany({ + select: { + city: true, + country: true, + }, + distinct: ['city'], +}) +``` + + + + + +```js no-lines no-copy +;[ + { city: 'Paris', country: 'France' }, + { city: 'Lyon', country: 'France' }, +] +``` + + + + + +##### Select distinct on multiple fields + +The following example returns all distinct `city` _and_ `country` field combinations, and selects only the `city` and `country` fields: + + + + + +```ts +const distinctCitiesAndCountries = await prisma.user.findMany({ + select: { + city: true, + country: true, + }, + distinct: ['city', 'country'], +}) +``` + + + + + +```js no-lines no-copy +;[ + { city: 'Paris', country: 'France' }, + { city: 'Paris', country: 'Denmark' }, + { city: 'Lyon', country: 'France' }, +] +``` + + + + + +Note that there is now a "Paris, Denmark" in addition to "Paris, France": + +##### Select distinct in combination with a filter + +The following example returns all distinct `city` _and_ `country` field combinations where the user's email contains `"prisma.io"`, and selects only the `city` and `country` fields: + + + + + +```ts +const distinctCitiesAndCountries = await prisma.user.findMany({ + where: { + email: { + contains: 'prisma.io', + }, + }, + select: { + city: true, + country: true, + }, + distinct: ['city', 'country'], +}) +``` + + + + + +```js no-copy +;[ + { city: 'Paris', country: 'Denmark' }, + { city: 'Lyon', country: 'France' }, +] +``` + + + + + +## Nested queries + +### `create` + +A nested `create` query adds a new related record or set of records to a parent record. See: [Working with relations](/orm/prisma-client/queries/relation-queries) . + +#### Remarks + +- `create` is available as a nested query when you `create` (`prisma.user.create(...)`) a new parent record or `update` (`prisma.user.update(...)`) an existing parent record. + +> You can use a nested `create` _or_ a nested `createMany` to create multiple related records - [each technique pros and cons](/orm/prisma-client/queries/relation-queries#create-a-single-record-and-multiple-related-records) . + +#### Examples + +##### Create a new `User` record with a new `Profile` record + +```ts highlight=5;normal +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + profile: { + create: { bio: 'Hello World' }, + }, + }, +}) +``` + +##### Create a new `Profile` record with a new `User` record + +```ts +const user = await prisma.profile.create({ + data: { + bio: 'Hello World', + user: { +| create: { email: 'alice@prisma.io' }, + }, + }, +}) +``` + +##### Create a new `User` record with a new `Post` record + +```ts highlight=5;normal +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + posts: { + create: { title: 'Hello World' }, + }, + }, +}) +``` + +##### Create a new `User` record with two new `Post` records + +Because it's a one-to-many relation, you can also create several `Post` records at once by passing an array to `create`: + +```ts highlight=5-12;normal +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + posts: { + create: [ + { + title: 'This is my first post', + }, + { + title: 'Here comes a second post', + }, + ], + }, + }, +}) +``` + +You can also use a nested [`createMany`](#createmany-1) to achieve the same result. + +##### Update an existing `User` record by creating a new `Profile` record + +```ts highlight=5;normal; +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + create: { bio: 'Hello World' }, + }, + }, +}) +``` + +##### Update an existing `User` record by creating a new `Post` record + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { +| create: { title: 'Hello World' }, + }, + }, +}) +``` + +### `createMany` + +A nested `createMany` query adds a new set of records to a parent record. See: [Working with relations](/orm/prisma-client/queries/relation-queries) . + +#### Remarks + +- `createMany` is available as a nested query when you `create` (`prisma.user.create(...)`) a new parent record or `update` (`prisma.user.update(...)`) an existing parent record. +- Available in the context of a has-many relation - for example, you can `prisma.user.create(...)` a user and use a nested `createMany` to create multiple posts (posts have one user). +- **Not** available in the context of a many-to-many relation - for example, you **cannot** `prisma.post.create(...)` a post and use a nested `createMany` to create categories (many posts have many categories). +- Does not support nesting additional relations - you cannot nest an additional `create` or `createMany`. +- Allows setting foreign keys directly - for example, setting the `categoryId` on a post. + +> You can use a nested `create` _or_ a nested `createMany` to create multiple related records - [each technique pros and cons](/orm/prisma-client/queries/relation-queries#create-a-single-record-and-multiple-related-records) . + +#### Examples + +##### Update a `User` and multiple new related `Post` records + +```ts +const user = await prisma.user.update({ + where: { + id: 9, + }, + data: { + name: 'Elliott', + posts: { + createMany: { + data: [{ title: 'My first post' }, { title: 'My second post' }], + }, + }, + }, +}) +``` + +### `set` + +`set` overwrites the value of a relation - for example, replacing a list of `Post` records with a different list. See: [Working with relations](/orm/prisma-client/queries/relation-queries) + +#### Examples + +##### Update an existing `User` record by disconnecting any previous `Post` records and connecting two other existing ones + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + set: [{ id: 32 }, { id: 42 }], + }, + }, +}) +``` + +### `connect` + +A nested `connect` query connects a record to an existing related record by specifying an ID or unique identifier. See: [Working with relations](/orm/prisma-client/queries/relation-queries) + +#### Remarks + +- `connect` is available as a nested query when you create a new parent record or update an existing parent record. +- If the related record does not exist, Prisma Client throws an exception: + + ``` + The required connected records were not found. Expected 1 records to be connected, found 0. + ``` + +#### Examples + +##### Create a new `Profile` record and connect it to an existing `User` record via unique field + +```ts +const user = await prisma.profile.create({ + data: { + bio: 'Hello World', + user: { + connect: { email: 'alice@prisma.io' }, + }, + }, +}) +``` + +##### Create a new `Profile` record and connect it to an existing `User` record via an ID field + +```ts +const user = await prisma.profile.create({ + data: { + bio: 'Hello World', + user: { + connect: { id: 42 }, // sets userId of Profile record + }, + }, +}) +``` + +In [2.11.0](https://github.com/prisma/prisma/releases/2.11.0) and later, you can set the foreign key directly: + +```ts +const user = await prisma.profile.create({ + data: { + bio: 'Hello World', + userId: 42, + }, +}) +``` + +##### Create a new `Post` record and connect it to an existing `User` record + +```ts +const user = await prisma.post.create({ + data: { + title: 'Hello World', + author: { + connect: { email: 'alice@prisma.io' }, + }, + }, +}) +``` + +##### Update an existing `User` record by connecting it to an existing `Profile` record + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + connect: { id: 24 }, + }, + }, +}) +``` + +##### Update an existing `User` record by connecting it to two existing `Post` records + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + connect: [{ id: 24 }, { id: 42 }], + }, + }, +}) +``` + +### `connectOrCreate` + +`connectOrCreate` _either_ connects a record to an existing related record by ID or unique identifier _or_ creates a new related record if the record does not exist. See: [Working with relations](/orm/prisma-client/queries/relation-queries) + +#### Remarks + + + +- Multiple `connectOrCreate` queries that run _as concurrent transactions_ can result in a **race condition**. Consider the following example, where two queries attempt to `connectOrCreate` a blog post tag named `computing` at the same time (tag names must be unique): + + + + + ```ts + const createPost = await prisma.post.create({ + data: { + title: 'How to create a compiler', + content: '...', + author: { + connect: { + id: 9, + }, + }, + tags: { + connectOrCreate: { + create: { + name: 'computing', + }, + where: { + name: 'computing', + }, + }, + }, + }, + }) + ``` + + + + + ```ts + const createPost = await prisma.post.create({ + data: { + title: 'How to handle schema drift in production', + content: '...', + author: { + connect: { + id: 15, + }, + }, + tags: { + connectOrCreate: { + create: { + name: 'computing', + }, + where: { + name: 'computing', + }, + }, + }, + }, + }) + ``` + + + + + If query A and query B overlap in the following way, query A results in an exception: + + | Query A (Fail ❌) | Query B (Success ✅) | + | :--------------------------------------------------------------- | :--------------------------------------------------------------- | + | Query hits server, starts transaction A | Query hits server, starts transaction B | + | | Find record where `tagName` equals `computing`, record not found | + | Find record where `tagName` equals `computing`, record not found | | + | | Create record where `tagName` equals `computing` and connect | + | Create record where `tagName` equals `computing` | | + | Unique violation, record already created by transaction B | | + + To work around this scenario, we recommend catching the unique violation exception (`PrismaClientKnownRequestError`, error `P2002`) and retrying failed queries. + + + +#### Examples + +##### Create a new `Profile` record, then connect it to an existing `User` record _or_ create a new `User` + +The following example: + +1. Creates a `Profile` +2. Attempts to connect the profile to a `User` where the email address is `alice@prisma.io` +3. Creates a new user if a matching user does not exist + +```ts +const user = await prisma.profile.create({ + data: { + bio: 'The coolest Alice on the planet', + user: { + connectOrCreate: { + where: { email: 'alice@prisma.io' }, + create: { email: 'alice@prisma.io'} + }, + }, +}) +``` + +##### Create a new `Post` record and connect it to an existing `User` record, _or_ create a new `User` + +```ts +const user = await prisma.post.create({ + data: { + title: 'Hello World', + author: { + connectOrCreate: { + where: { email: 'alice@prisma.io' }, + create: { email: 'alice@prisma.io' }, + }, + }, + }, +}) +``` + +##### Update an existing `User` record by connecting it to an existing `Profile` record, _or_ creating a new `Profile` record + +The following example: + +1. Attempts to connect the user to a `Profile` with an `id` of `20` +2. Creates a new profile if a matching profile does not exist + +```ts +const updateUser = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + connectOrCreate: { + where: { id: 20 }, + create: { + bio: 'The coolest Alice in town', + }, + }, + }, + }, +}) +``` + +##### Update an existing `User` record by connect it to two existing `Post` records, or creating two new `Post` records + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + connectOrCreate: [ + { + where: { id: 32 }, + create: { title: 'This is my first post' }, + }, + { + where: { id: 19 }, + create: { title: 'This is my second post' }, + }, + ], + }, + }, +}) +``` + +### `disconnect` + +A nested `disconnect` query breaks the connection between a parent record and a related record, but does not delete either record. See: [Working with relations](/orm/prisma-client/queries/relation-queries) + +#### Remarks + +- `disconnect` is only available if the relation is optional. +- If the relationship you are attempting to disconnect does not exist: + + - ([In 2.21.0 and later](https://github.com/prisma/prisma/releases/tag/2.21.0)), the operation does nothing + - (Before [2.21.0](https://github.com/prisma/prisma/releases/tag/2.21.0)) Prisma Client throws an exception if the provided ID or unique identifier is not connected: + + ``` + The records for relation `PostToUser` between the `User` and `Post` models are not connected. + ``` + +#### Examples + +##### Update an existing `User` record by disconnecting the `Profile` record it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'bob@prisma.io' }, + data: { + profile: { + disconnect: true, + }, + }, +}) +``` + +##### Update an existing `User` record by disconnecting two `Post` records it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + disconnect: [{ id: 44 }, { id: 46 }], + }, + }, +}) +``` + +### `update` + +A nested `update` query updates one or more related records where the parent record's ID is `n`. See: [Working with relations](/orm/prisma-client/queries/relation-queries#update-a-specific-related-record) + +#### Remarks + +- Nested `update` queries are only available in the context of a top-level `update` query (for example, `prisma.user.update(...)`). +- If the parent record does not exist, Prisma Client throws an exception: + + ``` + AssertionError("Expected a valid parent ID to be present for nested update to-one case.") + ``` + +- If the related record that you want to update does not exist, Prisma Client throws an exception: + + ``` + AssertionError("Expected a valid parent ID to be present for nested update to-one case.") + ``` + +#### Examples + +##### Update an existing `User` record by updating the `Profile` record it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + update: { bio: 'Hello World' }, + }, + }, +}) +``` + +##### Update an existing `User` record by updating two `Post` records it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + update: [ + { + data: { published: true }, + where: { id: 32 }, + }, + { + data: { published: true }, + where: { id: 23 }, + }, + ], + }, + }, +}) +``` + +### `upsert` + + + +This section covers the usage of nested upsert within `update()`. To learn about the [`upsert()`](#upsert) operation, reference the linked documentation. + + + +A nested `upsert` query updates a related record if it exists, or creates a new related record. + +#### Examples + +##### Update an existing `User` record by updating the `Profile` record it's connected to or creating a new one (_upsert_) + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + upsert: { + create: { bio: 'Hello World' }, + update: { bio: 'Hello World' }, + }, + }, + }, +}) +``` + +##### Update an existing `User` record by updating two `Post` record it's connected to or creating new ones (_upsert_) + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + upsert: [ + { + create: { title: 'This is my first post' }, + update: { title: 'This is my first post' }, + where: { id: 32 }, + }, + { + create: { title: 'This is my second post' }, + update: { title: 'This is my second post' }, + where: { id: 23 }, + }, + ], + }, + }, +}) +``` + +### `delete` + +A nested `delete` query deletes a related record. The parent record is not deleted. + +#### Remarks + +- `delete` is only available if the relation is optional. + +#### Examples + +##### Update an existing `User` record by deleting the `Profile` record it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + profile: { + delete: true, + }, + }, +}) +``` + +##### Update an existing `User` record by deleting two `Post` records it's connected to + +```ts +const user = await prisma.user.update({ + where: { email: 'alice@prisma.io' }, + data: { + posts: { + delete: [{ id: 34 }, { id: 36 }], + }, + }, +}) +``` + +### `updateMany` + +A nested `updateMany` updates a list of related records and supports filtering - for example, you can update a user's unpublished posts. + +#### Examples + +##### Update all unpublished posts belonging to a specific user + +```ts +const result = await prisma.user.update({ + where: { + id: 2, + }, + data: { + posts: { + updateMany: { + where: { + published: false, + }, + data: { + likes: 0, + }, + }, + }, + }, +}) +``` + +### `deleteMany` + +A nested `deleteMany` deletes related records and supports filtering. For example, you can delete a user's posts while updating other properties of that user. + +#### Examples + +##### Delete all posts belonging to a specific user as part of an update + +```ts +const result = await prisma.user.update({ + where: { + id: 2, + }, + data: { + name: 'Updated name', + posts: { + deleteMany: {}, + }, + }, +}) +``` + +## Filter conditions and operators + + + +- From version 4.3.0, you can also use these operators to compare _fields_ in the same model [with the `.fields` property](#compare-columns-in-the-same-table). +- In versions before 4.3.0, you can compare fields in the same model [with raw queries](/orm/more/help-and-troubleshooting/help-articles/comparing-columns-through-raw-queries). + + + +### `equals` + +Value equals `n`. + +#### Examples + +##### Return all users where `name` equals `"Eleanor"` + +```ts +const result = await prisma.user.findMany({ + where: { + name: { + equals: 'Eleanor', + }, + }, +}) +``` + +You can also exclude the `equals`: + +```ts +const result = await prisma.user.findMany({ + where: { + name: 'Eleanor', + }, +}) +``` + +### `not` + +Value does not equal `n`. + +#### Examples + +##### Return all users where `name` does **not** equal `"Eleanor"` + +```ts +const result = await prisma.user.findMany({ + where: { + name: { + not: 'Eleanor', + }, + }, +}) +``` + +### `in` + +Value `n` exists in list. + +#### Remarks + +- `null` values are not returned. For example, if you combine `in` and `NOT` to return user whose name is _not_ in the list, users with `null` value names are not returned. + +#### Examples + +##### Get `User` records where the `id` can be found in the following list: `[22, 91, 14, 2, 5]` + +```ts +const getUser = await prisma.user.findMany({ + where: { + id: { in: [22, 91, 14, 2, 5] }, + }, +}) +``` + +##### Get `User` records where the `name` can be found in the following list: `['Saqui', 'Clementine', 'Bob']` + +```ts +const getUser = await prisma.user.findMany({ + where: { + name: { in: ['Saqui', 'Clementine', 'Bob'] }, + }, +}) +``` + +##### Get `User` records where `name` is **not** present in the list + +The following example combines `in` and [`NOT`](#not). You can also use [`notIn`](#notin). + +```ts +const getUser = await prisma.user.findMany({ + where: { + NOT: { + name: { in: ['Saqui', 'Clementine', 'Bob'] }, + }, + }, +}) +``` + +##### Get a `User` record where at least one `Post` has at least one specified `Category` + +```ts +const getUser = await prisma.user.findMany({ + where: { + // Find users where.. + posts: { + some: { + // ..at least one (some) posts.. + categories: { + some: { + // .. have at least one category .. + name: { + in: ['Food', 'Introductions'], // .. with a name that matches one of the following. + }, + }, + }, + }, + }, + }, +}) +``` + +### `notIn` + +Value `n` does not exist in list. + +#### Remarks + +- `null` values are not returned. + +#### Examples + +##### Get `User` records where the `id` can **not** be found in the following list: `[22, 91, 14, 2, 5]` + +```ts +const getUser = await prisma.user.findMany({ + where: { + id: { notIn: [22, 91, 14, 2, 5] }, + }, +}) +``` + +### `lt` + +Value `n` is less than `x`. + +#### Examples + +##### Get all `Post` records where `likes` is less than `9` + +```ts +const getPosts = await prisma.post.findMany({ + where: { + likes: { + lt: 9, + }, + }, +}) +``` + +### `lte` + +Value `n` is less than _or_ equal to `x`. + +#### Examples + +##### Get all `Post` records where `likes` is less or equal to `9` + +```ts +const getPosts = await prisma.post.findMany({ + where: { + likes: { + lte: 9, + }, + }, +}) +``` + +### `gt` + +Value `n` is greater than `x`. + +#### Examples + +##### Get all `Post` records where `likes` is greater than `9` + +```ts +const getPosts = await prisma.post.findMany({ + where: { + likes: { + gt: 9, + }, + }, +}) +``` + +### `gte` + +Value `n` is greater than _or_ equal to `x`. + +#### Examples + +##### Get all `Post` records where `likes` is greater than or equal to `9` + +```ts +const getPosts = await prisma.post.findMany({ + where: { + likes: { + gte: 9, + }, + }, +}) +``` + +#### Examples + +##### Get all `Post` records where `date_created` is greater than March 19th, 2020 + +```js +const result = await prisma.post.findMany({ + where: { + date_created: { + gte: new Date( + '2020-03-19T14:21:00+0200' + ) /* Includes time offset for UTC */, + }, + }, +}) +``` + +### `contains` + +Value `n` contains `x`. + +#### Examples + +##### Count all `Post` records where `content` contains `databases` + +```js +const result = await prisma.post.count({ + where: { + content: { + contains: 'databases', + }, + }, +}) +``` + +##### Count all `Post` records where `content` **does not** contain `databases` + +```js +const result = await prisma.post.count({ + where: { + NOT: { + content: { + contains: 'databases', + }, + }, + }, +}) +``` + +### `search` + +Use [Full-Text Search](/orm/prisma-client/queries/full-text-search) to search within a `String` field. + +Full-Text Search is currently in **Preview** and only available for **PostgreSQL** and **MySQL**. To use `search`, you'll need to enable the `fullTextSearch` +preview feature. + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch"] +} +``` + +#### Examples + +##### Find all posts with a title that contains `cat` or `dog`. + +```js +const result = await prisma.post.findMany({ + where: { + title: { + search: 'cat | dog', + }, + }, +}) +``` + +##### Find all posts with a title that contains `cat` and `dog`. + +```js +const result = await prisma.post.findMany({ + where: { + title: { + search: 'cat & dog', + }, + }, +}) +``` + +##### Find all posts with a title that doesn't contain `cat`. + +```js +const result = await prisma.post.findMany({ + where: { + title: { + search: '!cat', + }, + }, +}) +``` + +### `mode` + +#### Remarks + +- Supported by the PostgreSQL and MongoDB connectors only + +#### Examples + +##### Get all `Post` records where `title` contains `prisma`, in a case insensitive way + +```js +const result = await prisma.post.findMany({ + where: { + title: { + contains: 'prisma', + mode: 'insensitive', + }, + }, +}) +``` + +### `startsWith` + +#### Examples + +##### Get all `Post` records where `title` starts with `Pr` (such as `Prisma`) + +```js +const result = await prisma.post.findMany({ + where: { + title: { + startsWith: 'Pr', + }, + }, +}) +``` + +### `endsWith` + +#### Get all `User` records where `email` ends with `prisma.io` + +```js +const result = await prisma.user.findMany({ + where: { + email: { + endsWith: 'prisma.io', + }, + }, +}) +``` + +### `AND` + +All conditions must return `true`. Alternatively, pass a list of objects into the `where` clause - the [`AND` operator is not required](#get-all-post-records-where-the-content-field-contains-prisma-and-published-is-false-no-and). + +#### Examples + +##### Get all `Post` records where the `content` field contains `Prisma` and `published` is `false` + +```js +const result = await prisma.post.findMany({ + where: { + AND: [ + { + content: { + contains: 'Prisma', + }, + }, + { + published: { + equals: false, + }, + }, + ], + }, +}) +``` + +##### Get all `Post` records where the `content` field contains `Prisma` and `published` is `false` (no `AND`) + +The following format returns the same results as the previous example **without** the `AND` operator: + +```js +const result = await prisma.post.findMany({ + where: { + content: { + contains: 'Prisma', + }, + published: { + equals: false, + }, + }, +}) +``` + +##### Get all `Post` records where the `title` field contains `Prisma` or `databases`, and `published` is `false` + +The following example combines `OR` and `AND`: + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + AND: { + published: false, + }, + }, +}) +``` + +### `OR` + +One or more conditions must return `true`. + +#### Examples + +##### Get all `Post` records where the `title` field contains `Prisma` or `databases` + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + }, +}) +``` + +##### Get all `Post` records where the `title` filed contains `Prisma` or `databases`, but not `SQL` + +The following example combines `OR` and `NOT`: + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + NOT: { + title: { + contains: 'SQL', + }, + }, + }, +}) +``` + +##### Get all `Post` records where the `title` field contains `Prisma` or `databases`, and `published` is `false` + +The following example combines `OR` and `AND`: + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + AND: { + published: false, + }, + }, +}) +``` + +### `NOT` + +All conditions must return `false`. + +#### Examples + +##### Get all `Post` records where the `title` filed contains `Prisma` or `databases`, but not `SQL` + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + NOT: { + title: { + contains: 'SQL', + }, + }, + }, +}) +``` + +##### Get all `Post` records where the `title` field contains `Prisma` or `databases`, but not `SQL`, and the related `User` record' email address does not contain `sarah` + +```js +const result = await prisma.post.findMany({ + where: { + OR: [ + { + title: { + contains: 'Prisma', + }, + }, + { + title: { + contains: 'databases', + }, + }, + ], + NOT: { + title: { + contains: 'SQL', + }, + }, + user: { + NOT: { + email: { + contains: 'sarah', + }, + }, + }, + }, + include: { + user: true, + }, +}) +``` + +## Relation filters + +### `some` + +Returns all records where **one or more** ("some") _related_ records match filtering criteria. + +#### Remarks + +- You can use `some` without parameters to return all records with at least one relation + +#### Examples + +##### Get all `User` records where _some_ posts mention `Prisma` + +```ts +const result = await prisma.user.findMany({ + where: { + post: { + some: { + content: { + contains: "Prisma" + } + } + } + } +} +``` + +### `every` + +Returns all records where **all** ("every") _related_ records match filtering criteria. + +#### Examples + +##### Get all `User` records where _all_ posts are published + +```ts +const result = await prisma.user.findMany({ + where: { + post: { + every: { + published: true + }, + } + } +} +``` + +### `none` + +Returns all records where **zero** _related_ records match filtering criteria. + +#### Remarks + +- You can use `none` without parameters to [return all records with no relations](#get-all-user-records-with-zero-posts) + +#### Examples + +##### Get all `User` records with zero posts + +```ts +const result = await prisma.user.findMany({ + where: { + post: { + none: {} // User has no posts + } + } +} +``` + +##### Get all `User` records with zero published posts + +```ts +const result = await prisma.user.findMany({ + where: { + post: { + none: { + published: true + } + } + } +} +``` + +### `is` + +Returns all records where related record matches filtering criteria (for example, user's name `is` Bob). + +#### Examples + +##### Get all `Post` records where user's name is `"Bob"` + +```ts +const result = await prisma.post.findMany({ + where: { + user: { + is: { + name: "Bob" + }, + } + } +} +``` + +### `isNot` + +Returns all records where related record matches filtering criteria (for example, user's name `isNot` Bob). + +#### Examples + +##### Get all `Post` records where user's name is NOT `"Bob"` + +```ts +const result = await prisma.post.findMany({ + where: { + user: { + isNot: { + name: "Bob" + }, + } + } +} +``` + +## Scalar list methods + +### `set` + +Use `set` to overwrite the value of a scalar list field. + +#### Remarks + +- `set` is optional - you can set the value directly: + + ```ts + tags: ['computers', 'books'] + ``` + +#### Examples + +##### Set the value of `tags` to a list of string values + +```ts +const setTags = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: { + set: ['computing', 'books'], + }, + }, +}) +``` + +##### Set `tags` to a list of values _without_ using the `set` keyword + +```ts +const setTags = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: ['computing', 'books'], + }, +}) +``` + +#### Set the value of `tags` to a single string value + +```ts +const setTags = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: { + set: 'computing', + }, + }, +}) +``` + +### `push` + +`push` is available in version [2.20.0](https://github.com/prisma/prisma/releases/2.20.0) and later. Use `push` to add _one_ value or _multiple_ values to a scalar list field. + +#### Remarks + +- Available for PostgreSQL and MongoDB only. +- You can push a list of values or only a single value. + +#### Examples + +##### Add a `computing` item to the `tags` list + +```ts +const addTag = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: { + push: 'computing', + }, + }, +}) +``` + +```ts +const addTag = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: { + push: ['computing', 'genetics'], + }, + }, +}) +``` + +### `unset` + + + +This method is available on MongoDB only in versions +[3.11.1](https://github.com/prisma/prisma/releases/tag/3.11.1) and later. + + + +Use `unset` to unset the value of a scalar list. Unlike `set: null`, `unset` removes the list entirely. + +#### Examples + +##### Unset the value of `tags` + +```ts +const setTags = await prisma.post.update({ + where: { + id: 9, + }, + data: { + tags: { + unset: true, + }, + }, +}) +``` + +## Scalar list filters + +Scalar list filters allow you to filter by the contents of a list / array field. + + + +Available for: + +- PostgreSQL in versions [2.15.0](https://github.com/prisma/prisma/releases/tag/2.15.0) and later +- CockroachDB in versions [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) and later +- MongoDB in versions [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0) and later + + + +### Remarks + +- Scalar list / array filters [ignore `NULL` values](/orm/prisma-client/special-fields-and-types/working-with-scalar-lists-arrays#null-values-in-arrays) . Using `isEmpty` or `NOT` does not return records with `NULL` value lists / arrays, and `{ equals: null }` results in an error. + +### `has` + +The given value exists in the list. + +#### Examples + +The following query returns all `Post` records where the `tags` list includes `"databases"`: + +```ts +const posts = await client.post.findMany({ + where: { + tags: { + has: 'databases', + }, + }, +}) +``` + +The following query returns all `Post` records where the `tags` list **does not** include `"databases"`: + +```ts +const posts = await client.post.findMany({ + where: { + NOT: { + tags: { + has: 'databases', + }, + }, + }, +}) +``` + +### `hasEvery` + +Every value exists in the list. + +#### Examples + +The following query returns all `Post` records where the `tags` list includes _at least_ `"databases"` _and_ `"typescript"`: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + hasEvery: ['databases', 'typescript'], + }, + }, +}) +``` + +### `hasSome` + +At least one value exists in the list. + +#### Examples + +The following query returns all `Post` records where the `tags` list includes `"databases"` _or_ `"typescript"`: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + hasSome: ['databases', 'typescript'], + }, + }, +}) +``` + +### `isEmpty` + +The list is empty. + +#### Examples + +The following query returns all `Post` records that have no tags: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + isEmpty: true, + }, + }, +}) +``` + +### `isSet` + + + +This filter is available on MongoDB only in versions +[3.11.1](https://github.com/prisma/prisma/releases/tag/3.11.1) and later. + + + +Filter lists to include only results that have been set (either set to a value, or explicitly set to `null`). Setting this filter to `true` will exclude undefined results that are not set at all. + +#### Examples + +The following query returns all `Post` records where the `tags` have been set to either `null` or a value: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + isSet: true, + }, + }, +}) +``` + +### `equals` + +The list matches the given value exactly. + +#### Examples + +The following query returns all `Post` records where the `tags` list includes `"databases"` and `"typescript"` only: + +```ts +const posts = await prisma.post.findMany({ + where: { + tags: { + equals: ['databases', 'typescript'], + }, + }, +}) +``` + +## Composite type methods + + + +Available for MongoDB only in Prisma `3.10.0` and later. + + + +Composite type methods allow you to create, update and delete [composite types](/orm/prisma-client/special-fields-and-types/composite-types). + +### `set` + +Use `set` to overwrite the value of a composite type. + +#### Remarks + +- The `set` keyword is optional - you can set the value directly: + ```ts + photos: [ + { height: 100, width: 200, url: '1.jpg' }, + { height: 100, width: 200, url: '2.jpg' }, + ] + ``` + +#### Examples + +##### Set the `shippingAddress` composite type within a new `order` + +```ts +const order = await prisma.order.create({ + data: { + // Normal relation + product: { connect: { id: 'some-object-id' } }, + color: 'Red', + size: 'Large', + // Composite type + shippingAddress: { + set: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + }, + }, +}) +``` + +##### Set an optional composite type to `null` + +```ts +const order = await prisma.order.create({ + data: { + // Embedded optional type, set to null + billingAddress: { + set: null, + }, + }, +}) +``` + +### `unset` + +Use `unset` to unset the value of a composite type. Unlike `set: null`, this removes the field entirely from the MongoDB document. + +#### Examples + +##### Remove the `billingAddress` from an `order` + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + billingAddress: { + // Unset the billing address + // Removes "billingAddress" field from order + unset: true, + }, + }, +}) +``` + +### `update` + +Use `update` to update fields within a required composite type. + +#### Remarks + +The `update` method cannot be used on optional types. Instead, use [upsert](#upsert-2) + +#### Examples + +##### Update the zip field of a `shippingAddress` composite type + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + shippingAddress: { + // Update just the zip field + update: { + zip: '41232', + }, + }, + }, +}) +``` + +### `upsert` + +Use `upsert` to update an existing optional composite type if it exists, and otherwise set the composite type. + +#### Remarks + +The `upsert` method cannot be used on required types. Instead, use [update](#update-2) + +#### Examples + +##### Create a new `billingAddress` if it doesn't exist, and otherwise update it + +```ts +const order = await prisma.order.update({ + where: { + id: 'some-object-id', + }, + data: { + billingAddress: { + // Create the address if it doesn't exist, + // otherwise update it + upsert: { + set: { + street: '1084 Candycane Lane', + city: 'Silverlake', + zip: '84323', + }, + update: { + zip: '84323', + }, + }, + }, + }, +}) +``` + +### `push` + +Use `push` to push values to the end of a list of composite types. + +#### Examples + +##### Add a new photo to the `photos` list + +```ts +const product = prisma.product.update({ + where: { + id: 10, + }, + data: { + photos: { + // Push a photo to the end of the photos list + push: [{ height: 100, width: 200, url: '1.jpg' }], + }, + }, +}) +``` + +## Composite type filters + + + +Available for MongoDB only in Prisma `3.11.0` and later. + + + +Composite type filters allow you to filter the contents of [composite types](/orm/prisma-client/special-fields-and-types/composite-types). + +### `equals` + +Use `equals` to filter results by matching a composite type or a list of composite types. Requires all required fields of the composite type to match. + +#### Remarks + +When matching optional fields, you need to distinguish between undefined (missing) fields of the document, and fields that have been explicitly set to `null`: + +- If you omit an optional field, it will match undefined fields, but not fields that have been set to `null` +- If you filter for `null` values of an optional field with `equals: { ... exampleField: null ... }`, then it will match only documents where the field has been set to `null`, and not undefined fields + +The ordering of fields and lists matters when using `equals`: + +- For fields, `{ "a": "1", "b": "2" }` and `{ "b": "2", "a": "1" }` are not considered equal +- For lists, `[ { "a": 1 }, { "a": 2 } ]` and `[ { "a": 2 }, { "a": 1 } ]` are not considered equal + +#### Examples + +##### Find orders that exactly match the given `shippingAddress` + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + equals: { + street: '555 Candy Cane Lane', + city: 'Wonderland', + zip: '52337', + }, + }, + }, +}) +``` + +##### Find products with photos that match all of a list of `url`s + +```ts +const product = prisma.product.findMany({ + where: { + equals: { + photos: [{ url: '1.jpg' }, { url: '2.jpg' }], + }, + }, +}) +``` + +### `is` + +Use `is` to filter results by matching specific fields within composite types. + +#### Examples + +##### Find orders with a `shippingAddress` that matches the given street name + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + is: { + street: '555 Candy Cane Lane', + }, + }, + }, +}) +``` + +### `isNot` + +Use `isNot` to filter results for composite type fields that do not match. + +#### Examples + +##### Find orders with a `shippingAddress` that does not match the given zip code + +```ts +const orders = await prisma.order.findMany({ + where: { + shippingAddress: { + isNot: { + zip: '52337', + }, + }, + }, +}) +``` + +### `isEmpty` + +Use `isEmpty` to filter results for an empty list of composite types. + +#### Examples + +##### Find products with no photos + +```ts +const product = prisma.product.findMany({ + where: { + photos: { + isEmpty: true, + }, + }, +}) +``` + +### `every` + +Use `every` to filter for lists of composite types where every item in the list matches the condition + +#### Examples + +##### Find the first product where every photo has a `height` of `200` + +```ts +const product = prisma.product.findFirst({ + where: { + photos: { + every: { + { height: 200 }, + } + } + }, +}) +``` + +### `some` + +Use `some` to filter for lists of composite types where one or more items in the list match the condition. + +#### Examples + +##### Find the first product where one or more photos have a `url` of `2.jpg` + +```ts +const product = prisma.product.findFirst({ + where: { + photos: { + some: { + { url: "2.jpg" }, + } + } + }, +}) +``` + +### `none` + +Use `none` to filter for lists of composite types where no items in the list match the condition. + +#### Examples + +##### Find the first product where no photos have a `url` of `2.jpg` + +```ts +const product = prisma.product.findFirst({ + where: { + photos: { + none: { + { url: "2.jpg" }, + } + } + }, +}) + +``` + +## Atomic number operations + +Atomic operations on update is available for number field types (`Float` and `Int`). This feature allows you to update a field based on its **current** value (such as _subtracting_ or _dividing_) without risking a race condition. + +
+ +Overview: Race conditions + +A race conditions occurs when two or more operations must be done in sequence in order to complete a task. In the following example, two clients try to increase the same field (`postCount`) by one: + +| Client | Operation | Value | +| :------- | :------------------ | :----- | +| Client 1 | **Get** field value | `21` | +| Client 2 | **Get** field value | `21` | +| Client 2 | **Set** field value | `22` | +| Client 1 | **Set** field value | `22` ✘ | + +The value _should_ be `23`, but the two clients did not read and write to the `postCount` field in sequence. Atomic operations on update combine read and write into a single operation, which prevents a race condition: + +| Client | Operation | Value | +| :------- | :-------------------------- | :----------------- | +| Client 1 | **Get and set** field value | `21` → `22` | +| Client 2 | **Get and set** field value | `22` → `23` ✔ | + +
+ +### Operators + +| Option | Description | +| :---------- | :------------------------------------------------------------ | +| `increment` | Adds `n` to the current value. | +| `decrement` | Subtacts `n` from the current value. | +| `multiply` | Multiplies the current value by `n`. | +| `divide` | Divides the current value by `n`. | +| `set` | Sets the current field value. Identical to `{ myField : n }`. | + +### Remarks + +- You can only perform **one** atomic update per field, per query. +- If a field is `null`, it will not be updated by `increment`, `decrement`, `multiply`, or `divide`. + +### Examples + +#### Increment all `view` and `likes` fields of all `Post` records by `1` + +```ts +const updatePosts = await prisma.post.updateMany({ + data: { + views: { + increment: 1, + }, + likes: { + increment: 1, + }, + }, +}) +``` + +#### Set all `views` fields of all `Post` records to `0` + +```ts +const updatePosts = await prisma.post.updateMany({ + data: { + views: { + set: 0, + }, + }, +}) +``` + +Can also be written as: + +```ts +const updatePosts = await prisma.post.updateMany({ + data: { + views: 0, + }, +}) +``` + +## `Json` filters + +For use cases and advanced examples, see: [Working with `Json` fields](/orm/prisma-client/special-fields-and-types/working-with-json-fields). + + + +Supported by [PostgreSQL](/orm/overview/databases/postgresql) and [MySQL](/orm/overview/databases/mysql) with different syntaxes for the `path` option. PostgreSQL does not support filtering on object key values in arrays. + + + +The examples in this section assumes that the value of the `pet` field is: + +```json +{ + "favorites": { + "catBreed": "Turkish van", + "dogBreed": "Rottweiler", + "sanctuaries": ["RSPCA", "Alley Cat Allies"], + "treats": [ + { "name": "Dreamies", "manufacturer": "Mars Inc" }, + { "name": "Treatos", "manufacturer": "The Dog People" } + ] + }, + "fostered": { + "cats": ["Bob", "Alice", "Svetlana the Magnificent", "Queenie"] + }, + "owned": { + "cats": ["Elliott"] + } +} +``` + +### Remarks + +- The implementation of `Json` filtering [differs between database connectors](/orm/prisma-client/special-fields-and-types/working-with-json-fields) +- Filtering is case sensitive in PostgreSQL and does not yet support `mode` + +### `path` + +`path` represents the location of a specific key. The following query returns all users where the nested `favourites` > `dogBreed` key equals `"Rottweiler"`. + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['favorites', 'dogBreed'], + equals: 'Rottweiler', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.favorites.dogBreed', + equals: 'Rottweiler', + }, + }, +}) +``` + + + + +The following query returns all users where the nested `owned` > `cats` array contains `"Elliott"`. + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['owned', 'cats'], + array_contains: ['Elliott'], + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.owned.cats', + array_contains: 'Elliott', + }, + }, +}) +``` + + + + + + +Filtering by the key values of objects inside an array (below) is only supported by the MySQL connector. + + + +The following query returns all users where the nested `favorites` > `treats` array contains an object where the `name` value is `"Dreamies"`: + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.favorites.treats[*].name', + array_contains: 'Dreamies', + }, + }, +}) +``` + +### `string_contains` + +The following query returns all users where the nested `favorites` > `catBreed` key value contains `"Van"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['favorites', 'catBreed'], + string_contains: 'Van', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.favorites.catBreed', + string_contains: 'Van', + }, + }, +}) +``` + + + + + +### `string_starts_with` + +The following query returns all users where the nested `favorites` > `catBreed` key value starts with `"Turkish"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['favorites', 'catBreed'], + string_starts_with: 'Turkish', + }, + }, +}) +``` + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.favorites.catBreed', + string_starts_with: 'Turkish', + }, + }, +}) +``` + + + + + +### `string_ends_with` + +The following query returns all users where the nested `favorites` \> `catBreed` key value ends with `"Van"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['favorites', 'catBreed'], + string_ends_with: 'Van', + }, + }, +}) +``` + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.favorites.catBreed', + string_ends_with: 'Van', + }, + }, +}) +``` + + + + + +### `array_contains` + +The following query returns all users where the `sanctuaries` array contains the value `"RSPCA"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['sanctuaries'], + array_contains: ['RSPCA'], + }, + }, +}) +``` + + + +**Note**: In PostgreSQL, the value of `array_contains` must be an array and not a string, even if the array only contains a single value. + + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.sanctuaries', + array_contains: 'RSPCA', + }, + }, +}) +``` + + + + +The following query returns all users where the `sanctuaries` array contains _all_ the values in the given array: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['sanctuaries'], + array_contains: ['RSPCA', 'Alley Cat Allies'], + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.sanctuaries', + array_contains: ['RSPCA', 'Alley Cat Allies'], + }, + }, +}) +``` + + + + +### `array_starts_with` + +The following query returns all users where the `sanctuaries` array starts with the value `"RSPCA"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['sanctuaries'], + array_starts_with: 'RSPCA', + }, + }, +}) +``` + + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.sanctuaries', + array_starts_with: 'RSPCA', + }, + }, +}) +``` + + + + +### `array_ends_with` + +The following query returns all users where the `sanctuaries` array ends with the value `"Alley Cat Allies"`: + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: ['sanctuaries'], + array_ends_with: 'Alley Cat Allies', + }, + }, +}) +``` + + + + +```ts +const getUsers = await prisma.user.findMany({ + where: { + pets: { + path: '$.sanctuaries', + array_ends_with: 'Alley Cat Allies', + }, + }, +}) +``` + + + + +## Client methods + +**Note:** Client-level methods are prefixed by `$`. + +### Remarks + +- `$on` and `$use` client methods do not exist on extended client instances which are extended using [`$extends`](#extends) + + + +In [extended clients](/orm/prisma-client/client-extensions), Client methods do not necessarily exist. If you are extending your client, make sure to check for existence before using Client methods like `$transaction` or `$connect`. + +In addition, if you are using `$on` or `$use`, you will need to use these client methods before extending your client as these methods do not exist on extended clients. For `$use` specifically we recommend transitioning [to use query extensions](/orm/prisma-client/client-extensions/query). + + + +### `$disconnect()` + +The `$disconnect()` method closes the database connections that were established when `$connect` was called and stops the process that was running Prisma's query engine. See [Connection management](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management) for an overview of `$connect()` and `$disconnect()`. + +#### Remarks + +- `$disconnect()` returns a `Promise`, so you should call it inside an `async` function with the `await` keyword. + +### `$connect()` + +The `$connect()` method establishes a physical connection to the database via Prisma's query engine. See [Connection management](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management) for an overview of `$connect()` and `$disconnect()`. + +#### Remarks + +- `$connect()` returns a `Promise`, so you should call it inside an `async` function with the `await` keyword. + +### `$on()` + + + +`$on` is not available in [extended clients](/orm/prisma-client/client-extensions). Please either migrate to client extensions or use the `$on` method prior to extending your client. + + + +The `$on()` method allows you to subscribe to [logging events](#log) or the [exit hook](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management#exit-hooks). + +### `$use()` + + + +`$use` is not available in [extended clients](/orm/prisma-client/client-extensions). Please [either migrate to query extensions](/orm/prisma-client/client-extensions/query) or use the `$use` method prior to extending your client. + + + +The `$use()` method adds [middleware](/orm/prisma-client/client-extensions/middleware) : + +```ts +prisma.$use(async (params, next) => { + console.log('This is middleware!') + // Modify or interrogate params here + + return next(params) +}) +``` + +#### `next` + +`next` represents the "next level" in the middleware stack, which could be the next middleware or the Prisma Query, depending on [where in the stack you are](/orm/prisma-client/client-extensions/middleware#running-order-and-the-middleware-stack). + +#### `params` + +`params` is an object with information to use in your middleware. + +| Parameter | Description | +| :----------------- | :--------------------------------------------------------------------------------------------- | +| `action` | The query type - for example, `create` or `findMany`. | +| `args` | Arguments that were passed into the query - for example, `where`, `data`, or `orderBy` | +| `dataPath` | Populated if you use the [fluent API](/orm/prisma-client/queries/relation-queries#fluent-api). | +| `model` | The model type - for example, `Post` or `User`. | +| `runInTransaction` | Returns `true` if the query ran in the context of a [transaction](#transaction). | + +:::tip + +If you need the `model` property as a string, use: `String(params.model)` + +::: + +Example parameter values: + +```js +{ + args: { where: { id: 15 } }, + dataPath: [ 'select', 'author', 'select', 'posts' ], + runInTransaction: false, + action: 'findMany', + model: 'Post' +} +``` + +#### Examples + +See [middleware examples](/orm/prisma-client/client-extensions/middleware#samples) . + +### `$executeRaw()` + +See: [Raw database access (`$executeRaw()`)](/orm/prisma-client/queries/raw-database-access/raw-queries#executeraw). + +### `$queryRaw()` + +See: [Raw database access (`$queryRaw()`)](/orm/prisma-client/queries/raw-database-access/raw-queries#queryraw). + +### `$runCommandRaw()` + +See: [Raw database access (`$runCommandRaw()`)](/orm/prisma-client/queries/raw-database-access/raw-queries#runcommandraw). + +### `$transaction()` + +See: [Transactions](/orm/prisma-client/queries/transactions). + +### `$metrics` + +Prisma metrics give you a detailed insight into how Prisma Client interacts with your database. You can use this insight to help diagnose performance issues with your application. Learn more: [Metrics](/orm/prisma-client/observability-and-logging/metrics). + +Prisma metrics has the following methods: + +- `$metrics.json()`: [Retrieves Prisma metrics in JSON format](/orm/prisma-client/observability-and-logging/metrics#retrieve-metrics-in-json-format). +- `$metrics.prometheus()`: [Retrieves Prisma metrics in Prometheus format](/orm/prisma-client/observability-and-logging/metrics#retrieve-metrics-in-prometheus-format). + +### `$extends` + +With `$extends`, you can create and use Prisma Client extensions to add functionality to Prisma Client in the following ways: + +- `model`: add custom methods to your models +- `client`: add custom methods to your client +- `query`: create custom Prisma Client queries +- `result`: add custom fields to your query results + +Learn more: [Prisma Client extensions](/orm/prisma-client/client-extensions). + +## Utility types + +Utility types are helper functions and types that live on the Prisma namespace. They are useful for keeping your application type safe. + +### `Prisma.validator` + +The `validator` helps you create re-usable query parameters based on your schema models while making sure that the objects you create are valid. See also: [Using `Prisma.validator`](/orm/prisma-client/type-safety/prisma-validator) + +There are two ways you can use the `validator`: + +#### Using generated Prisma Client types + +Using types provides a type-level approach to validate data: + +```ts +Prisma.validator({ args }) +``` + +#### Using a "selector" + +When using the selector pattern, you use an existing Prisma Client instance to create a validator. This pattern allows you to select the model, operation, and query option to validate against. + +You can also use an instance of Prisma Client that has been extended using a [Prisma Client extension](/orm/prisma-client/client-extensions). + +```ts +Prisma.validator( + PrismaClientInstance, + '', + '', + '' +)({ args }) +``` + +#### Examples + +The following example shows how you can extract and validate the input for the `create` operation you can reuse within your app: + +```ts +import { Prisma } from '@prisma/client' + +const validateUserAndPostInput = (name, email, postTitle) => { + return Prisma.validator()({ + name, + email, + posts: { + create: { + title: postTitle, + }, + }, + }) +} +``` + +Here is an alternative syntax for the same operation: + +```ts +import { Prisma } from '@prisma/client' +import prisma from './prisma' + +const validateUserAndPostInput = (name, email, postTitle) => { + return Prisma.validator( + prisma, + 'user', + 'create', + 'data' + )({ + name, + email, + posts: { + create: { + title: postTitle, + }, + }, + }) +} +``` + +## Compare columns in the same table + +You can compare columns in the same table directly, for non-unique filters. + +This feature was moved to general availability in version 5.0.0 and was available via the `fieldReference` Preview feature from Prisma versions 4.3.0 to 4.16.2. + + + +In the following situations, you must [use raw queries to compare columns in the same table](/orm/more/help-and-troubleshooting/help-articles/comparing-columns-through-raw-queries): + +- If you use a version earlier than 4.3.0 +- If you want to use a unique filter, such as [`findUnique`](#findunique) or [`findUniqueOrThrow`](#finduniqueorthrow) +- If you want to compare a field with a [unique constraint](/orm/prisma-schema/data-model/models#defining-a-unique-field) +- If you want to use one of the following operators to compare a [JSON field](/orm/prisma-client/special-fields-and-types/working-with-json-fields) in MySQL or MariaDB with another field: [`gt`](#gt), [`gte`](#gte), [`lt`](#lt), or [`lte`](#lte). Note that you can use these operators to compare the JSON field with a scalar value. This limitation applies only if you try to compare a JSON field with another field. + + + +To compare columns in the same table, use the `.fields` property. In the following example, the query returns all records where the value in the `prisma.product.quantity` field is less than or equal to the value in the `prisma.product.warnQuantity` field. + +```ts +prisma.product.findMany({ + where: { quantity: { lte: prisma.product.fields.warnQuantity } }, +}) +``` + + + +`fields` is a special property of every model. It contains the list of fields for that model. + + + +### Considerations + +#### Fields must be of the same type + +You can only make comparisons on fields of the same type. For example, the following causes an error: + +```ts +await prisma.order.findMany({ + where: { + id: { equals: prisma.order.fields.due }, + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // Type error: id is a string, while amountDue is an integer + }, +}) +``` + +#### Fields must be in the same model + +You can only make comparisons with the `fields` property on fields in the same model. The following example does not work: + +```ts +await prisma.order.findMany({ + where: { + id: { equals: prisma.user.fields.name }, + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // Type error: name is a field on the User model, not Order + }, +}) +``` + +However, you can compare fields in separate models with [standard queries](#model-queries). + +#### In `groupBy` model queries, put your referenced fields in the `by` argument + +If you use the [groupBy](#groupby) model query with the `having` option, then you must put your referenced fields in the `by` argument. + +The following example works: + +```ts +prisma.user.groupBy({ + by: ['id', 'name'], + having: { id: { equals: prisma.user.fields.name } }, +}) +``` + +The following example does not work, because `name` is not in the `by` argument: + +```ts +prisma.user.groupBy({ + by: ['id'], + having: { id: { equals: prisma.user.fields.name } }, + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // name is not in the 'by' argument +}) +``` + +#### Search for fields in scalar lists + +If your data source supports scalar lists (for example in PostgreSQL), then you can search for all records where a specific field is in a list of fields. To do so, reference the scalar list with the [`in`](#in) and [`notIn`](#notin) filters. For example: + +```ts +await prisma.user.findMany({ + where: { + // find all users where 'name' is in a list of tags + name: { in: prisma.user.fields.tags }, + }, +}) +``` + +## Filter on non-unique fields with `UserWhereUniqueInput` + +From version 5.0.0, the generated type `UserWhereUniqueInput` on [`where`](#where) exposes all fields on the model, not just unique fields. +This was available under the [`extendedWhereUnique` Preview flag](/orm/reference/preview-features/client-preview-features#preview-features-promoted-to-general-availability) between versions 4.5.0 to 4.16.2 + +You must specify at least one unique field in your `where` statement [outside of boolean operators](#boolean-operators-with-userwhereuniqueinput), and you can specify any number of additional unique and non-unique fields. You can use this to add filters to any operation that returns a single record. For example, you can use this feature for the following: + +- [Optimistic concurrency control on updates](#optimistic-concurrency-control-on-updates) +- [Permission checks](#permission-checks) +- [Soft deletes](#soft-deletes) + +From version 4.6.0, you can use this feature to filter on optional [one-to-one nested reads](/orm/prisma-client/queries/relation-queries#nested-reads). + +### Optimistic concurrency control on updates + +You can filter on non-unique fields to perform [optimistic concurrency control](/orm/prisma-client/queries/transactions#optimistic-concurrency-control) on `update` operations. + +To perform optimistic concurrency control, we recommend that you use a `version` field to check whether the data in a record or related record has changed while your code executes. Before version 4.5.0, you could not evaluate the `version` field in an `update` operation, because the field is non-unique. From version 4.5.0, you can evaluate the `version` field. + +In the following example, `updateOne` and `updateTwo` first read the same record and then attempt to update it. The database only executes these updates if the value in `version` is the same as the value when it did the initial read. When the database executes the first of these updates (which might be `updateOne` or `updateTwo`, depending on timing), it increments the value in `version`. This means that the database does not execute the second update because the value in `version` has changed. + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + city String + version Int +} +``` + +```ts +function updateOne() { + const user = await prisma.user.findUnique({ id: 1 }) + + await prisma.user.update({ + where: { id: user.id, version: user.version }, + data: { city: 'Berlin', version: { increment: 1 } }, + }) +} + +function updateTwo() { + const user = await prisma.user.findUnique({ id: 1 }) + + await prisma.user.update({ + where: { id: user.id, version: user.version }, + data: { city: 'New York', version: { increment: 1 } }, + }) +} + +function main() { + await Promise.allSettled([updateOne(), updateTwo()]) +} +``` + +### Permission checks + +You can filter on non-unique fields to check permissions during an update. + +In the following example, a user wants to update a post title. The `where` statement checks the value in `authorId` to confirm that the user is the author of the post. The application only updates the post title if the user is the post author. + +```ts +await prisma.post.update({ + where: { id: 1, authorId: 1 }, + data: { title: 'Updated post title' }, +}) +``` + +### Soft deletes + +You can filter on non-unique fields to handle soft deletes. + +In the following example, we do not want to return a post if it is soft-deleted. The operation only returns the post if the value in `isDeleted` is `false`. + +```ts +prisma.Post.findUnique({ where: { id: postId, isDeleted: false } }) +``` + +### `UserWhereUniqueInput` considerations + +#### Boolean operators with `UserWhereUniqueInput` + +With `UserWhereUniqueInput`, you must specify at least one unique field outside of the boolean operators `AND`, `OR`, `NOT`. You can still use these boolean operators in conjunction with any other unique fields or non-unique fields in your filter. + +In the following example, we test `id`, a unique field, in conjunction with `email`. This is valid. + +```ts +await prisma.user.update({ + where: { id: 1, OR: [{ email: "bob@prisma.io" }, { email: "alice@prisma.io" }] }, + // ^^^ Valid: the expression specifies a unique field (`id`) outside of any boolean operators + data: { ... } +}) + +// SQL equivalent: +// WHERE id = 1 AND (email = "bob@prisma.io" OR email = "alice@prisma.io") +``` + +The following example is not valid, because there is no unique field outside of any boolean operators: + +```ts +await prisma.user.update({ + where: { OR: [{ email: "bob@prisma.io" }, { email: "alice@prisma.io" }] }, + // ^^^ Invalid: the expressions does not contain a unique field outside of boolean operators + data: { ... } +}) +``` + +#### One-to-one relations + +From version 4.5.0, you can filter on non-unique fields in the following operations on [one-to-one relations](/orm/prisma-schema/data-model/relations/one-to-one-relations): + +- Nested update +- Nested upsert +- Nested disconnect +- Nested delete + +Prisma Client automatically uses a unique filter to select the appropriate related record. As a result, you do not need to specify a unique filter in your `where` statement with a `WhereUniqueInput` [generated type](#generated-types-for-where). Instead, the `where` statement has a `WhereInput` generated type. You can use this to filter without the restrictions of `WhereUniqueInput`. + +##### Nested update example + +```ts +await prisma.user.update({ + where: { id: 1, }, + data: { + to_one: { + // Before Prisma version 4.5.0 + update: { field: "updated" } + // From Prisma version 4.5.0, you can also do the following: + update: { where: { /*WhereInput*/ }, data: { field: "updated" } } } + } + } +}) +``` + +##### Nested upsert example + +```ts +await prisma.user.update({ + where: { id: 1, }, + data: { + to_one: { + upsert: { + where: { /* WhereInput */ } // new argument from Prisma 4.5.0 + create: { /* CreateInput */ }, + update: { /* CreateInput */ }, + } + } + } +}) +``` + +##### Nested disconnect example + +```ts +await prisma.user.update({ + where: { id: 1, }, + data: { + to_one: { + // Before Prisma version 4.5.0 + disconnect: true + // From Prisma version 4.5.0, you can also do the following: + disconnect: { /* WhereInput */ } + } + } +}) +``` + +##### Nested delete example + +```ts +await prisma.user.update({ + where: { id: 1, }, + data: { + to_one: { + // Before Prisma version 4.5.0 + delete: true + // From Prisma version 4.5.0, you can also do the following: + delete: { /* WhereInput */ } + } + } +}) +``` + +## `PrismaPromise` behavior + +All Prisma Client queries return an instance of `PrismaPromise`. This is a ["thenable"](https://masteringjs.io/tutorials/fundamentals/thenable), meaning a `PrismaPromise` only executes when you call `await` or `.then()` or `.catch()`. This behavior is different from a regular JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), which starts executing immediately. + +For example: + +```ts +const findPostOperation = prisma.post.findMany({}) // Query not yet executed + +findPostOperation.then() // Prisma Client now executes the query +// or +await findPostOperation // Prisma Client now executes the query +``` + +When using the [`$transaction` API](/orm/prisma-client/queries/transactions#the-transaction-api), this behavior makes it possible for Prisma Client to pass all the queries on to the query engine as a single transaction. diff --git a/docs/200-orm/500-reference/100-prisma-schema-reference.mdx b/docs/200-orm/500-reference/100-prisma-schema-reference.mdx new file mode 100644 index 0000000000..adcd31a74f --- /dev/null +++ b/docs/200-orm/500-reference/100-prisma-schema-reference.mdx @@ -0,0 +1,3313 @@ +--- +title: 'Prisma schema reference' +navTitle: 'Prisma Schema' +metaTitle: 'Prisma Schema API' +metaDescription: 'API reference documentation for the Prisma Schema Language (PSL).' +tocDepth: 3 +toc: true +--- + +## `datasource` + +Defines a [data source](/orm/prisma-schema/overview/data-sources) in the Prisma schema. + +### Fields + +A `datasource` block accepts the following fields: + +| Name | Required | Type | Description | +| ------------------- | -------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `provider` | **Yes** | String (`postgresql`, `mysql`, `sqlite`, `sqlserver`, `mongodb`, `cockroachdb`) | Describes which data source connectors to use. | +| `url` | **Yes** | String (URL) | Connection URL including authentication info. Most connectors use [the syntax provided by the database](/orm/reference/connection-urls#format). | +| `shadowDatabaseUrl` | No | String (URL) | Connection URL to the shadow database used by Prisma Migrate. Allows you to use a cloud-hosted database as the shadow database. | +| `directUrl` | No | String (URL) | Connection URL for direct connection to the database.

If you use a connection pooler URL in the `url` argument (for example, if you use [Prisma Accelerate](/accelerate) or pgBouncer), Prisma CLI commands that require a direct connection to the database use the URL in the `directUrl` argument.

The `directUrl` property is supported by Prisma Studio from version 5.1.0 upwards. | +| `relationMode` | No | String (`foreignKeys`, `prisma`) | Sets whether [referential integrity](/orm/prisma-schema/data-model/relations/relation-mode) is enforced by foreign keys in the database or emulated in the Prisma Client.

In preview in versions 3.1.1 and later. The field is named `relationMode` in versions 4.5.0 and later, and was previously named `referentialIntegrity`. | +| `extensions` | No | List of strings (PostgreSQL extension names) | Allows you to [represent PostgreSQL extensions in your schema](/orm/prisma-schema/postgresql-extensions#how-to-represent-postgresql-extensions-in-your-prisma-schema). Available in preview for PostgreSQL only in Prisma versions 4.5.0 and later. | + +The following providers are available: + +- [`sqlite`](/orm/overview/databases/sqlite) +- [`postgresql`](/orm/overview/databases/postgresql) +- [`mysql`](/orm/overview/databases/mysql) +- [`sqlserver`](/orm/overview/databases/sql-server) +- [`mongodb`](/orm/overview/databases/mongodb) +- [`cockroachdb`](/orm/overview/databases/cockroachdb) + +### Remarks + +- You can only have **one** `datasource` block in a schema. +- `datasource db` is convention - however, you can give your data source any name - for example, `datasource mysql` or `datasource data`. + +### Examples + +#### Specify a PostgreSQL data source + +In this example, the target database is available with the following credentials: + +- User: `johndoe` +- Password: `mypassword` +- Host: `localhost` +- Port: `5432` +- Database name: `mydb` +- Schema name: `public` + +```prisma +datasource db { + provider = "postgresql" + url = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public" +} +``` + +Learn more about PostgreSQL connection strings [here](/orm/overview/databases/postgresql). + +#### Specify a PostgreSQL data source via an environment variable + +In this example, the target database is available with the following credentials: + +- User: `johndoe` +- Password: `mypassword` +- Host: `localhost` +- Port: `5432` +- Database name: `mydb` +- Schema name: `public` + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +When running a Prisma CLI command that needs the database connection URL (e.g. `prisma generate`), you need to make sure that the `DATABASE_URL` environment variable is set. + +One way to do so is by creating a [`.env`](https://github.com/motdotla/dotenv) file with the following contents. Note that the file must be in the same directory as your `schema.prisma` file to automatically picked up the Prisma CLI. + +``` +DATABASE_URL=postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public +``` + +#### Specify a MySQL data source + +In this example, the target database is available with the following credentials: + +- User: `johndoe` +- Password: `mypassword` +- Host: `localhost` +- Port: `3306` +- Database name: `mydb` + +```prisma +datasource db { + provider = "mysql" + url = "mysql://johndoe:mypassword@localhost:3306/mydb" +} +``` + +Learn more about MySQL connection strings [here](/orm/overview/databases/mysql). + +#### Specify a MongoDB data source + +- User: `root` +- Password: `password` +- Host: `cluster1.test1.mongodb.net` +- Port: N/A +- Database name: `testing` + +```prisma +datasource db { + provider = "mongodb" + url = "mongodb+srv://root:password@cluster1.test1.mongodb.net/testing?retryWrites=true&w=majority" +} +``` + +Learn more about MongoDB connection strings [here](/orm/overview/databases/mongodb). + +#### Specify a SQLite data source + +In this example, the target database is located in a file called `dev.db`: + +```prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} +``` + +Learn more about SQLite connection strings [here](/orm/overview/databases/sqlite). + +#### Specify a CockroachDB data source + +In this example, the target database is available with the following credentials: + +- User: `johndoe` +- Password: `mypassword` +- Host: `localhost` +- Port: `26257` +- Database name: `mydb` +- Schema name: `public` + +```prisma +datasource db { + provider = "cockroachdb" + url = "postgresql://johndoe:mypassword@localhost:26257/mydb?schema=public" +} +``` + +The format for connection strings is the same as for PostgreSQL. Learn more about PostgreSQL connection strings [here](/orm/overview/databases/postgresql). + +## `generator` + +Defines a [generator](/orm/prisma-schema/overview/generators) in the Prisma schema. + +### Fields + +A `generator` block accepts the following fields: + +| Name | Required | Type | Description | +| ----------------- | -------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| `provider` | **Yes** | String (file path) or Enum (`prisma-client-js`) | Describes which [generator](/orm/prisma-schema/overview/generators) to use. This can point to a file that implements a generator or specify a built-in generator directly. | +| `output` | No | String (file path) | Determines the location for the generated client, [learn more](/orm/prisma-client/setup-and-configuration/generating-prisma-client#using-a-custom-output-path). **Default**: `node_modules/.prisma/client` | +| `previewFeatures` | No | List of Enums | Use intellisense to see list of currently available Preview features (`Ctrl+Space` in Visual Studio Code) **Default**: none | | +| `engineType` | No | Enum (`library` or `binary`) | Defines the [query engine](/orm/more/under-the-hood/engines) type to download and use. **Default**: `library` | +| `binaryTargets` | No | List of Enums (see below) | Specify the OS on which the Prisma Client will run to ensure compatibility of the [query engine](/orm/more/under-the-hood/engines). **Default**: `native` | + +#### `binaryTargets` options + +The following tables list all supported operating systems with the name of platform to specify in [`binaryTargets`](/orm/prisma-schema/overview/generators#binary-targets). + +Unless specified otherwise, the default supported CPU architecture is x86_64. + +##### macOS + +| Build OS | Prisma engine build name | +| :----------------- | :----------------------- | +| macOS Intel x86_64 | `darwin` | +| macOS ARM64 | `darwin-arm64` | + +##### Windows + +| Build OS | Prisma engine build name | +| :------- | :----------------------- | +| Windows | `windows` | + +##### Linux (Alpine on x86_64 architectures) + +| Build OS | Prisma engine build name | OpenSSL | +| :---------------------- | :--------------------------- | :-----: | +| Alpine (3.17 and newer) | `linux-musl-openssl-3.0.x`\* | 3.0.x | +| Alpine (3.16 and older) | `linux-musl` | 1.1.x | + +\* Available in Prisma versions 4.8.0 and later. + +##### Linux (Alpine on ARM64 architectures) + +| Build OS | Prisma engine build name | OpenSSL | +| :---------------------- | :--------------------------------- | :-----: | +| Alpine (3.17 and newer) | `linux-musl-arm64-openssl-3.0.x`\* | 3.0.x | +| Alpine (3.16 and older) | `linux-musl-arm64-openssl-1.1.x`\* | 1.1.x | + +\* Available in Prisma versions 4.10.0 and later. + +##### Linux (Debian), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :------------------- | :----------------------- | :-----: | +| Debian 8 (Jessie) | `debian-openssl-1.0.x` | 1.0.x | +| Debian 9 (Stretch) | `debian-openssl-1.1.x` | 1.1.x | +| Debian 10 (Buster) | `debian-openssl-1.1.x` | 1.1.x | +| Debian 11 (Bullseye) | `debian-openssl-1.1.x` | 1.1.x | +| Debian 12 (Bookworm) | `debian-openssl-3.0.x` | 3.0.x | + +##### Linux (Ubuntu), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :--------------------- | :----------------------- | :-----: | +| Ubuntu 14.04 (trusty) | `debian-openssl-1.0.x` | 1.0.x | +| Ubuntu 16.04 (xenial) | `debian-openssl-1.0.x` | 1.0.x | +| Ubuntu 18.04 (bionic) | `debian-openssl-1.1.x` | 1.1.x | +| Ubuntu 19.04 (disco) | `debian-openssl-1.1.x` | 1.1.x | +| Ubuntu 20.04 (focal) | `debian-openssl-1.1.x` | 1.1.x | +| Ubuntu 21.04 (hirsute) | `debian-openssl-1.1.x` | 1.1.x | +| Ubuntu 22.04 (jammy) | `debian-openssl-3.0.x` | 3.0.x | +| Ubuntu 23.04 (lunar) | `debian-openssl-3.0.x` | 3.0.x | + +##### Linux (CentOS), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :------- | :----------------------- | :-----: | +| CentOS 7 | `rhel-openssl-1.0.x` | 1.0.x | +| CentOS 8 | `rhel-openssl-1.1.x` | 1.1.x | + +##### Linux (Fedora), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :-------- | :----------------------- | :-----: | +| Fedora 28 | `rhel-openssl-1.1.x` | 1.1.x | +| Fedora 29 | `rhel-openssl-1.1.x` | 1.1.x | +| Fedora 30 | `rhel-openssl-1.1.x` | 1.1.x | +| Fedora 36 | `rhel-openssl-3.0.x` | 3.0.x | +| Fedora 37 | `rhel-openssl-3.0.x` | 3.0.x | +| Fedora 38 | `rhel-openssl-3.0.x` | 3.0.x | + +##### Linux (Linux Mint), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :------------ | :----------------------- | :-----: | +| Linux Mint 18 | `debian-openssl-1.0.x` | 1.0.x | +| Linux Mint 19 | `debian-openssl-1.1.x` | 1.1.x | +| Linux Mint 20 | `debian-openssl-1.1.x` | 1.1.x | +| Linux Mint 21 | `debian-openssl-3.0.x` | 3.0.x | + +##### Linux (Arch Linux), x86_64 + +| Build OS | Prisma engine build name | OpenSSL | +| :-------------------- | :----------------------- | :-----: | +| Arch Linux 2019.09.01 | `debian-openssl-1.1.x` | 1.1.x | +| Arch Linux 2023.04.23 | `debian-openssl-3.0.x` | 3.0.x | + +##### Linux ARM64 (all major distros but Alpine) + +| Build OS | Prisma engine build name | OpenSSL | +| :----------------------------- | :-------------------------- | :-----: | +| Linux ARM64 glibc-based distro | `linux-arm64-openssl-1.0.x` | 1.0.x | +| Linux ARM64 glibc-based distro | `linux-arm64-openssl-1.1.x` | 1.1.x | +| Linux ARM64 glibc-based distro | `linux-arm64-openssl-3.0.x` | 3.0.x | + +### Examples + +#### Specify the `prisma-client-js` generator with the default `output`, `previewFeatures`, `engineType` and `binaryTargets` + +```prisma +generator client { + provider = "prisma-client-js" +} +``` + +Note that the above `generator` definition is **equivalent** to the following because it uses the default values for `output`, `engineType` and `binaryTargets` (and implicitly `previewFeatures`): + +```prisma +generator client { + provider = "prisma-client-js" + output = "node_modules/.prisma/client" + engineType = "library" + binaryTargets = ["native"] +} +``` + +#### Specify a custom `output` location for Prisma Client + +This example shows how to define a custom `output` location of the generated asset to override the default one. + +```prisma +generator client { + provider = "prisma-client-js" + output = "../src/generated/client" +} +``` + +#### Specify custom `binaryTargets` to ensure compatibility with the OS + +This example shows how to configure Prisma Client to run on `Ubuntu 19.04 (disco)` based on the table [above](#linux-ubuntu-x86_64). + +```prisma +generator client { + provider = "prisma-client-js" + binaryTargets = ["debian-openssl-1.1.x"] +} +``` + +#### Specify a `provider` pointing to some custom generator implementation + +This example shows how to use a custom generator that's located in a directory called `my-generator`. + +```prisma +generator client { + provider = "./my-generator" +} +``` + +## `model` + +Defines a Prisma [model](/orm/prisma-schema/data-model/models#defining-models) . + +### Remarks + +- Every record of a model must be _uniquely_ identifiable. You must define _at least_ one of the following attributes per model: + - [`@unique`](#unique) + - [`@@unique`](#unique-1) + - [`@id`](#id) + - [`@@id`](#id-1) + +#### Naming conventions + +- Model names must adhere to the following regular expression: `[A-Za-z][A-Za-z0-9_]*` +- Model names must start with a letter and are typically spelled in [PascalCase](https://wiki.c2.com/?PascalCase) +- Model names should use the singular form (for example, `User` instead of `user`, `users` or `Users`) +- Prisma has a number of **reserved words** that are being used by Prisma internally and therefore cannot be used as a model name. You can find the reserved words [here](https://github.com/prisma/prisma/blob/main/packages/client/src/generation/generateClient.ts#L376) and [here](https://github.com/prisma/prisma-engines/blob/main/psl/parser-database/src/names/reserved_model_names.rs#L44). + +> **Note**: You can use the [`@@map` attribute](#map-1) to map a model (for example, `User`) to a table with a different name that does not match model naming conventions (for example, `users`). + +#### Order of fields + +- In version 2.3.0 and later, introspection lists model fields in the same order as the corresponding columns in the database. Relation fields are listed after scalar fields. + +### Examples + +#### A model named `User` with two scalar fields + + + + +```prisma +model User { + email String @unique // `email` can not be optional because it's the only unique field on the model + name String? +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? +} +``` + + + + +## `model` fields + +[Fields](/orm/prisma-schema/data-model/models#defining-fields) are properties of models. + +### Remarks + +#### Naming conventions + +- Must start with a letter +- Typically spelled in camelCase +- Must adhere to the following regular expression: `[A-Za-z][A-Za-z0-9_]*` + +> **Note**: You can use the [`@map` attribute](#map) to [map a field name to a column](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) with a different name that does not match field naming conventions: e.g. `myField @map("my_field")`. + +## `model` field scalar types + +The _data source connector_ determines what _native database type_ each of Prisma scalar type maps to. Similarly, the _generator_ determines what _type in the target programming language_ each of these types map to. + +Prisma models also have [model field types](/orm/prisma-schema/data-model/relations) that define relations between models. + +### `String` + +Variable length text. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | ---------------- | +| PostgreSQL | `text` | +| SQL Server | `nvarchar(1000)` | +| MySQL | `varchar(191)` | +| MongoDB | `String` | +| SQLite | `TEXT` | +| CockroachDB | `STRING` | + +#### PostgreSQL + +| Native database type | Native database type attribute | Notes | +| :------------------- | :----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `text` | `@db.Text` | | +| `char(x)` | `@db.Char(x)` | +| `varchar(x)` | `@db.VarChar(x)` | +| `bit(x)` | `@db.Bit(x)` | +| `varbit` | `@db.VarBit` | +| `uuid` | `@db.Uuid` | +| `xml` | `@db.Xml` | +| `inet` | `@db.Inet` | +| `citext` | `@db.Citext` | Only available if [Citext extension is enabled](/orm/prisma-schema/data-model/unsupported-database-features#enable-postgresql-extensions-for-native-database-functions). | + +#### MySQL + +| Native database type | Native database type attribute | Notes | +| :------------------- | :----------------------------- | ----- | +| `VARCHAR(x)` | `@db.VarChar(x)` | | +| `TEXT` | `@db.Text` | +| `CHAR(x)` | `@db.Char(x)` | +| `TINYTEXT` | `@db.TinyText` | +| `MEDIUMTEXT` | `@db.MediumText` | +| `LONGTEXT` | `@db.LongText` | + +You can use Prisma Migrate to map `@db.Bit(1)` to `String`: + +```prisma +model Model { + /* ... */ + myField String @db.Bit(1) +} +``` + +#### MongoDB + +`String` + +| Native database type attribute | Notes | +| :----------------------------- | :-------------------------------------------------------------------------------- | +| `@db.String` | | +| `@db.ObjectId` | Required if the underlying BSON type is `OBJECT_ID` (ID fields, relation scalars) | + +#### Microsoft SQL Server + +| Native database type | Native database type attribute | Notes | +| :------------------- | :----------------------------- | ----- | +| `char(x)` | `@db.Char(x)` | +| `nchar(x)` | `@db.NChar(x)` | +| `varchar(x)` | `@db.VarChar(x)` | +| `nvarchar(x)` | `@db.NVarChar(x)` | | +| `text` | `@db.Text` | +| `ntext` | `@db.NText` | +| `xml` | `@db.Xml` | +| `uniqueidentifier` | `@db.UniqueIdentifier` | + +#### SQLite + +`TEXT` + +#### CockroachDB + +| Native database type | Native database type attribute | Notes | +| :--------------------------------------- | :----------------------------- | ----- | +| `STRING(x)` \| `TEXT(x)` \| `VARCHAR(x)` | `@db.String(x)` | | +| `CHAR(x)` | `@db.Char(x)` | | +| `"char"` | `@db.CatalogSingleChar` | | +| `BIT(x)` | `@db.Bit(x)` | | +| `VARBIT` | `@db.VarBit` | | +| `UUID` | `@db.Uuid` | | +| `INET` | `@db.Inet` | | + +Note that the `xml` and `citext` types supported in PostgreSQL are not currently supported in CockroachDB. + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `string` | + +### `Boolean` + +True or false value. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | --------------- | +| PostgreSQL | `boolean` | +| SQL Server | `tinyint` | +| MySQL | `TINYINT(1)` | +| MongoDB | `Bool` | +| SQLite | `INTEGER` | +| CockroachDB | `BOOL` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| :-------------------- | :----------------------------- | ----- | +| `boolean` | `@db.Boolean` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TINYINT(1)` | `@db.TinyInt(1)` | `TINYINT` maps to `Int` if the max length is greater than 1 (for example, `TINYINT(2)`) _or_ the default value is anything other than `1`, `0`, or `NULL` | +| `BIT(1)` | `@db.Bit` | + +#### MongoDB + +`Bool` + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| :-------------------- | :----------------------------- | ----- | +| `bit` | `@db.Bit` | | + +#### SQLite + +`INTEGER` + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| :-------------------- | :----------------------------- | ----- | +| `BOOL` | `@db.Bool` | | + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `boolean` | + +### `Int` + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | --------------- | +| PostgreSQL | `integer` | +| SQL Server | `int` | +| MySQL | `INT` | +| MongoDB | `Int` | +| SQLite | `INTEGER` | +| CockroachDB | `INT` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| -------------------------- | ---------------------------------------- | ----- | +| `integer` \| `int`, `int4` | `@db.Integer` | | +| `smallint` \| `int2` | `@db.SmallInt` | | +| `smallserial` \| `serial2` | `@db.SmallInt @default(autoincrement())` | | +| `serial` \| `serial4` | `@db.Int @default(autoincrement())` | | +| `oid` | `@db.Oid` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| :-------------------- | :----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `INT` | `@db.Int` | | +| `INT UNSIGNED` | `@db.UnsignedInt` | | +| `SMALLINT` | `@db.SmallInt` | +| `SMALLINT UNSIGNED` | `@db.UnsignedSmallInt` | +| `MEDIUMINT` | `@db.MediumInt` | +| `MEDIUMINT UNSIGNED` | `@db.UnsignedMediumInt` | +| `TINYINT` | `@db.TinyInt` | `TINYINT` maps to `Int` if the max length is greater than 1 (for example, `TINYINT(2)`) _or_ the default value is anything other than `1`, `0`, or `NULL`. `TINYINT(1)` maps to `Boolean`. | +| `TINYINT UNSIGNED` | `@db.UnsignedTinyInt` | `TINYINT(1) UNSIGNED` maps to `Int`, not `Boolean` | +| `YEAR` | `@db.Year` | + +#### MongoDB + +`Int` + +| Native database type attribute | Notes | +| :----------------------------- | :---- | +| `@db.Int` | | +| `@db.Long` | | + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `int` | `@db.Int` | | +| `smallint` | `@db.SmallInt` | | +| `tinyint` | `@db.TinyInt` | | +| `bit` | `@db.Bit` | + +#### SQLite + +`INTEGER` + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| ---------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| `INTEGER` \| `INT` \| `INT8` | `@db.Int8` | Note that this differs from PostgreSQL, where `integer` and `int` are aliases for `int4` and map to `@db.Integer` | +| `INT4` | `@db.Int4` | | +| `INT2` \| `SMALLINT` | `@db.Int2` | | +| `SMALLSERIAL` \| `SERIAL2` | `@db.Int2 @default(autoincrement())` | | +| `SERIAL` \| `SERIAL4` | `@db.Int4 @default(autoincrement())` | | +| `SERIAL8` \| `BIGSERIAL` | `@db.Int8 @default(autoincrement())` | | + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `number` | + +### `BigInt` + +`BigInt` is available in version [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | --------------- | +| PostgreSQL | `bigint` | +| SQL Server | `int` | +| MySQL | `BIGINT` | +| MongoDB | `Long` | +| SQLite | `INTEGER` | +| CockroachDB | `INTEGER` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| ------------------------ | -------------------------------------- | ----- | +| `bigint` \| `int8` | `@db.BigInt` | | +| `bigserial` \| `serial8` | `@db.BigInt @default(autoincrement())` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ---------------------------------------------- | ----- | +| `BIGINT` | `@db.BigInt` | | +| `SERIAL` | `@db.UnsignedBigInt @default(autoincrement())` | | + +#### MongoDB + +`Long` + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `bigint` | `@db.BigInt` | | + +#### SQLite + +`INTEGER` + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| --------------------------- | ------------------------------------ | -------------------------------------------------------------------------- | +| `BIGINT` \| `INT` \| `INT8` | `@db.Int8` | Note that this differs from PostgreSQL, where `int` is an alias for `int4` | +| `bigserial` \| `serial8` | `@db.Int8 @default(autoincrement())` | | + +#### Clients + +| Client | Type | Description | +| :--------------- | :-------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | +| Prisma Client JS | [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) | See [examples of working with `BigInt`](/orm/prisma-client/special-fields-and-types#working-with-bigint) | + +### `Float` + +Floating point number. + +> `Float` maps to `Double` in [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later - see [release notes](https://github.com/prisma/prisma/releases/tag/2.17.0) and [Video: Changes to the default mapping of Float in Prisma 2.17.0](https://www.youtube.com/watch?v=OsuGP_xNHco&%3Bab_channel=Prisma) for more information about this change. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | ------------------ | +| PostgreSQL | `double precision` | +| SQL Server | `float(53)` | +| MySQL | `DOUBLE` | +| MongoDB | `Double` | +| SQLite | `REAL` | +| CockroachDB | `DOUBLE PRECISION` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `double precision` | `@db.DoublePrecision` | | +| `real` | `@db.Real` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `FLOAT` | `@db.Float` | +| `DOUBLE` | `@db.Double` | + +#### MongoDB + +`Double` + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | +| --------------------- | ------------------------------ | +| `float` | `@db.Float` | +| `money` | `@db.Money` | +| `smallmoney` | `@db.SmallMoney` | +| `real` | `@db.Real` | + +#### SQLite connector + +`REAL` + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| ------------------------------ | ------------------------------ | ----- | +| `DOUBLE PRECISION` \| `FLOAT8` | `@db.Float8` | | +| `REAL` \| `FLOAT4` \| `FLOAT` | `@db.Float4` | | + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `number` | + +### `Decimal` + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | -------------------------------------------------------------- | +| PostgreSQL | `decimal(65,30)` | +| SQL Server | `decimal(32,16)` | +| MySQL | `DECIMAL(65,30)` | +| MongoDB | [Not supported](https://github.com/prisma/prisma/issues/12637) | +| SQLite | `DECIMAL` | +| CockroachDB | `DECIMAL` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| ---------------------- | ------------------------------ | ----- | +| `decimal` \| `numeric` | `@db.Decimal(p, s)`† | | +| `money` | `@db.Money` | | + +- † `p` (precision), the maximum total number of decimal digits to be stored. `s` (scale), the number of decimal digits that are stored to the right of the decimal point. + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| ---------------------- | ------------------------------ | ----- | +| `DECIMAL` \| `NUMERIC` | `@db.Decimal(p, s)`† | | + +- † `p` (precision), the maximum total number of decimal digits to be stored. `s` (scale), the number of decimal digits that are stored to the right of the decimal point. + +#### MongoDB + +[Not supported](https://github.com/prisma/prisma/issues/12637). + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| ---------------------- | ------------------------------ | ----- | +| `decimal` \| `numeric` | `@db.Decimal(p, s)`† | | + +- † `p` (precision), the maximum total number of decimal digits to be stored. `s` (scale), the number of decimal digits that are stored to the right of the decimal point. + +#### SQLite + +`DECIMAL` (changed from `REAL` in 2.17.0) + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| ------------------------------- | ------------------------------ | ------------------------------------------------------------- | +| `DECIMAL` \| `DEC` \| `NUMERIC` | `@db.Decimal(p, s)`† | | +| `money` | Not yet | PostgreSQL's `money` type is not yet supported by CockroachDB | + +- † `p` (precision), the maximum total number of decimal digits to be stored. `s` (scale), the number of decimal digits that are stored to the right of the decimal point. + +#### Clients + +| Client | Type | Description | +| :--------------- | :------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | +| Prisma Client JS | [`Decimal`](https://mikemcl.github.io/decimal.js/) | See [examples of working with `Decimal`](/orm/prisma-client/special-fields-and-types#working-with-decimal) | + +### `DateTime` + +#### Remarks + +- Prisma Client returns all `DateTime` as native [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) objects. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | --------------- | +| PostgreSQL | `timestamp(3)` | +| SQL Server | `datetime2` | +| MySQL | `DATETIME(3)` | +| MongoDB | `Timestamp` | +| SQLite | `NUMERIC` | +| CockroachDB | `TIMESTAMP` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `timestamp(x)` | `@db.Timestamp(x)` | | +| `timestamptz(x)` | `@db.Timestamptz(x)` | | +| `date` | `@db.Date` | | +| `time(x)` | `@db.Time(x)` | | +| `timetz(x)` | `@db.Timetz(x)` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `DATETIME(x)` | `@db.DateTime(x)` | | +| `DATE(x)` | `@db.Date(x)` | +| `TIME(x)` | `@db.Time(x)` | +| `TIMESTAMP(x)` | `@db.Timestamp(x)` | + +You can also use MySQL's `YEAR` type with `Int`: + +```prisma +yearField Int @db.Year +``` + +#### MongoDB + +`Timestamp` + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `date` | `@db.Date` | +| `time` | `@db.Time` | +| `datetime` | `@db.DateTime` | +| `datetime2` | `@db.DateTime2` | | +| `smalldatetime` | `@db.SmallDateTime` | +| `datetimeoffset` | `@db.DateTimeOffset` | + +#### SQLite + +`NUMERIC` or `STRING`. If the underlying data type is `STRING`, you must use one of the following formats: + +- [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) (`1996-12-19T16:39:57-08:00`) +- [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3) (`Tue, 1 Jul 2003 10:52:37 +0200`) + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `TIMESTAMP(x)` | `@db.Timestamp(x)` | | +| `TIMESTAMPTZ(x)` | `@db.Timestamptz(x)` | | +| `DATE` | `@db.Date` | | +| `TIME(x)` | `@db.Time(x)` | | +| `TIMETZ(x)` | `@db.Timetz(x)` | | + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `Date` | + +### `Json` + +A JSON object. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | -------------------------------------------------------------------------------------------------------- | +| PostgreSQL | `jsonb` | +| SQL Server | [Not supported](https://github.com/prisma/prisma/issues/7417) | +| MySQL | `JSON` | +| MongoDB | [A valid `BSON` object (Relaxed mode)](https://docs.mongodb.com/manual/reference/mongodb-extended-json/) | +| SQLite | Not supported | +| CockroachDB | `JSONB` | + +#### PostgreSQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `json` | `@db.Json` | +| `jsonb` | `@db.JsonB` | | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `JSON` | `@db.Json` | + +#### MongoDB + +[A valid `BSON` object (Relaxed mode)](https://docs.mongodb.com/manual/reference/mongodb-extended-json/) + +#### Microsoft SQL Server + +Microsoft SQL Server does not have a specific data type for JSON - however, there are a number of [built-in functions for reading and modifying JSON](https://docs.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver15#extract-values-from-json-text-and-use-them-in-queries). + +| Native database types | Native database type attribute | +| --------------------- | ------------------------------ | +| `JSON` | `@db.NVarChar` | + +#### SQLite + +Not supported + +#### CockroachDB + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `JSON` \| `JSONB` | `@db.JsonB` | | + +#### Clients + +| Prisma Client JS | +| ---------------- | +| `object` | + +### `Bytes` + +`Bytes` is available in version [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later. + +#### Default type mappings + +| Connector | Default mapping | +| ----------- | --------------- | +| PostgreSQL | `bytea` | +| SQL Server | `varbinary` | +| MySQL | `LONGBLOB` | +| MongoDB | `BinData` | +| SQLite | `BLOB` | +| CockroachDB | `BYTES` | + +#### PostgreSQL + +| Native database types | Native database type attribute | +| --------------------- | ------------------------------ | +| `bytea` | `@db.ByteA` | + +#### MySQL + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `LONGBLOB` | `@db.LongBlob` | | +| `BINARY` | `@db.Binary` | +| `VARBINARY` | `@db.VarBinary` | +| `TINYBLOB` | `@db.TinyBlob` | +| `BLOB` | `@db.Blob` | +| `MEDIUMBLOB` | `@db.MediumBlob` | +| `BIT` | `@db.Bit` | + +#### MongoDB + +`BinData` + +| Native database type attribute | Notes | +| :----------------------------- | :-------------------------------------------------------------------------------- | +| `@db.ObjectId` | Required if the underlying BSON type is `OBJECT_ID` (ID fields, relation scalars) | +| `@db.BinData` | | + +#### Microsoft SQL Server + +| Native database types | Native database type attribute | Notes | +| --------------------- | ------------------------------ | ----- | +| `binary` | `@db.Binary` | +| `varbinary` | `@db.VarBinary` | | +| `image` | `@db.Image` | + +#### SQLite + +`BLOB` + +#### CockroachDB + +| Native database types | Native database type attribute | +| ---------------------------- | ------------------------------ | +| `BYTES` \| `BYTEA` \| `BLOB` | `@db.Bytes` | + +#### Clients + +| Client | Type | Description | +| :--------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| Prisma Client JS | [`Buffer`](https://nodejs.org/api/buffer.html) | See [examples of working with `Buffer`](/orm/prisma-client/special-fields-and-types#working-with-bytes) | + +### `Unsupported` + + + +**Not supported by MongoDB**
+The [MongoDB connector](/orm/overview/databases/mongodb) does not support the `Unsupported` type. + +
+ +The `Unsupported` type was introduced in [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and allows you to represent data types in the Prisma schema that are not supported by Prisma Client. Fields of type `Unsupported` can be created during Introspection with `prisma db pull` or written by hand, and created in the database with Prisma Migrate or `db push`. + +#### Remarks + +- Fields with `Unsupported` types are not available in the generated client. +- If a model contains a **required** `Unsupported` type, `prisma.model.create(..)`, `prisma.model.update(...)` and `prisma.model.upsert(...)` are not available in Prisma Client. +- When you introspect a database that contains unsupported types, Prisma will provide the following warning: + + ``` + *** WARNING *** + + These fields are not supported by Prisma Client, because Prisma does not currently support their types. + * Model "Post", field: "circle", original data type: "circle" + ``` + +#### Examples + +```prisma +model Star { + id Int @id @default(autoincrement()) + position Unsupported("circle")? + example1 Unsupported("circle") + circle Unsupported("circle")? @default(dbgenerated("'<(10,4),11>'::circle")) +} +``` + +## `model` field type modifiers + +### `[]` modifier + +Makes a field a list. + +#### Remarks + +- Cannot be optional (for example `Post[]?`). + +##### Relational databases + +- Scalar lists (arrays) are only supported in the data model if your database natively supports them. Currently, scalar lists are therefore only supported when using PostgreSQL or CockroachDB (since MySQL and SQLite don't natively support scalar lists). + +##### MongoDB + +- Scalar lists are supported + +#### Examples + +##### Define a scalar list + + + + +```prisma highlight=3;normal +model User { + id Int @id @default(autoincrement()) + favoriteColors String[] +} +``` + + + + +```prisma highlight=3;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + favoriteColors String[] +} +``` + + + + +##### Define a scalar list with a default value + +Available in version 4.0.0 and later. + + + + +```prisma highlight=3;normal +model User { + id Int @id @default(autoincrement()) + favoriteColors String[] @default(["red", "blue", "green"]) +} +``` + + + + +```prisma highlight=3;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + favoriteColors String[] @default(["red", "blue", "green"]) +} +``` + + + + +### `?` modifier + +Makes a field optional. + +#### Remarks + +- Cannot be used with a list field (for example, `Posts[]`) + +#### Examples + +##### Optional `name` field + +```prisma highlight=3;normal +model User { + id Int @id @default(autoincrement()) + name String? +} +``` + +## Attributes + +Attributes modify the behavior of a [field](#model-fields) or block (e.g. [models](#model)). There are two ways to add attributes to your data model: + +- _Field_ attributes are prefixed with `@` +- _Block_ attributes are prefixed with `@@` + +Some attributes take arguments. Arguments in attributes are always named, but in most cases the argument _name_ can be omitted. + +> **Note**: The leading underscore in a signature means the _argument name_ can be omitted. + +### `@id` + +Defines a single-field ID on the model. + +#### Remarks + +##### General + +- Cannot be defined on a relation field +- Cannot be optional + +##### Relational databases + +- Corresponding database type: `PRIMARY KEY` +- Can be annotated with a [`@default()`](#default) value that uses [functions](#attribute-functions) to auto-generate an ID: + + - [`autoincrement()`](#autoincrement) + - [`cuid()`](#cuid) + - [`uuid()`](#uuid) + +- Can be defined on any scalar field (`String`, `Int`, `enum`) + +##### MongoDB + +- Corresponding database type: [Any valid BSON type, except arrays](https://docs.mongodb.com/manual/core/document/#the-_id-field) +- Every model must define an `@id` field +- The [underlying ID field name is always `_id`](https://docs.mongodb.com/manual/core/document/#the-_id-field), and must be mapped with `@map("_id")` +- Can be defined on any scalar field (`String`, `Int`, `enum`) unless you want to use `ObjectId` in your database +- To use an [`ObjectId`](https://docs.mongodb.com/manual/reference/method/ObjectId/) as your ID, you must: + + - Use the `String` or `Bytes` field type + - Annotate your field with `@db.ObjectId`: + + ```prisma + id String @db.ObjectId @map("_id") + ``` + + - Optionally, annotate your field with a [`@default()`](#default) value that uses [the `auto()` function](#auto) to auto-generate an `ObjectId` + + ```prisma + id String @db.ObjectId @map("_id") @default(auto()) + ``` + +- [`cuid()`](#cuid) and [`uuid()`](#uuid) are supported but do not generate a valid `ObjectId` - use `auto()` instead for `@id` +- `autoincrement()` is **not supported** + +#### Arguments + +| Name | Required | Type | Description | +| ----------- | -------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `map` | **No** | `String` | The name of the underlying primary key constraint in the database.

Not supported for MySQL or MongoDB. | +| `length` | **No** | `number` | Allows you to specify a maximum length for the subpart of the value to be indexed.

MySQL only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `sort` | **No** | `String` | Allows you to specify in what order the entries of the ID are stored in the database. The available options are `Asc` and `Desc`.

SQL Server only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `clustered` | **No** | `Boolean` | Defines whether the ID is clustered or non-clustered. Defaults to `true`.

SQL Server only. In preview in versions 3.13.0 and later, and in general availability in versions 4.0.0 and later. | + +#### Signature + +```prisma no-lines +@id(map: String?, length: number?, sort: String?, clustered: Boolean?) +``` + +> **Note**: Before version 4.0.0, or 3.5.0 with the `extendedIndexes` Preview feature enabled, the signature was: +> +> ```prisma no-lines +> @id(map: String?) +> ``` + +> **Note**: Before version 3.0.0, the signature was: +> +> ```prisma no-lines +> @id +> ``` + +#### Examples + +In most cases, you want your database to create the ID. To do this, annotate the ID field with the `@default` attribute and initialize the field with a [function](#attribute-functions). + +##### Generate autoincrementing integers as IDs (Relational databases only) + +```prisma +model User { + id Int @id @default(autoincrement()) + name String +} +``` + +##### Generate `ObjectId` as IDs (MongoDB only) + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String +} +``` + +##### Generate `cuid()` values as IDs + + + + +```prisma +model User { + id String @id @default(cuid()) + name String +} +``` + + + + +```prisma +model User { + id String @id @default(cuid()) @map("_id") + name String +} +``` + + + +You cannot use `cuid()` to generate a default value if your `id` field is of type `ObjectId`. Use the following syntax to generate a valid `ObjectId`: + +```prisma +id String @id @default(auto()) @db.ObjectId @map("_id") +``` + + + + + + +##### Generate `uuid()` values as IDs + + + + + +```prisma +model User { + id String @id @default(uuid()) + name String +} +``` + + + + + +```prisma +model User { + id String @id @default(uuid()) @map("_id") + name String +} +``` + + + +You cannot use `uuid()` to generate a default value if your `id` field is of type `ObjectId`. Use the following syntax to generate a valid `ObjectId`: + +```prisma +id String @id @default(auto()) @db.ObjectId @map("_id") +``` + + + + + + +##### Single-field IDs _without_ default values + +In the following example, `id` does not have a default value: + + + + +```prisma +model User { + id String @id + name String +} +``` + + + + +```prisma +model User { +id String @id @map("_id") @db.ObjectId +name String +} +``` + +```prisma +model User { +id String @id @map("_id") +name String +} +``` + + + + +Note that in the above case, you _must_ provide your own ID values when creating new records for the `User` model using Prisma Client, e.g.: + +```ts +const newUser = await prisma.user.create({ + data: { + id: 1, + name: 'Alice', + }, +}) +``` + +###### Specify an ID on relation scalar field without a default value + +In the following example, `authorId` is a both a relation scalar and the ID of `Profile`: + + + + +```prisma +model Profile { + authorId Int @id + author User @relation(fields: [authorId], references: [id]) + bio String +} + +model User { + id Int @id + email String @unique + name String? + profile Profile? +} +``` + + + + +```prisma +model Profile { + authorId String @id @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + bio String +} + +model User { + id String @id @map("_id") @db.ObjectId + email String @unique + name String? + profile Profile? +} +``` + + + + +In this scenario, you cannot create a `Profile` only - you must use Prisma Client's [nested writes](/orm/prisma-client/queries/relation-queries#nested-writes) create a `User` **or** connect the profile to an existing user. + +The following example creates a user and a profile: + +```ts +const userWithProfile = await prisma.user.create({ + data: { + id: 3, + email: 'bob@prisma.io', + name: 'Bob Prismo', + profile: { + create: { + bio: "Hello, I'm Bob Prismo and I love apples, blue nail varnish, and the sound of buzzing mosquitoes.", + }, + }, + }, +}) +``` + +The following example connects a new profile to a user: + +```ts +const profileWithUser = await prisma.profile.create({ + data: { + bio: "Hello, I'm Bob and I like nothing at all. Just nothing.", + author: { + connect: { + id: 22, + }, + }, + }, +}) +``` + +### `@@id` + + + +**Not supported by MongoDB**
+The [MongoDB connector](/orm/overview/databases/mongodb) does not support composite IDs. + +
+ +Defines a multi-field ID (composite ID) on the model. + +#### Remarks + +- Corresponding database type: `PRIMARY KEY` +- Can be annotated with a [`@default()`](#default) value that uses [functions](#attribute-functions) to auto-generate an ID +- Cannot be optional +- Can be defined on any scalar field (`String`, `Int`, `enum`) +- Cannot be defined on a relation field +- The name of the composite ID field in Prisma Client has the following pattern: `field1_field2_field3` + +#### Arguments + +| Name | Required | Type | Description | +| ----------- | -------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `fields` | **Yes** | `FieldReference[]` | A list of field names - for example, `["firstname", "lastname"]` | +| `name` | **No** | `String` | The name that Prisma Client will expose for the argument covering all fields, e.g. `fullName` in `fullName: { firstName: "First", lastName: "Last"}` | +| `map` | **No** | `String` | The name of the underlying primary key constraint in the database.

Not supported for MySQL. | +| `length` | **No** | `number` | Allows you to specify a maximum length for the subpart of the value to be indexed.

MySQL only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `sort` | **No** | `String` | Allows you to specify in what order the entries of the ID are stored in the database. The available options are `Asc` and `Desc`.

SQL Server only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `clustered` | **No** | `Boolean` | Defines whether the ID is clustered or non-clustered. Defaults to `true`.

SQL Server only. In preview in versions 3.13.0 and later, and in general availability in versions 4.0.0 and later. | + +The name of the `fields` argument on the `@@id` attribute can be omitted: + +```prisma no-lines +@@id(fields: [title, author]) +@@id([title, author]) +``` + +#### Signature + +```prisma no-lines +@@id(_ fields: FieldReference[], name: String?, map: String?) +``` + +> **Note**: Until version 3.0.0, the signature was: +> +> ```prisma no-lines +> @@id(_ fields: FieldReference[]) +> ``` + +#### Examples + +##### Specify a multi-field ID on two `String` fields (Relational databases only) + +```prisma +model User { + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@id([firstName, lastName]) +} +``` + +When you create a user, you must provide a unique combination of `firstName` and `lastName`: + +```ts +const user = await prisma.user.create({ + data: { + firstName: 'Alice', + lastName: 'Smith', + }, +}) +``` + +To retrieve a user, use the generated composite ID field (`firstName_lastName`): + +```ts +const user = await prisma.user.findUnique({ + where: { + firstName_lastName: { + firstName: 'Alice', + lastName: 'Smith', + }, + }, +}) +``` + +##### Specify a multi-field ID on two `String` fields and one `Boolean` field (Relational databases only) + +```prisma +model User { + firstName String + lastName String + email String @unique + isAdmin Boolean @default(false) + + @@id([firstName, lastName, isAdmin]) +} +``` + +When creating new `User` records, you now must provide a unique combination of values for `firstName`, `lastName` and `isAdmin`: + +```ts +const user = await prisma.user.create({ + data: { + firstName: 'Alice', + lastName: 'Smith', + isAdmin: true, + }, +}) +``` + +##### Specify a multi-field ID that includes a relation field (Relational databases only) + +```prisma +model Post { + title String + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int + + @@id([authorId, title]) +} + +model User { + id Int @default(autoincrement()) + email String @unique + name String? + posts Post[] +} +``` + +When creating new `Post` records, you now must provide a unique combination of values for `authorId` (foreign key) and `title`: + +```ts +const post = await prisma.post.create({ + data: { + title: 'Hello World', + author: { + connect: { + email: 'alice@prisma.io', + }, + }, + }, +}) +``` + +### `@default` + +Defines a [default value for a field](/orm/prisma-schema/data-model/models#defining-a-default-value) . + +#### Remarks + +##### Relational databases + +- Corresponding database type: `DEFAULT` +- Default values can be a static value (`4`, `"hello"`) or one of the following [functions](#attribute-functions): + + - [`autoincrement()`](#autoincrement) + - [`sequence()`](#sequence) (CockroachDB only) + - [`dbgenerated()`](#dbgenerated) + - [`cuid()`](#cuid) + - [`uuid()`](#uuid) + - [`now()`](#now) + +- Default values that cannot yet be represented in the Prisma schema are represented by the `dbgenerated()` function when you use [introspection](/orm/prisma-schema/introspection). +- Default values are not allowed on relation fields in the Prisma schema. Note however that you can still define default values on the fields backing a relation (the ones listed in the `fields` argument in the `@relation` attribute). A default value on the field backing a relation will mean that relation is populated automatically for you. +- Default values can be used with [scalar lists](/orm/prisma-client/special-fields-and-types/working-with-scalar-lists-arrays) in databases that natively support them. + +##### MongoDB + +- Default values can be a static value (`4`, `"hello"`) or one of the following [functions](#attribute-functions): + + - [`auto()`](#auto) (can only be used with `@db.ObjectId` to generate an `ObjectId` in MongoDB) + - [`cuid()`](#cuid) + - [`uuid()`](#uuid) + - [`now()`](#now) + +- Default values are currently not allowed on relation fields in the Prisma schema. +- Default values can be used with [scalar lists](/orm/prisma-client/special-fields-and-types/working-with-scalar-lists-arrays) in databases that natively support them. + +#### Arguments + +| Name | Required | Type | Description | +| ------- | -------- | ----------------------------------------- | -------------------- | +| `value` | **Yes** | An expression (e.g. `5`, `true`, `now()`) | | +| `map` | **No** | String | **SQL Server only.** | + +The name of the `value` argument on the `@default` attribute can be omitted: + +```prisma no-lines +id Int @id @default(value: autoincrement()) +id Int @id @default(autoincrement()) +``` + +#### Signature + +```prisma no-lines +@default(_ value: Expression, map: String?) +``` + +> **Note**: Until version 3.0.0, the signature was: +> +> ```prisma no-lines +> @default(_ value: Expression) +> ``` + +#### Examples + +##### Default value for an `Int` + + + + +```prisma +model User { + email String @unique + profileViews Int @default(0) +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + profileViews Int @default(0) +} +``` + + + + +##### Default value for a `Float` + + + + +```prisma +model User { + email String @unique + number Float @default(1.1) +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + number Float @default(1.1) +} +``` + + + + +##### Default value for `Decimal` + + + + +```prisma +model User { + email String @unique + number Decimal @default(22.99) +} +``` + + + + +[Not supported](https://github.com/prisma/prisma/issues/12637). + + + + +##### Default value for `BigInt` + + + + +```prisma +model User { + email String @unique + number BigInt @default(34534535435353) +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + number BigInt @default(34534535435353) +} +``` + + + + +##### Default value for a `String` + + + + +```prisma +model User { + email String @unique + name String @default("") +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + name String @default("") +} +``` + + + + +##### Default value for a `Boolean` + + + + +```prisma +model User { + email String @unique + isAdmin Boolean @default(false) +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + isAdmin Boolean @default(false) +} +``` + + + + +##### Default value for a `DateTime` + +Note that static default values for `DateTime` are based on the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard. + + + + +```prisma +model User { + email String @unique + data DateTime @default("2020-03-19T14:21:00+02:00") +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + data DateTime @default("2020-03-19T14:21:00+02:00") +} +``` + + + + +##### Default value for a `Bytes` + + + + +```prisma +model User { + email String @unique + secret Bytes @default("SGVsbG8gd29ybGQ=") +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + secret Bytes @default("SGVsbG8gd29ybGQ=") +} +``` + + + + +##### Default value for an `enum` + + + + +```prisma +enum Role { + USER + ADMIN +} +``` + +```prisma highlight=5;normal +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + + + + +```prisma +enum Role { + USER + ADMIN +} +``` + +```prisma highlight=5;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + name String? + role Role @default(USER) + posts Post[] + profile Profile? +} +``` + + + + +##### Default values for scalar lists + + + + +```prisma highlight=4;normal +model User { + id Int @id @default(autoincrement()) + posts Post[] + favoriteColors String[] @default(["red", "yellow", "purple"]) + roles Role[] @default([USER, DEVELOPER]) +} + +enum Role { + USER + DEVELOPER + ADMIN +} +``` + + + + +```prisma highlight=4;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] + favoriteColors String[] @default(["red", "yellow", "purple"]) + roles Role[] @default([USER, DEVELOPER]) +} + +enum Role { + USER + DEVELOPER + ADMIN +} +``` + + + + +### `@unique` + +Defines a unique constraint for this field. + +#### Remarks + +##### General + +- A field annotated with `@unique` can be optional or required +- A field annotated with `@unique` _must_ be required if it represents the only unique constraint on a model without an `@id` / `@@id` +- A model can have any number of unique constraints +- Can be defined on any scalar field +- **Cannot** be defined on a relation field + +##### Relational databases + +- Corresponding database type: `UNIQUE` +- `NULL` values are considered to be distinct (multiple rows with `NULL` values in the same column are allowed) +- Adding a unique constraint automatically adds a corresponding _unique index_ to the specified column(s). + +##### MongoDB + +- Enforced by a [unique index in MongoDB](https://docs.mongodb.com/manual/core/index-unique/) + +#### Arguments + +| Name | Required | Type | Description | +| ----------- | -------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `map` | **No** | `String` | | +| `length` | **No** | `number` | Allows you to specify a maximum length for the subpart of the value to be indexed.

MySQL only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `sort` | **No** | `String` | Allows you to specify in what order the entries of the constraint are stored in the database. The available options are `Asc` and `Desc`.

In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `clustered` | **No** | `Boolean` | Defines whether the constraint is clustered or non-clustered. Defaults to `false`.

SQL Server only. In preview in versions 3.13.0 and later, and in general availability in versions 4.0.0 and later. | + +- ¹ Can be required by some of the index and field types. + +#### Signature + +```prisma no-lines +@unique(map: String?, length: number?, sort: String?) +``` + +> **Note**: Before version 4.0.0, or 3.5.0 with the `extendedIndexes` Preview feature enabled, the signature was: +> +> ```prisma no-lines +> @unique(map: String?) +> ``` + +> **Note**: Before version 3.0.0, the signature was: +> +> ```no-lines +> @unique +> ``` + +#### Examples + +##### Specify a unique attribute on a required `String` field + + + + +```prisma +model User { + email String @unique + name String +} +``` + + + + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId + name String +} +``` + + + + +##### Specify a unique attribute on an optional `String` field + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String? @unique + name String +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String? @unique + name String +} +``` + + + + +##### Specify a unique attribute on relation scalar field `authorId` + + + + +```prisma +model Post { + author User @relation(fields: [authorId], references: [id]) + authorId Int @unique + title String + published Boolean @default(false) +} + +model User { + id Int @id @default(autoincrement()) + email String? @unique + name String + Post Post[] +} +``` + + + + +```prisma +model Post { + author User @relation(fields: [authorId], references: [id]) + authorId String @unique @db.ObjectId + title String + published Boolean @default(false) +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String? @unique + name String + Post Post[] +} +``` + + + + +##### Specify a unique attribute with `cuid()` values as default values + + + + +```prisma +model User { + token String @unique @default(cuid()) + name String +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + token String @unique @default(cuid()) + name String +} +``` + + + + +### `@@unique` + +Defines a compound [unique constraint](/orm/prisma-schema/data-model/models#defining-a-unique-field) for the specified fields. + +#### Remarks + +##### General + +- All fields that make up the unique constraint **must** be mandatory fields. The following model is **not** valid because `id` could be `null`: + + ```prisma + model User { + firstname Int + lastname Int + id Int? + + @@unique([firstname, lastname, id]) + } + ``` + + The reason for this behavior is that all connectors consider `null` values to be distinct, which means that two rows that _look_ identical are considered unique: + + ``` + firstname | lastname | id + -----------+----------+------ + John | Smith | null + John | Smith | null + ``` + +- A model can have any number of `@@unique` blocks + +##### Relational databases + +- Corresponding database type: `UNIQUE` +- A `@@unique` block is required if it represents the only unique constraint on a model without an `@id` / `@@id` +- Adding a unique constraint automatically adds a corresponding _unique index_ to the specified column(s) + +##### MongoDB + +- Enforced by a [compound index in MongoDB](https://docs.mongodb.com/manual/core/index-compound/) - you must create this index yourself +- A `@@unique` block cannot be used as the only unique identifier for a model - MongoDB requires an `@id` field + +#### Arguments + +| Name | Required | Type | Description | +| ----------- | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| `fields` | **Yes** | `FieldReference[]` | A list of field names - for example, `["firstname", "lastname"]`. Fields must be mandatory - see remarks. | +| `name` | **No** | `String` | The name of the unique combination of fields - defaults to `fieldName1_fieldName2_fieldName3` | +| `map` | **No** | `String` | +| `length` | **No** | `number` | Allows you to specify a maximum length for the subpart of the value to be indexed.

MySQL only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `sort` | **No** | `String` | Allows you to specify in what order the entries of the constraint are stored in the database. The available options are `Asc` and `Desc`.

In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | | +| `clustered` | **No** | `Boolean` | Defines whether the constraint is clustered or non-clustered. Defaults to `false`.

SQL Server only. In preview in versions 3.13.0 and later, and in general availability in versions 4.0.0 and later. | + +The name of the `fields` argument on the `@@unique` attribute can be omitted: + +```prisma no-lines +@@unique(fields: [title, author]) +@@unique([title, author]) +@@unique(fields: [title, author], name: "titleAuthor") +``` + +The `length` and `sort` arguments are added to the relevant field names: + +```prisma no-lines +@@unique(fields: [title(length:10), author]) +@@unique([title(sort: Desc), author(sort: Asc)]) +``` + +#### Signature + +> ```prisma no-lines +> @@unique(_ fields: FieldReference[], name: String?, map: String?) +> ``` + +> **Note**: Before version 4.0.0, or before version 3.5.0 with the `extendedIndexes` Preview feature enabled, the signature was: +> +> ```prisma no-lines +> @@unique(_ fields: FieldReference[], name: String?, map: String?) +> ``` + +> **Note**: Before version 3.0.0, the signature was: +> +> ```prisma no-lines +> @@unique(_ fields: FieldReference[], name: String?) +> ``` + +#### Examples + +##### Specify a multi-field unique attribute on two `String` fields + + + + +```prisma +model User { + id Int @default(autoincrement()) + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique([firstName, lastName]) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique([firstName, lastName]) +} +``` + + + + +To retrieve a user, use the generated field name (`firstname_lastname`): + +```ts highlight=3;normal +const user = await prisma.user.findUnique({ + where: { + firstName_lastName: { + firstName: 'Alice', + lastName: 'Smith', + isAdmin: true, + }, + }, +}) +``` + +##### Specify a multi-field unique attribute on two `String` fields and one `Boolean` field + + + + +```prisma +model User { + id Int @default(autoincrement()) + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique([firstName, lastName, isAdmin]) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique([firstName, lastName, isAdmin]) +} +``` + + + + +##### Specify a multi-field unique attribute that includes a relation field + + + + +```prisma +model Post { + id Int @default(autoincrement()) + author User @relation(fields: [authorId], references: [id]) + authorId Int + title String + published Boolean @default(false) + + @@unique([authorId, title]) +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + posts Post[] +} +``` + + + + +```prisma +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + author User @relation(fields: [authorId], references: [id]) + authorId String @db.ObjectId + title String + published Boolean @default(false) + + @@unique([authorId, title]) +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique + posts Post[] +} +``` + + + + +##### Specify a custom `name` for a multi-field unique attribute + + + + +```prisma +model User { + id Int @default(autoincrement()) + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique(fields: [firstName, lastName, isAdmin], name: "admin_identifier") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + firstName String + lastName String + isAdmin Boolean @default(false) + + @@unique(fields: [firstName, lastName, isAdmin], name: "admin_identifier") +} +``` + + + + +To retrieve a user, use the custom field name (`admin_identifier`): + +```ts highlight=3;normal +const user = await prisma.user.findUnique({ + where: { + admin_identifier: { + firstName: 'Alice', + lastName: 'Smith', + isAdmin: true, + }, + }, +}) +``` + +### `@@index` + +Defines an index in the database. + +#### Remarks + +##### Relational databases + +- Corresponding database type: `INDEX` +- There are some additional index configuration options that cannot be provided via the Prisma schema yet. These include: + - PostgreSQL and CockroachDB: + - Define index fields as expressions (e.g. `CREATE INDEX title ON public."Post"((lower(title)) text_ops);`) + - Define partial indexes with `WHERE` + - Create indexes concurrently with `CONCURRENTLY` + + + +While you cannot configure these option in your Prisma schema, you can still configure them on the database-level directly. + + + +##### MongoDB + +- In version `3.12.0` and later, you can define an index on a field of a [composite type](/orm/prisma-schema/data-model/models#defining-composite-types) using the syntax `@@index([compositeType.field])`. See [Defining composite type indexes](/orm/prisma-schema/data-model/models#defining-composite-type-indexes) for more details. + +#### Arguments + +| Name | Required | Type | Description | +| ----------- | -------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `fields` | **Yes** | `FieldReference[]` | A list of field names - for example, `["firstname", "lastname"]` | +| `name` | **No** | `String` | The name that Prisma Client will expose for the argument covering all fields, e.g. `fullName` in `fullName: { firstName: "First", lastName: "Last"}` | +| `map` | **No** | `map` | The name of the index in the underlying database (Prisma generates an index name that respects identifier length limits if you do not specify a name. Prisma uses the following naming convention: `tablename.field1_field2_field3_unique`) | +| `length` | **No** | `number` | Allows you to specify a maximum length for the subpart of the value to be indexed.

MySQL only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `sort` | **No** | `String` | Allows you to specify in what order the entries of the index or constraint are stored in the database. The available options are `asc` and `desc`.

In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `clustered` | **No** | `Boolean` | Defines whether the index is clustered or non-clustered. Defaults to `false`.

SQL Server only. In preview in versions 3.5.0 and later, and in general availability in versions 4.0.0 and later. | +| `type` | **No** | `identifier` | Allows you to specify an index access method. Defaults to `BTree`.

PostgreSQL and CockroachDB only. In preview with the `Hash` index access method in versions 3.6.0 and later, and with the `Gist`, `Gin`, `SpGist` and `Brin` methods added in 3.14.0. In general availability in versions 4.0.0 and later. | +| `ops` | **No** | `identifier` or a `function` | Allows you to define the index operators for certain index types.

PostgreSQL only. In preview in versions 3.14.0 and later, and in general availability in versions 4.0.0 and later. | + +The _name_ of the `fields` argument on the `@@index` attribute can be omitted: + +```prisma no-lines +@@index(fields: [title, author]) +@@index([title, author]) +``` + +The `length` and `sort` arguments are added to the relevant field names: + +```prisma no-lines +@@index(fields: [title(length:10), author]) +@@index([title(sort: Asc), author(sort: Desc)]) +``` + +#### Signature + +```prisma no-lines +@@index(_ fields: FieldReference[], map: String?) +``` + +> **Note**: Until version 3.0.0, the signature was: +> +> ```prisma no-lines +> @@index(_ fields: FieldReference[], name: String?) +> ``` +> +> The old `name` argument will still be accepted to avoid a breaking change. + +#### Examples + +Assume you want to add an index for the `title` field of the `Post` model + +##### Define a single-column index (Relational databases only) + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + + @@index([title]) +} +``` + +##### Define a multi-column index (Relational databases only) + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + + @@index([title, content]) +} +``` + +##### Define an index with a name (Relational databases only) + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + + @@index(fields: [title, content], name: "main_index") +} +``` + +##### Define an index on a composite type field (Relational databases only) + +```prisma +type Address { + street String + number Int +} + +model User { + id Int @id + email String + address Address + + @@index([address.number]) +} +``` + +### `@relation` + +Defines meta information about the relation. [Learn more](/orm/prisma-schema/data-model/relations#the-relation-attribute). + +#### Remarks + +##### Relational databases + +- Corresponding database types: `FOREIGN KEY` / `REFERENCES` + +##### MongoDB + +- If your model's primary key is of type `ObjectId` in the underlying database, both the primary key _and_ the foreign key must have the `@db.ObjectId` attribute + +#### Arguments + +| Name | Type | Required | Description | Example | +| :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- | +| `name` | `String` | Sometimes (e.g. to disambiguate a relation) | Defines the name of the relationship. In an m-n-relation, it also determines the name of the underlying relation table. | `"CategoryOnPost"`, `"MyRelation"` | +| `fields` | `FieldReference[]` | On [annotated](/orm/prisma-schema/data-model/relations#relation-fields) relation fields | A list of [fields](/orm/prisma-schema/data-model/models#defining-fields) of the _current_ model | `["authorId"]`, `["authorFirstName, authorLastName"]` | +| `references` | `FieldReference[]` | On [annotated](/orm/prisma-schema/data-model/relations#relation-fields) relation fields | A list of [fields](/orm/prisma-schema/data-model/models#defining-fields) of the model on _the other side of the relation_ | `["id"]`, `["firstName, lastName"]` | +| `map` | `String` | No | Defines a [custom name](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) for the foreign key in the database. | `["id"]`, `["firstName, lastName"]` | +| `onUpdate` | Enum. See [Types of referential actions](/orm/prisma-schema/data-model/relations/referential-actions#types-of-referential-actions) for values. | No | Defines the [referential action](/orm/prisma-schema/data-model/relations/referential-actions) to perform when a referenced entry in the referenced model is being updated. | `Cascade`, `NoAction` | +| `onDelete` | Enum. See [Types of referential actions](/orm/prisma-schema/data-model/relations/referential-actions#types-of-referential-actions) for values. | No | Defines the [referential action](/orm/prisma-schema/data-model/relations/referential-actions) to perform when a referenced entry in the referenced model is being deleted. | `Cascade`, `NoAction` | + +The name of the `name` argument on the `@relation` attribute can be omitted (`references` is required): + + +```prisma +@relation(name: "UserOnPost", references: [id]) +@relation("UserOnPost", references: [id]) + +// or + +@relation(name: "UserOnPost") +@relation("UserOnPost") +``` + + +#### Signature + +```prisma no-lines +@relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?, map: String?) +``` + +With SQLite, the signature changes to: + +```prisma no-lines +@relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?, onDelete: ReferentialAction?, onUpdate: ReferentialAction?) +``` + +> **Note**: Until version 3.0.0, the signature was: +> +> ```prisma no-lines +> @relation(_ name: String?, fields: FieldReference[]?, references: FieldReference[]?) +> ``` + +#### Examples + +See: [The `@relation` attribute](/orm/prisma-schema/data-model/relations#the-relation-attribute). + +### `@map` + +Maps a field name or enum value from the Prisma schema to a column or document field with a different name in the database. If you do not use `@map`, the Prisma field name matches the column name or document field name exactly. + +> See [Using custom model and field names](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) to see how `@map` and `@@map` changes the generated Prisma Client. + +#### Remarks + +##### General + +- `@map` **does not** rename the columns / fields in the database +- `@map` **does** [change the field names in the generated client](#map-the-firstname-field-to-a-column-called-first_name) + +##### MongoDB + +Your `@id` field must include `@map("_id")`. For example: + +```prisma +model User { + id String @default(auto()) @map("_id") @db.ObjectId +} +``` + +#### Arguments + +| Name | Type | Required | Description | Example | +| :----- | :------- | :------- | :--------------------------------------------------------------------------- | :------------------------------ | +| `name` | `String` | **Yes** | The database column (relational databases) or document field (MongoDB) name. | `"comments"`, `"someFieldName"` | + +The name of the `name` argument on the `@map` attribute can be omitted: + +```prisma +@map(name: "is_admin") +@map("users") +``` + +#### Signature + +```prisma no-lines +@map(_ name: String) +``` + +#### Examples + +##### Map the `firstName` field to a column called `first_name` + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + firstName String @map("first_name") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + firstName String @map("first_name") +} +``` + + + + +The generated client: + +```ts highlight=3;normal +await prisma.user.create({ + data: { + firstName: 'Yewande', // first_name --> firstName + }, +}) +``` + +##### Map an enum named `ADMIN` to a database enum named `admin` + +```prisma +enum Role { + ADMIN @map("admin") + CUSTOMER +} +``` + +### `@@map` + +Maps the Prisma schema model name to a table (relational databases) or collection (MongoDB) with a different name, or an enum name to a different underlying enum in the database. If you do not use `@@map`, the model name matches the table (relational databases) or collection (MongoDB) name exactly. + +> See [Using custom model and field names](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) to see how `@map` and `@@map` changes the generated Prisma Client. + +#### Arguments + +| Name | Type | Required | Description | Example | +| :----- | :------- | :------- | :---------------------------------------------------------------------- | :------------------------------------------ | +| `name` | `String` | **Yes** | The database table (relational databases) or collection (MongoDB) name. | `"comments"`, `"someTableOrCollectionName"` | + +The name of the `name` argument on the `@@map` attribute can be omitted + +```prisma +@@map(name: "users") +@@map("users") +``` + +#### Signature + +```prisma no-lines +@@map(_ name: String) +``` + +#### Examples + +##### Map the `User` model to a database table/collection named `users` + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String + + @@map("users") +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + + @@map("users") +} +``` + + + + +The generated client: + +```ts highlight=1;normal +await prisma.user.create({ + // users --> user + data: { + name: 'Yewande', + }, +}) +``` + +##### Map the `Role` enum to a native enum in the database named `_Role` its values to lowercase values in the database + +```prisma +enum Role { + ADMIN @map("admin") + CUSTOMER @map("customer") + + @@map("_Role") +} +``` + +### `@updatedAt` + +Automatically stores the time when a record was last updated. If you do not supply a time yourself, Prisma Client will automatically set the value for fields with this attribute. + +#### Remarks + +- Compatible with [`DateTime`](#datetime) fields +- Implemented at Prisma level + +#### Arguments + +N/A + +#### Signature + +```prisma no-lines +@updatedAt +``` + +#### Examples + + + + +```prisma line-number +model Post { + id String @id + updatedAt DateTime @updatedAt +} +``` + + + + +```prisma line-number +model Post { + id String @id @map("_id") @db.ObjectId + updatedAt DateTime @updatedAt +} +``` + + + + +### `@ignore` + +Add `@ignore` to a field that you want to exclude from Prisma Client (for example, a field that you do not want Prisma users to update). Ignored fields are excluded from the generated Prisma Client. The model's `create` method is disabled when doing this for _required_ fields with no `@default` (because the database cannot create an entry without that data). + +#### Remarks + +- In [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later, Prisma automatically adds `@ignore` to fields that _refer to_ invalid models when you introspect. + +#### Examples + +The following example demonstrates manually adding `@ignore` to exclude the `email` field from Prisma Client: + +```prisma file=schema.prisma highlight=4;normal +model User { + id Int @id + name String + email String @ignore // this field will be excluded +} +``` + +### `@@ignore` + +Add `@@ignore` to a model that you want to exclude from Prisma Client (for example, a model that you do not want Prisma users to update). Ignored models are excluded from the generated Prisma Client. + +#### Remarks + +- In [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later, Prisma adds `@@ignore` to an invalid model. (It also adds [`@ignore`](#ignore) to relations pointing to such a model) + +#### Examples + +In the following example, the `Post` model is invalid because it does not have a unique identifier. Use `@@ignore` to exclude it from the generated Prisma Client API: + +```prisma file=schema.prisma highlight=7;normal +/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +model Post { + id Int @default(autoincrement()) // no unique identifier + author User @relation(fields: [authorId], references: [id]) + authorId Int + + @@ignore +} +``` + +In the following example, the `Post` model is invalid because it does not have a unique identifier, and the `posts` relation field on `User` is invalid because it refers to the invalid `Post` model. Use `@@ignore` on the `Post` model and `@ignore` on the `posts` relation field in `User` to exclude both the model and the relation field from the generated Prisma Client API: + +```prisma file=schema.prisma highlight=7,13;normal +/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +model Post { + id Int @default(autoincrement()) // no unique identifier + author User @relation(fields: [authorId], references: [id]) + authorId Int + + @@ignore +} + +model User { + id Int @id @default(autoincrement()) + name String? + posts Post[] @ignore +} +``` + +### `@@schema` + + + +To use this attribute, you must have the [`multiSchema`](https://github.com/prisma/prisma/issues/1122#issuecomment-1231773471) preview feature enabled. Multiple database schema support is currently available with the PostgreSQL, CockroachDB, and SQL Server connectors. + + + +Add `@@schema` to a model to specify which schema in your database should contain the table associated with that model. + +#### Arguments + +| Name | Type | Required | Description | Example | +| :----- | :------- | :------- | :------------------------------- | :----------------- | +| `name` | `String` | **Yes** | The name of the database schema. | `"base"`, `"auth"` | + +The name of the `name` argument on the `@@schema` attribute can be omitted + +```prisma +@@schema(name: "auth") +@@schema("auth") +``` + +#### Signature + +```prisma no-lines +@@schema(_ name: String) +``` + +#### Examples + +##### Map the `User` model to a database schema named `auth` + +```prisma highlight=3,9,16;normal +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + schemas = ["auth"] +} + +model User { + id Int @id @default(autoincrement()) + name String + + @@schema("auth") +} +``` + + + +For more information about using the `multiSchema` feature, refer to [this guide](/orm/prisma-schema/data-model/multi-schema). + + + +## Attribute functions + +### `auto()` + + + This function is available on MongoDB only. + + +Represents **default values** that are automatically generated by the database. + +#### Remarks + +##### MongoDB + +Used to generate an `ObjectId` for `@id` fields: + +```prisma +id String @map("_id") @db.ObjectId @default(auto()) +``` + +##### Relational databases + +The `auto()` function is not available on relational databases. + +#### Example + +##### Generate `ObjectId` (MongoDB only) + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? +} +``` + +### `autoincrement()` + + + +**Not supported by MongoDB**
+The [MongoDB connector](/orm/overview/databases/mongodb) does not support the `autoincrement()` function. + +
+ +Create a sequence of integers in the underlying database and assign the incremented values to the ID values of the created records based on the sequence. + +#### Remarks + +- Compatible with `Int` on most databases (`BigInt` on CockroachDB) +- Implemented on the database-level, meaning that it manifests in the database schema and can be recognized through introspection. Database implementations: + + | Database | Implementation | + | ----------- | ------------------------------------------------------------------------------------------------- | + | PostgreSQL | [`SERIAL`](https://www.postgresql.org/docs/9.1/datatype-numeric.html#DATATYPE-SERIAL) type | + | MySQL | [`AUTO_INCREMENT`](https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html) attribute | + | SQLite | [`AUTOINCREMENT`](https://www.sqlite.org/autoinc.html) keyword | + | CockroachDB | [`SERIAL`](https://www.postgresql.org/docs/9.1/datatype-numeric.html#DATATYPE-SERIAL) type | + +#### Examples + +##### Generate autoincrementing integers as IDs (Relational databases only) + +```prisma +model User { + id Int @id @default(autoincrement()) + name String +} +``` + +### `sequence()` + + + +**Only supported by CockroachDB**
+The sequence function is only supported by [CockroachDB connector](/orm/overview/databases/cockroachdb). + +
+ +Create a sequence of integers in the underlying database and assign the incremented values to the values of the created records based on the sequence. + +#### Optional arguments + +| Argument | Example | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `virtual` | `@default(sequence(virtual))`
Virtual sequences are sequences that do not generate monotonically increasing values and instead produce values like those generated by the built-in function `unique_rowid()`. | +| `cache` | `@default(sequence(cache: 20))`
The number of sequence values to cache in memory for reuse in the session. A cache size of `1` means that there is no cache, and cache sizes of less than `1` are not valid. | +| `increment` | `@default(sequence(increment: 4))`
The new value by which the sequence is incremented. A negative number creates a descending sequence. A positive number creates an ascending sequence. | +| `minValue` | `@default(sequence(minValue: 10))`
The new minimum value of the sequence. | +| `maxValue` | `@default(sequence(maxValue: 3030303))`
The new maximum value of the sequence. | +| `start` | `@default(sequence(start: 2))`
The value the sequence starts at, if it's restarted or if the sequence hits the `maxValue`. | + +#### Examples + +##### Generate sequencing integers as IDs + +```prisma +model User { + id Int @id @default(sequence(maxValue: 4294967295)) + name String +} +``` + +### `cuid()` + +Generate a globally unique identifier based on the [`cuid`](https://github.com/ericelliott/cuid) spec. + +#### Remarks + +- Compatible with `String` +- Implemented by Prisma and therefore not "visible" in the underlying database schema. You can still use `cuid()` when using [introspection](/orm/prisma-schema/introspection) by [manually changing your Prisma schema](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) and [generating Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client), in that case the values will be generated by Prisma's [query engine](/orm/more/under-the-hood/engines). +- Since the length of `cuid()` output is undefined per the cuid creator, a safe field size is 30 characters, in order to allow for enough characters for very large values. If you set the field size as less than 30, and then a larger value is generated by `cuid()`, you might see Prisma errors such as `Error: The provided value for the column is too long for the column's type.` + +##### MongoDB + +- `cuid()` does not generate a valid `ObjectId` - [use the `@db.ObjectId` syntax](#generate-objectid-as-ids-mongodb-only) if you want to use `ObjectId` in the underlying database. However, you can still use `cuid()` if your `_id` field is not of type `ObjectId`. + +#### Examples + +##### Generate `cuid()` values as IDs + + + + +```prisma +model User { + id String @id @default(cuid()) + name String +} +``` + + + + +```prisma +model User { + id String @id @default(cuid()) @map("_id") + name String +} +``` + + + + +### `uuid()` + +Generate a globally unique identifier based on the [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) spec, version 4 (random). + +#### Remarks + +- Compatible with `String` +- Implemented by Prisma and therefore not "visible" in the underlying database schema. You can still use `uuid()` when using [introspection](/orm/prisma-schema/introspection) by [manually changing your Prisma schema](/orm/prisma-client/setup-and-configuration/custom-model-and-field-names) and [generating Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client), in that case the values will be generated by Prisma's [query engine](/orm/more/under-the-hood/engines). + + + +**Note (Relational databases)**: If you do not want to use Prisma's `uuid()` function, you can use [the native database function with `dbgenerated`](#override-default-value-behavior-for-supported-types). + + + +##### MongoDB + +- `uuid()` does not generate a valid `ObjectId` - [use the `@db.ObjectId` syntax](#generate-objectid-as-ids-mongodb-only) if you want to use `ObjectId` in the underlying database. However, you can still use `uuid()` if your `_id` field is not of type `ObjectId`. + +#### Examples + +##### Generate `uuid()` values as IDs + + + + +```prisma +model User { + id String @id @default(uuid()) + name String +} +``` + + + + +```prisma +model User { + id String @id @default(uuid()) @map("_id") + name String +} +``` + + + + +### `now()` + +Set a timestamp of the time when a record is created. + +#### Remarks + +##### General + +- Compatible with [`DateTime`](#datetime) + +##### Relational databases + +- Implemented on the database-level, meaning that it manifests in the database schema and can be recognized through introspection. Database implementations: + + | Database | Implementation | + | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | + | PostgreSQL | [`CURRENT_TIMESTAMP`](https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) and aliases like `now()` | + | MySQL | [`CURRENT_TIMESTAMP`](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_current-timestamp) and aliases like `now()` | + | SQLite | `CURRENT_TIMESTAMP` and aliases like `date('now')` | + | CockroachDB | [`CURRENT_TIMESTAMP`](https://www.cockroachlabs.com/docs/stable/functions-and-operators#special-syntax-forms) and aliases like `now()` | + +##### MongoDB + +- Implemented at Prisma level + +#### Examples + +##### Set current timestamp value when a record is created + + + + +```prisma +model User { + id String @id + createdAt DateTime @default(now()) +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + createdAt DateTime @default(now()) +} +``` + + + + +### `dbgenerated()` + +Represents **default values** that cannot be expressed in the Prisma schema (such as `random()`). + +#### Remarks + +##### Relational databases + +- Compatible with any type +- If a value is present, it cannot be empty (for example, `dbgenerated("")`) - [2.21.0](https://github.com/prisma/prisma/releases/tag/2.21.0) and later +- Accepts a `String` value in [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) and later, which allows you to: + + - [Set default values for `Unsupported` types](#set-default-value-for-unsupported-type) + - [Override default value behavior for supported types](#override-default-value-behavior-for-supported-types) + +- String values in `dbgenerated` might not match what the DB returns as the default value, because values such as strings may be explicitly cast (e.g. `'hello'::STRING`). When a mismatch is present, Prisma Migrate indicates a migration is still needed. You can use `prisma db pull` to infer the correct value to resolve the discrepancy. ([Related issue](https://github.com/prisma/prisma/issues/14917)) + +#### Examples + +##### Set default value for `Unsupported` type + +```prisma +circle Unsupported("circle")? @default(dbgenerated("'<(10,4),11>'::circle")) +``` + +##### Override default value behavior for supported types + +You can also use `dbgenerated()` to set the default value for supported types. For example, in PostgreSQL you can generate UUIDs at the database level rather than rely on Prisma's `uuid()`: + +```prisma highlight=2;add|3;delete +model User { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + id String @id @default(uuid()) @db.Uuid + test String? +} +``` + + + +**Note**: [`gen_random_uuid()` is a PostgreSQL function](https://www.postgresql.org/docs/13/functions-uuid.html). To use it in PostgreSQL versions 12.13 and earlier, you must enable the `pgcrypto` extension.

In Prisma versions 4.5.0 and later, you can declare the `pgcrypto` extension in your Prisma schema with the [`postgresqlExtensions` preview feature](/orm/prisma-schema/postgresql-extensions). + +
+ +## Attribute argument types + +### `FieldReference[]` + +An array of [field](#model-fields) names: `[id]`, `[firstName, lastName]` + +### `String` + +A variable length text in double quotes: `""`, `"Hello World"`, `"Alice"` + +### `Expression` + +An expression that can be evaluated by Prisma: `42.0`, `""`, `Bob`, `now()`, `cuid()` + +## `enum` + + + +**Not supported by SQLite and Microsoft SQL Server**
+The [SQLite connector](/orm/overview/databases/sqlite) and the The [Microsoft SQL Server connector](/orm/overview/databases/sql-server) do not support the `enum` type. + +
+ +Defines an [enum](/orm/prisma-schema/data-model/models#defining-enums) . + +### Remarks + +- Enums are natively supported by [PostgreSQL](https://www.postgresql.org/docs/current/datatype-enum.html) and [MySQL](https://dev.mysql.com/doc/refman/8.0/en/enum.html) +- Enums are implemented and enforced at Prisma level in MongoDB + +### Naming conventions + +- Enum names must start with a letter (they are typically spelled in [PascalCase](http://wiki.c2.com/?PascalCase)) +- Enums must use the singular form (e.g. `Role` instead of `role`, `roles` or `Roles`). +- Must adhere to the following regular expression: `[A-Za-z][A-Za-z0-9_]*` + +### Examples + +#### Specify an `enum` with two possible values + + + + +```prisma +enum Role { + USER + ADMIN +} + +model User { + id Int @id @default(autoincrement()) + role Role +} +``` + + + + +```prisma +enum Role { + USER + ADMIN +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + role Role +} +``` + + + + +#### Specify an `enum` with two possible values and set a default value + + + + +```prisma +enum Role { + USER + ADMIN +} + +model User { + id Int @id @default(autoincrement()) + role Role @default(USER) +} +``` + + + + +```prisma +enum Role { + USER + ADMIN +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + role Role @default(USER) +} +``` + + + + +## `type` + + + +Composite types are available **for MongoDB only**. + + + + + +Composite types are available in versions 3.12.0 and later, and in versions 3.10.0 and later if you enable the `mongodb` Preview feature flag. + + + +Defines a [composite type](/orm/prisma-schema/data-model/models#defining-composite-types) . + +### Naming conventions + +Type names must: + +- start with a letter (they are typically spelled in [PascalCase](http://wiki.c2.com/?PascalCase)) +- adhere to the following regular expression: `[A-Za-z][A-Za-z0-9_]*` + +### Examples + +#### Define a `Product` model with a list of `Photo` composite types + +```prisma +model Product { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + photos Photo[] +} + +type Photo { + height Int + width Int + url String +} +``` diff --git a/docs/200-orm/500-reference/200-prisma-cli-reference.mdx b/docs/200-orm/500-reference/200-prisma-cli-reference.mdx new file mode 100644 index 0000000000..7ce3f018ff --- /dev/null +++ b/docs/200-orm/500-reference/200-prisma-cli-reference.mdx @@ -0,0 +1,1449 @@ +--- +title: 'Prisma CLI reference' +navTitle: 'Prisma CLI' +metaTitle: 'Prisma CLI' +metaDescription: 'This page gives an overview of all available Prisma CLI commands, explains their options and shows numerous usage examples.' +tocDepth: 3 +--- + + + +This document describes the Prisma CLI commands, arguments, and options. + + + +## Commands + +### `version` (`-v`) + +The `version` command outputs information about your current `prisma` version, platform, and engine binaries. + +#### Options + +The `version` command recognizes the following options to modify its behavior: + +| Option | Required | Description | +| -------- | -------- | ------------------------------------------- | +| `--json` | No | Outputs version information in JSON format. | + +#### Examples + +##### Output version information + + + + + +```terminal +prisma version +``` + + + + + +```code no-copy +Environment variables loaded from .env +prisma : 2.21.0-dev.4 +@prisma/client : 2.21.0-dev.4 +Current platform : windows +Query Engine : query-engine 2fb8f444d9cdf7c0beee7b041194b42d7a9ce1e6 (at C:\Users\veroh\AppData\Roaming\npm\node_modules\@prisma\cli\query-engine-windows.exe) +Migration Engine : migration-engine-cli 2fb8f444d9cdf7c0beee7b041194b42d7a9ce1e6 (at C:\Users\veroh\AppData\Roaming\npm\node_modules\@prisma\cli\migration-engine-windows.exe) +Format Binary : prisma-fmt 60ba6551f29b17d7d6ce479e5733c70d9c00860e (at node_modules\@prisma\engines\prisma-fmt-windows.exe) +Default Engines Hash : 60ba6551f29b17d7d6ce479e5733c70d9c00860e +Studio : 0.365.0 +``` + + + + + +##### Output version information (`-v`) + + + + + +```terminal +prisma -v +``` + + + + + +```code no-copy +Environment variables loaded from .env +prisma : 2.21.0-dev.4 +@prisma/client : 2.21.0-dev.4 +Current platform : windows +Query Engine : query-engine 2fb8f444d9cdf7c0beee7b041194b42d7a9ce1e6 (at C:\Users\veroh\AppData\Roaming\npm\node_modules\@prisma\cli\query-engine-windows.exe) +Migration Engine : migration-engine-cli 2fb8f444d9cdf7c0beee7b041194b42d7a9ce1e6 (at C:\Users\veroh\AppData\Roaming\npm\node_modules\@prisma\cli\migration-engine-windows.exe) +Format Binary : prisma-fmt 60ba6551f29b17d7d6ce479e5733c70d9c00860e (at node_modules\@prisma\engines\prisma-fmt-windows.exe) +Default Engines Hash : 60ba6551f29b17d7d6ce479e5733c70d9c00860e +Studio : 0.365.0 +``` + + + + + +##### Output version information as JSON + + + + + +```terminal +prisma version --json +``` + + + + + +```code no-copy +Environment variables loaded from .env +{ + "prisma": "2.21.0-dev.4", + "@prisma/client": "2.21.0-dev.4", + "current-platform": "windows", + "query-engine": "query-engine 60ba6551f29b17d7d6ce479e5733c70d9c00860e (at node_modules\\@prisma\\engines\\query-engine-windows.exe)", + "migration-engine": "migration-engine-cli 60ba6551f29b17d7d6ce479e5733c70d9c00860e (at node_modules\\@prisma\\engines\\migration-engine-windows.exe)", + "format-binary": "prisma-fmt 60ba6551f29b17d7d6ce479e5733c70d9c00860e (at node_modules\\@prisma\\engines\\prisma-fmt-windows.exe)", + "default-engines-hash": "60ba6551f29b17d7d6ce479e5733c70d9c00860e", + "studio": "0.365.0" +} +``` + + + + + +### `init` + +Bootstraps a fresh Prisma project within the current directory. + +The `init` command does not interpret any existing files. Instead, it creates a `prisma` directory containing a bare-bones `schema.prisma` file within your current directory. + +#### Arguments + +| Argument | Required | Description | Default | +| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| `--datasource-provider` | No | Specifies the value for the `provider` field in the `datasource` block. Options are `sqlite`, `postgresql`, `mysql`, `sqlserver`, `mongodb` and `cockroachdb`. | `postgresql` | +| `--url` | No | Define a custom datasource url. | | +| `--generator-provider` | No | Define the generator provider to use. | `prisma-client-js` | +| `--preview-feature` | No | Define the [Preview features](/orm/reference/preview-features) to use. To define multiple Preview features, you have to provide the flag multiple times for each Preview feature. [See example](#run-prisma-init---preview-feature) | | +| `--output` | No | Specifies the [output location for the generated client](/orm/prisma-client/setup-and-configuration/generating-prisma-client#using-a-custom-output-path). | `node_modules/.prisma/client` | + +#### Examples + +##### Run `prisma init` + + + + + +```terminal +prisma init +``` + + + + + +```code no-copy wrap +✔ Your Prisma schema was created at prisma/schema.prisma. + You can now open it in your favorite editor. + +Next steps: +1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started +2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb. +3. Run prisma db pull to turn your database schema into a Prisma schema. +4. Run prisma generate to generate Prisma Client. You can then start querying your database. + +More information in our documentation: +https://pris.ly/d/getting-started +``` + + + + + +##### Run `prisma init --datasource-provider sqlite` + +```terminal +prisma init --datasource-provider sqlite +``` + +The command output contains helpful information on how to use the generated files and begin using Prisma with your project. + +#### Run `prisma init --preview-feature` + + + + + +```terminal +prisma init --preview-feature multiSchema +``` + + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} +``` + + + + + + + + + +```terminal +prisma init --preview-feature multiSchema --preview-feature metrics +``` + + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema", "metrics"] +} +``` + + + + + +#### Generated Assets + +**`prisma/schema.prisma`** + +An initial `schema.prisma` file to define your schema in: + +```prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +**`.env`** + +A file to define environment variables for your project: + +``` +# Environment variables declared in this file are automatically made available to Prisma. +# See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables + +# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. +# See the documentation for all the connection string options: https://pris.ly/d/connection-strings + +DATABASE_URL="file:./dev.db" +``` + +##### Run `prisma init --url mysql://user:password@localhost:3306/mydb` + +```terminal +prisma init --url mysql://user:password@localhost:3306/mydb +``` + +The command output contains helpful information on how to use the generated files and begin using Prisma with your project. + +#### Generated Assets + +**`prisma/schema.prisma`** + +A minimal `schema.prisma` file to define your schema in: + +```prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +**`.env`** + +A file to define environment variables for your project: + +``` +# Environment variables declared in this file are automatically made available to Prisma. +# See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables + +# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. +# See the documentation for all the connection string options: https://pris.ly/d/connection-strings + +DATABASE_URL="mysql://user:password@localhost:3306/mydb" +``` + +### `generate` + +The `generate` command generates assets like Prisma Client based on the [`generator`](/orm/prisma-schema/overview/generators) and [`data model`](/orm/prisma-schema/data-model/models) blocks defined in your `prisma/schema.prisma` file. + +The `generate` command is most often used to generate Prisma Client with the `prisma-client-js` generator. This does three things: + +1. Searches the current directory and parent directories to find the applicable `npm` project. It will create a `package.json` file in the current directory if it cannot find one. +2. Installs the `@prisma/client` into the `npm` project if it is not already present. +3. Inspects the current directory to find a Prisma schema file to process. It will then generate a customized [Prisma Client](https://github.com/prisma/prisma-client-js) for your project. + +#### Prerequisites + +To use the `generate` command, you must add a generator definition in your `schema.prisma` file. The `prisma-client-js` generator, used to generate Prisma Client, can be added by including the following in your `schema.prisma` file: + +```prisma +generator client { + provider = "prisma-client-js" +} +``` + +#### Options + +| Option | Required | Description | Default | +| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| `--data-proxy` | No | The `generate` command will generate Prisma Client for use with [Prisma Accelerate](/accelerate) prior to Prisma 5.0.0. Mutually exclusive with `--accelerate` and `--no-engine`. +| `--accelerate` | No | The `generate` command will generate Prisma Client for use with [Prisma Accelerate](/accelerate). Mutually exclusive with `--data-proxy` and `--no-engine`. Available in Prisma 5.1.0 and later. | +| `--no-engine` | No | The `generate` command will generate Prisma Client without an accompanied engine for use with [Prisma Accelerate](/accelerate). Mutually exclusive with `--data-proxy` and `--accelerate`. Available in Prisma 5.2.0 and later. | +| `--watch` | No | The `generate` command will continue to watch the `schema.prisma` file and re-generate Prisma Client on file changes. | + + + +**Deprecation Warning** + +As of Prisma 5.2.0, `--data-proxy` and `--accelerate` are deprecated in favor of `--no-engine` as Prisma Client no longer requires an option to work with Prisma Accelerate. All options are available and work similarly, but we recommend `--no-engine` as it prevents an engine from being downloaded which will greatly impact the size of apps deployed to serverless and edge functions. + + + +#### Arguments + +| Argument | Required | Description | Default | | +| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | --- | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | | +| `--generator` | No | Specifies which generator to use to generate assets. This option may be provided multiple times to include multiple generators. By default, all generators in the target schema will be run. | | + +#### Examples + +##### Generate Prisma Client using the default `schema.prisma` path + + + + + +```terminal +prisma generate +``` + + + + + +```code no-copy +✔ Generated Prisma Client to ./node_modules/.prisma/client in 61ms + +You can now start using Prisma Client in your code: + +import { PrismaClient } from '@prisma/client' +// or const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +Explore the full API: https://pris.ly/d/client +``` + + + + + +##### Generate Prisma Client using a non-default `schema.prisma` path + +```terminal +prisma generate --schema=./alternative/schema.prisma +``` + +##### Continue watching the `schema.prisma` file for changes to automatically re-generate Prisma Client + + + + + +```terminal +prisma generate --watch +``` + + + + + +```code no-copy +Watching... /home/prismauser/prisma/prisma-play/prisma/schema.prisma + +✔ Generated Prisma Client to ./node_modules/.prisma/client in 45ms +``` + + + + + +##### Run the `generate` command with only a specific generator + +```terminal +prisma generate --generator client +``` + +##### Run the `generate` command with multiple specific generators + +```terminal +prisma generate --generator client --generator zod_schemas +``` + +#### Generated Assets + +The `prisma-client-js` generator creates a customized client for working with your database within the `./node_modules/.prisma/client` directory by default - you can [customize the output folder](/orm/prisma-client/setup-and-configuration/generating-prisma-client#using-a-custom-output-path). + +### `introspect` + + + +**Deprecation warning**
+From Prisma 3.0.0 onwards, the `prisma introspect` command is deprecated and replaced with the [`prisma db pull`](#db-pull) command. + +
+ +### `validate` + +Validates the [Prisma Schema Language](/orm/prisma-schema) of the Prisma schema file. + +#### Arguments + +| Argument | Required | Description | Default | +| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | + +#### Examples + +##### Validate a schema without errors + + + + +```terminal +prisma validate +``` + + + + +```code no-copy +Environment variables loaded from .env +Prisma schema loaded from prisma/schema.prisma +The schema at /absolute/path/prisma/schema.prisma is valid 🚀 +``` + + + + +##### Validate a schema with validation errors + + + + +```terminal +prisma validate +``` + + + + +```code no-copy +Environment variables loaded from .env +Prisma schema loaded from prisma/schema.prisma +Error: Schema validation error - Error (query-engine-node-api library) +Error code: P1012 +error: The preview feature "unknownFeatureFlag" is not known. Expected one of: [...] + --> schema.prisma:3 + | + 2 | provider = "prisma-client-js" + 3 | previewFeatures = ["unknownFeatureFlag"] + | + +Validation Error Count: 1 +[Context: getDmmf] + +Prisma CLI Version : 4.5.0 +``` + + + + +### `format` + +Formats the Prisma schema file, which includes validating, formatting, and persisting the schema. + +#### Arguments + +| Argument | Required | Description | Default | +| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | + +#### Examples + +##### Validate a schema without errors + + + + +```terminal +prisma format +``` + + + + +```code no-copy +Environment variables loaded from .env +Prisma schema loaded from prisma/schema.prisma +Formatted prisma/schema.prisma in 116ms � +``` + + + + +##### Formatting a schema with validation errors + + + + +```terminal +prisma format +``` + + + + +```code no-copy +Environment variables loaded from .env +Prisma schema loaded from prisma/schema.prisma +Error: Schema validation error - Error (query-engine-node-api library) +Error code: P1012 +error: The preview feature "unknownFeatureFlag" is not known. Expected one of: [...] + --> schema.prisma:3 + | + 2 | provider = "prisma-client-js" + 3 | previewFeatures = ["unknownFeatureFlag"] + | + +Validation Error Count: 1 +[Context: getDmmf] + +Prisma CLI Version : 4.5.0 +``` + + + + +### `debug` + +Prints information for debugging and bug reports. + + + +This is available from version 5.6.0 and newer. + + + +#### Arguments + +| Argument | Required | Description | Default | +| ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | +| `--help` / `--h` | No | Displays the help message | | + +#### Example + + + + + + + +```terminal +prisma debug +``` + + + + + +```text no-copy +-- Prisma schema -- +Path: /prisma/schema.prisma + +-- Local cache directory for engines files -- +Path: /.cache/prisma + +-- Environment variables -- +When not set, the line is dimmed and no value is displayed. +When set, the line is bold and the value is inside the `` backticks. + +For general debugging + - CI: + - DEBUG: + - NODE_ENV: + - RUST_LOG: + - RUST_BACKTRACE: + - NO_COLOR: + - TERM: `xterm-256color` + - NODE_TLS_REJECT_UNAUTHORIZED: + - NO_PROXY: + - http_proxy: + - HTTP_PROXY: + - https_proxy: + - HTTPS_PROXY: + +For more information about Prisma environment variables: +See https://www.prisma.io/docs/orm/reference/environment-variables-reference + +For hiding messages + - PRISMA_DISABLE_WARNINGS: + - PRISMA_HIDE_PREVIEW_FLAG_WARNINGS: + - PRISMA_HIDE_UPDATE_MESSAGE: + +For downloading engines + - PRISMA_ENGINES_MIRROR: + - PRISMA_BINARIES_MIRROR (deprecated): + - PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: + - BINARY_DOWNLOAD_VERSION: + +For configuring the Query Engine Type + - PRISMA_CLI_QUERY_ENGINE_TYPE: + - PRISMA_CLIENT_ENGINE_TYPE: + +For custom engines + - PRISMA_QUERY_ENGINE_BINARY: + - PRISMA_QUERY_ENGINE_LIBRARY: + - PRISMA_SCHEMA_ENGINE_BINARY: + - PRISMA_MIGRATION_ENGINE_BINARY: + +For the "postinstall" npm hook + - PRISMA_GENERATE_SKIP_AUTOINSTALL: + - PRISMA_SKIP_POSTINSTALL_GENERATE: + - PRISMA_GENERATE_IN_POSTINSTALL: + +For "prisma generate" + - PRISMA_GENERATE_DATAPROXY: + - PRISMA_GENERATE_NO_ENGINE: + +For Prisma Client + - PRISMA_SHOW_ALL_TRACES: + - PRISMA_CLIENT_NO_RETRY (Binary engine only): + +For Prisma Migrate + - PRISMA_SCHEMA_DISABLE_ADVISORY_LOCK: + - PRISMA_MIGRATE_SKIP_GENERATE: + - PRISMA_MIGRATE_SKIP_SEED: + +For Prisma Studio + - BROWSER: + +-- Terminal is interactive? -- +true + +-- CI detected? -- +false +``` + + + + + + + +If you're using an older version of Prisma, you can use this command by running: + +```terminal +npx prisma@latest debug +``` + +## `db` + +### `db pull` + +The `db pull` command connects to your database and adds Prisma models to your Prisma schema that reflect the current database schema. + + + +**Warning**: The command will overwrite the current `schema.prisma` file with the new schema. Some manual changes or customization can be lost. Be sure to back up your current `schema.prisma` file (or commit your current state to version control to be able to revert any changes) before running `db pull` if it contains important modifications. + + + + + +Introspection with the `db pull` command on the [MongoDB connector](/orm/overview/databases/mongodb) samples the data instead of reading a schema. + + + +#### Prerequisites + +Before using the `db pull` command, you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +| Option | Required | Description | Default | +| --------- | -------- | --------------------------------------------------------------------------------------------------------------------- | ------- | +| `--force` | No | Force overwrite of manual changes made to schema. The generated schema will be based on the introspected schema only. | +| `--print` | No | Prints the created `schema.prisma` to the screen instead of writing it to the filesystem. | + +#### Arguments + +| Argument | Required | Description | Default | +| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| `--schema` | No | Specifies the path to the desired `schema.prisma` file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`, `./prisma/schema.prisma` | + +#### Examples + +##### Analyze the database and write its schema to the `schema.prisma` file + + + + + +```terminal +prisma db pull +``` + + + + + +```code no-copy +Introspecting based on datasource defined in schema.prisma … + +✔ Wrote Prisma data model into schema.prisma in 38ms + +Run prisma generate to generate Prisma Client. +``` + + + + + +##### Specify an alternative `schema.prisma` file to read and write to + + + + + +```terminal +prisma db pull --schema=./alternative/schema.prisma +``` + + + + + +```code no-copy +Introspecting based on datasource defined in alternative/schema.prisma … + +✔ Wrote Prisma data model into alternative/schema.prisma in 60ms + +Run prisma generate to generate Prisma Client. +``` + + + + + +##### Display the generated `schema.prisma` file instead of writing it to the filesystem + + + + + +```terminal +prisma db pull --print +``` + + + + + +```prisma no-copy +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = "file:./hello-prisma.db" +} + +model User { + email String @unique + name String? + user_id Int @id @default(autoincrement()) + post Post[] + profile Profile[] +} + +model Post { + content String? + post_id Int @id @default(autoincrement()) + title String + author User? @relation(fields: [author_id], references: [user_id]) + author_id Int? +} + +model Profile { + bio String? + profile_id Int @id @default(autoincrement()) + user User @relation(fields: [user_id], references: [user_id]) + user_id Int @unique +} +``` + + + + + +### `db push` + +The `db push` command pushes the state of your Prisma schema file to the database without using migrations. It creates the database if the database does not exist. + +This command is a good choice when you do not need to version schema changes, such as during prototyping and local development. + +See also: + +- [Conceptual overview of `db push` and when to use it over Prisma Migrate](/orm/prisma-migrate/workflows/prototyping-your-schema) +- [Schema prototyping with `db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) + +#### Prerequisites + +Before using the `db push` command, you must define a valid [datasource](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +| Options | Required | Description | +| :------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------- | +| `--skip-generate` | No | Skip generation of artifacts such as Prisma Client | +| `--force-reset` | No | Resets the database and then updates the schema - useful if you need to start from scratch due to unexecutable migrations. | +| `--accept-data-loss` | No | Ignore data loss warnings. This option is required if as a result of making the schema changes, data may be lost. | +| `--help` / `--h` | No | Displays the help message | + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +Push the schema: + +```terminal +prisma db push +``` + +Push the schema, accepting data loss: + +```terminal +prisma db push --accept-data-loss +``` + +Push the schema with a custom schema location: + +```terminal +prisma db push --schema=/tmp/schema.prisma +``` + +### `db seed` + +`db seed` changed from Preview to Generally Available (GA) in 3.0.1. + +See [Seeding your database](/orm/prisma-migrate/workflows/seeding) + +#### Options + +| Options | Required | Description | +| :--------------- | :------- | :-------------------------------------------------------- | +| `--help` / `--h` | No | Displays the help message | +| `--` | No | Allows the use of custom arguments defined in a seed file | + +The `--` argument/ [delimiter](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02)/ double-dash is available from version 4.15.0 or later. + +#### Examples + +```terminal +prisma db seed +``` + +### `db execute` + + + +The `db execute` command is Generally Available in versions 3.13.0 and later. If you're using a version between 3.9.0 and 3.13.0, it is available behind a `--preview-feature` CLI flag. + + + + + +This command is currently not supported on [MongoDB](/orm/overview/databases/mongodb). + + + +This command applies a SQL script to the database without interacting with the Prisma migrations table. The script takes two inputs: + +- the SQL script, which can be provided either on standard input or in a file +- the data source, which can either be the URL of the data source or the path to your Prisma schema file + +The output of the command is connector-specific, and is not meant for returning data, but only to report success or failure. + +See also: + +- [Migration troubleshooting in production](/orm/prisma-migrate/workflows/patching-and-hotfixing#fixing-failed-migrations-with-migrate-diff-and-db-execute) + +#### Prerequisites + +Before using the `db execute` command, if you do not use the `--url` option you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +One of the following data source inputs is required: + +| Options | Description | +| :--------- | :------------------------------------------------------------------- | +| `--url` | URL of the data source to run the command on | +| `--schema` | Path to a Prisma schema file, uses the URL in the `datasource` block | + +One of the following script inputs is required: + +| Options | Description | +| :-------- | :-------------------------------------------------------------------- | +| `--stdin` | Use the terminal standard input as the script to be executed | +| `--file` | Path to a file. The content will be sent as the script to be executed | + +Other options: + +| Options | Required | Description | +| :------- | :------- | :------------------------- | +| `--help` | No | Displays the help message. | + +#### Examples + +- Take the content of a SQL file located at `./script.sql` and execute it on the database specified by the URL in the `datasource` block of your `schema.prisma` file: + + ```terminal + prisma db execute --file ./script.sql --schema schema.prisma + ``` + +- Take the SQL script from standard input and execute it on the database specified by the data source URL given in the `DATABASE_URL` environment variable: + + ```terminal wrap + echo 'TRUNCATE TABLE dev;' | prisma db execute --stdin --url="$DATABASE_URL" + ``` + +## Prisma Migrate + +Prisma Migrate changed from Preview to Generally Available (GA) in 2.19.0. + + + +**Does not apply for MongoDB**
+Instead of `migrate dev` and related commands, [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) is used for [MongoDB](/orm/overview/databases/mongodb). + +
+ +### `migrate dev` + +**For use in development environments only, requires shadow database** + +The `migrate dev` command: + +1. Reruns the existing migration history in the [shadow database](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database) in order to detect schema drift (edited or deleted migration file, or a manual changes to the database schema) +1. Applies pending migrations to the shadow database (for example, new migrations created by colleagues) +1. Generates a new migration from any changes you made to the Prisma schema before running `migrate dev` +1. Applies all unapplied migrations to the development database and updates the `_prisma_migrations` table +1. Triggers the generation of artifacts (for example, Prisma Client) + + + +This command is not supported on [MongoDB](/orm/overview/databases/mongodb). Use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) instead. + + + +See also: + +- [Conceptual overview of Prisma Migrate](/orm/prisma-migrate) +- [Developing with Prisma Migrate](/orm/prisma-migrate) + +#### Options + +| Option | Required | Description | Default | +| :---------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------- | :------ | +| `--create-only` | No | Creates a new migration based on the changes in the schema but does not apply that migration. Run `migrate dev` to apply migration. | | +| `--skip-seed` | No | Skip triggering seed | | +| `--skip-generate` | No | Skip triggering generators (for example, Prisma Client) | | +| `--name` / `-n` | No | Name the migration (e.g. `prisma migrate dev --name added_job_title`) | | +| `--help` / `-h` | No | Displays the help message | + + + +If a [schema drift](/orm/prisma-migrate/understanding-prisma-migrate/shadow-database#detecting-schema-drift) is detected while running `prisma migrate dev` using `--create-only`, you will be prompted to reset your database. + + + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--name` | No | The name of the migration. If no name is provided, the CLI will prompt you. | | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +Apply all migrations, then create and apply any new migrations: + +```terminal +prisma migrate dev +``` + +Apply all migrations and create a new migration if there are schema changes, but do not apply it: + +```terminal +prisma migrate dev --create-only +``` + +### `migrate reset` + +**For use in development environments only** + +This command: + +1. Drops the database/schema if possible, or performs a soft reset if the environment does not allow deleting databases/schemas +1. Creates a new database/schema with the same name if the database/schema was dropped +1. Applies all migrations +1. Runs seed scripts + + + +This command is not supported on [MongoDB](/orm/overview/databases/mongodb). Use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) instead. + + + +#### Options + +| Option | Required | Description | Default | +| :---------------- | :------- | :------------------------------------------------------ | :------ | +| `--force` | No | Skip the confirmation prompt | | +| `--skip-generate` | No | Skip triggering generators (for example, Prisma Client) | | +| `--skip-seed` | No | Skip triggering seed | | +| `--help` / `--h` | No | Displays the help message | + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +```terminal +prisma migrate reset +``` + +### `migrate deploy` + +The `migrate deploy` command applies all pending migrations, and creates the database if it does not exist. Primarily used in non-development environments. This command: + +- Does **not** look for drift in the database or changes in the Prisma schema +- Does **not** reset the database or generate artifacts +- Does **not** rely on a shadow database + + + +This command is not supported on [MongoDB](/orm/overview/databases/mongodb). Use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) instead. + + + +#### Options + +| Option | Required | Description | Default | +| :--------------- | :------- | :------------------------ | :------ | +| `--help` / `--h` | No | Displays the help message | + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +```terminal +prisma migrate deploy +``` + +### `migrate resolve` + +The `migrate resolve` command allows you to solve migration history issues in production by marking a failed migration as already applied (supports baselining) or rolled back. + +Note that this command can only be used with a failed migration. If you try to use it with a successful migration you will receive an error. + + + +This command is not supported on [MongoDB](/orm/overview/databases/mongodb). Use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) instead. + + + +#### Options + +| Option | Required | Description | Default | +| :--------------- | :------- | :------------------------ | :------ | +| `--help` / `--h` | No | Displays the help message | + +#### Arguments + +| Argument | Required | Description | Default | +| :-------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--applied` | No\* | Record a specific migration as applied - for example `--applied "20201231000000_add_users_table"` | | +| `--rolled-back` | No\* | Record a specific migration as rolled back - for example `--rolled-back "20201231000000_add_users_table"` | `./schema.prisma`
`./prisma/schema.prisma` | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +You must specify either `--rolled-back` _or_ `--applied`. + +#### Examples + +```terminal +prisma migrate resolve --applied 20201231000000_add_users_table +``` + +```terminal +prisma migrate resolve --rolled-back 20201231000000_add_users_table +``` + +### `migrate status` + +The `prisma migrate status` command looks up the migrations in `./prisma/migrations/*` folder and the entries in the `_prisma_migrations` table and compiles information about the state of the migrations in your database. + + + +This command is not supported on [MongoDB](/orm/overview/databases/mongodb). Use [`db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) instead. + + + +For example: + +``` +Status +3 migrations found in prisma/migrations + +Your local migration history and the migrations table from your database are different: + +The last common migration is: 20201127134938_new_migration + +The migration have not yet been applied: +20201208100950_test_migration + +The migrations from the database are not found locally in prisma/migrations: +20201208100950_new_migration +``` + +In versions 4.3.0 and later, `prisma migrate status` exits with exit code 1 in the following cases: + +- a database connection error occurs +- there are migration files in the `migrations` directory that have not been applied to the database +- the migration history in the `migrations` directory has diverged from the state of the database +- no migration table is found +- failed migrations are found + +#### Options + +| Option | Required | Description | Default | +| :--------------- | :------- | :------------------------ | :------ | +| `--help` / `--h` | No | Displays the help message | + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +```terminal +prisma migrate status +``` + +### `migrate diff` + + + +The `migrate diff` command is Generally Available in versions 3.13.0 and later. If you're using a version between 3.9.0 and 3.13.0, it is available behind a `--preview-feature` CLI flag. + + + + + +This command is only partially supported for [MongoDB](/orm/overview/databases/mongodb). See the command options below for details. + + + +This command compares two database schema sources and outputs a description of a migration taking the first to the state of the second. + +The output can be given either as a human-readable summary (the default) or an executable script. + + + +The `migrate diff` command can only compare database features that are [supported by Prisma](/orm/reference/database-features). If two databases differ only in unsupported features, such as views or triggers, then `migrate diff` will not show any difference between them. + + + +The format of the command is: + +```terminal +prisma migrate diff --from-... --to-... +``` + +where the `--from-...` and `--to-...` options are selected based on the type of database schema source. The supported types of sources are: + +- live databases +- migration histories +- Prisma data models +- an empty schema + +Both schema sources must use the same database provider. For example, a diff comparing a PostgreSQL data source with a SQLite data source is not supported. + +See also: + +- [Migration troubleshooting in production](/orm/prisma-migrate/workflows/patching-and-hotfixing#fixing-failed-migrations-with-migrate-diff-and-db-execute) + +#### Prerequisites + +Before using the `migrate diff` command, if you are using the `--from-schema-datasource` or `--to-schema-datasource` you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +One of the following `--from-...` options is required: + +| Options | Description | Notes | +| :------------------------- | :-------------------------------------------------------------------------------- | :----------------------- | +| `--from-url` | A data source URL | | +| `--from-migrations` | Path to the Prisma Migrate migrations directory | Not supported in MongoDB | +| `--from-schema-datamodel` | Path to a Prisma schema file, uses the data model for the diff | | +| `--from-schema-datasource` | Path to a Prisma schema file, uses the URL in the `datasource` block for the diff | | +| `--from-empty` | Assume that you the data model you are migrating from is empty | | + +One of the following `--to-...` options is required: + +| Options | Description | Notes | +| :----------------------- | :-------------------------------------------------------------------------------- | :----------------------- | +| `--to-url` | A data source URL | | +| `--to-migrations` | Path to the Prisma Migrate migrations directory | Not supported in MongoDB | +| `--to-schema-datamodel` | Path to a Prisma schema file, uses the data model for the diff | | +| `--to-schema-datasource` | Path to a Prisma schema file, uses the URL in the `datasource` block for the diff | | +| `--to-empty` | Assume that you the data model you are migrating to is empty | | + +Other options: + +| Options | Required | Description | Notes | +| :---------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------- | +| `--shadow-database-url` | No | URL for the shadow database | Only required if using `--to-migrations` or `--from-migrations` | +| `--script` | No | Outputs a SQL script instead of the default human-readable summary | Not supported in MongoDB | +| `--exit-code` | No | Change the exit code behavior to signal if the diff is empty or not (Empty: 0, Error: 1, Not empty: 2). Default behavior is Success: 0, Error: 1. | | +| `--help` | No | Displays the help message. | | + +#### Examples + +- Compare two databases specified by their data source URL, and output the default human-readable summary: + + ```terminal + prisma migrate diff \ + --from-url "$DATABASE_URL" \ + --to-url "postgresql://login:password@localhost:5432/db2" + ``` + +- Compare the state of a database with a URL of `$DATABASE_URL` to the schema defined by the migrations in the `./prisma/migrations` directory, and output the differences to a script `script.sql`: + + ```terminal + prisma migrate diff \ + --from-url "$DATABASE_URL" \ + --to-migrations ./prisma/migrations \ + --shadow-database-url $SHADOW_DATABASE_URL \ + --script > script.sql + ``` + +## Studio + +### `studio` + +The `studio` command allows you to interact with and manage your data interactively. It does this by starting a local web server with a web app configured with your project's data schema and records. + +#### Prerequisites + +Before using the `studio` command, you must define a valid [`datasource`](/orm/prisma-schema/overview/data-sources) within your `schema.prisma` file. + +For example, the following `datasource` defines a SQLite database file within the current directory: + +```prisma +datasource db { + provider = "sqlite" + url = "file:my-database.db" +} +``` + +#### Options + +The `studio` command recognizes the following options: + +| Option | Required | Description | Default | +| ----------------- | -------- | ----------------------------------- | ------------------------ | +| `-b`, `--browser` | No | The browser to auto-open Studio in. | `` | +| `-h`, `--help` | No | Show all available options and exit | | +| `-p`, `--port` | No | The port number to start Studio on. | 5555 | + +#### Arguments + +| Argument | Required | Description | Default | +| :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------- | +| `--schema` | No | Specifies the path to the desired schema.prisma file to be processed instead of the default path. Both absolute and relative paths are supported. | `./schema.prisma`
`./prisma/schema.prisma` | + +#### Examples + +#### Start Studio on the default port and open a new browser tab to it + +```terminal +prisma studio +``` + +#### Start Studio on a different port and open a new browser tab to it + +```terminal +prisma studio --port 7777 +``` + +#### Start Studio and open a Firefox tab to it + +```terminal +prisma studio --browser firefox +``` + +#### Start Studio without opening a new browser tab to it + +```terminal +prisma studio --browser none +``` + +## `package.json` entry options + +### `schema` + +The path to the desired `schema.prisma` file can be specified with the `prisma.schema` entry in the `package.json` file. The path defines the file the Prisma CLI should use when you run any of the CLI commands. Both absolute and relative paths are supported. + +```json file="package.json" +{ + "name": "my-project", + "version": "1.0.0", + "prisma": { + "schema": "./custom-path-to-schema/schema.prisma" + } +} +``` + +This is available from version 2.7.0 and later. + +### `seed` + +The command used to populate the datasource is specified in the `prisma.seed` entry in the `package.json` file. It is used when `prisma db seed` is invoked or triggered. + +See [Seeding your database](/orm/prisma-migrate/workflows/seeding) + +```json file="package.json" +{ + "name": "my-project", + "version": "1.0.0", + "prisma": { + "seed": "node ./prisma/seed.js" + } +} +``` + +This is available from version 3.0.1 and later. + +## Using a HTTP proxy for the CLI + +Prisma CLI supports [custom HTTP proxies](https://github.com/prisma/prisma/issues/506). This is particularly relevant when being behind a corporate firewall. + +To activate usage of the proxy, provide either of the following environment variables: + +- [`HTTP_PROXY`](/orm/reference/environment-variables-reference#http_proxy) or `http_proxy`: Proxy URL for http traffic, for example `http://localhost:8080` +- [`HTTPS_PROXY`](/orm/reference/environment-variables-reference#https_proxy) or `https_proxy`: Proxy URL for https traffic, for example `https://localhost:8080` diff --git a/docs/200-orm/500-reference/250-error-reference.mdx b/docs/200-orm/500-reference/250-error-reference.mdx new file mode 100644 index 0000000000..e386b1fb49 --- /dev/null +++ b/docs/200-orm/500-reference/250-error-reference.mdx @@ -0,0 +1,475 @@ +--- +title: 'Error message reference' +navTitle: 'Errors' +metaTitle: 'Errors' +metaDescription: 'Prisma Client, Migrate, Introspection error message reference' +tocDepth: 3 +--- + + + +For more information about how to work with exceptions and error codes, see [Handling exceptions and errors](/orm/prisma-client/debugging-and-troubleshooting/handling-exceptions-and-errors). + + + +## Prisma Client error types + +Prisma Client throws different kinds of errors. The following lists the exception types, and their documented data fields: + +### `PrismaClientKnownRequestError` + +Prisma Client throws a `PrismaClientKnownRequestError` exception if the query engine returns a known error related to the request - for example, a unique constraint violation. + +| **Property** | **Description** | +| :-------------- | :--------------------------------------------------------------------------------------------------------------- | +| `code` | A Prisma-specific [error code](#error-codes). | +| `meta` | Additional information about the error - for example, the field that caused the error: `{ target: [ 'email' ] }` | +| `message` | Error message associated with [error code](#error-codes). | +| `clientVersion` | Version of Prisma Client (for example, `2.19.0`) | + +### `PrismaClientUnknownRequestError` + +Prisma Client throws a `PrismaClientUnknownRequestError` exception if the query engine returns an error related to a request that does not have an error code. + +| **Property** | **Description** | +| :-------------- | :-------------------------------------------------------- | +| `message` | Error message associated with [error code](#error-codes). | +| `clientVersion` | Version of Prisma Client (for example, `2.19.0`) | + +### `PrismaClientRustPanicError` + +Prisma Client throws a `PrismaClientRustPanicError` exception if the underlying engine crashes and exits with a non-zero exit code. In this case, Prisma Client or the whole Node process must be restarted. + +| **Property** | **Description** | +| :-------------- | :-------------------------------------------------------- | +| `message` | Error message associated with [error code](#error-codes). | +| `clientVersion` | Version of Prisma Client (for example, `2.19.0`) | + +### `PrismaClientInitializationError` + +Prisma Client throws a `PrismaClientInitializationError` exception if something goes wrong when the query engine is started and the connection to the database is created. This happens either: + +- When `prisma.$connect()` is called OR +- When the first query is executed + +Errors that can occur include: + +- The provided credentials for the database are invalid +- There is no database server running under the provided hostname and port +- The port that the query engine HTTP server wants to bind to is already taken +- A missing or inaccessible environment variable +- The query engine binary for the current platform could not be found (`generator` block) + +| **Property** | **Description** | +| :-------------- | :-------------------------------------------------------- | +| `errorCode` | A Prisma-specific error code. | +| `message` | Error message associated with [error code](#error-codes). | +| `clientVersion` | Version of Prisma Client (for example, `2.19.0`) | + +### `PrismaClientValidationError` + +Prisma Client throws a `PrismaClientValidationError` exception if validation fails - for example: + +- Missing field - for example, an empty `data: {}` property when creating a new record +- Incorrect field type provided (for example, setting a `Boolean` field to `"Hello, I like cheese and gold!"`) + +| **Property** | **Description** | +| :-------------- | :----------------------------------------------- | +| `message` | Error message. | +| `clientVersion` | Version of Prisma Client (for example, `2.19.0`) | + +## Error codes + +### Common + +#### `P1000` + +"Authentication failed against database server at `{database_host}`, the provided database credentials for `{database_user}` are not valid. Please make sure to provide valid database credentials for the database server at `{database_host}`." + +#### `P1001` + +"Can't reach database server at `{database_host}`:`{database_port}` Please make sure your database server is running at `{database_host}`:`{database_port}`." + +#### `P1002` + +"The database server at `{database_host}`:`{database_port}` was reached but timed out. Please try again. Please make sure your database server is running at `{database_host}`:`{database_port}`. " + +#### `P1003` + +"Database \{database_file_name} does not exist at \{database_file_path}" + +"Database `{database_name}.{database_schema_name}` does not exist on the database server at `{database_host}:{database_port}`." + +"Database `{database_name}` does not exist on the database server at `{database_host}:{database_port}`." + +#### `P1008` + +"Operations timed out after `{time}`" + +#### `P1009` + +"Database `{database_name}` already exists on the database server at `{database_host}:{database_port}`" + +#### `P1010` + +"User `{database_user}` was denied access on the database `{database_name}`" + +#### `P1011` + +"Error opening a TLS connection: \{message}" + +#### `P1012` + +**Note:** If you get error code P1012 after you upgrade Prisma to version 4.0.0 or later, see the [version 4.0.0 upgrade guide](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-4#upgrade-your-prisma-schema). A schema that was valid before version 4.0.0 might be invalid in version 4.0.0 and later. The upgrade guide explains how to update your schema to make it valid. + +"\{full_error}" + +Possible P1012 error messages: + +- "Argument `{}` is missing." +- "Function `{}` takes {} arguments, but received {}." +- "Argument `{}` is missing in attribute `@{}`." +- "Argument `{}` is missing in data source block `{}`." +- "Argument `{}` is missing in generator block `{}`." +- "Error parsing attribute `@{}`: {}" +- "Attribute `@{}` is defined twice." +- "The model with database name `{}` could not be defined because another model with this name exists: `{}`" +- "`{}` is a reserved scalar type name and can not be used." +- "The {} `{}` cannot be defined because a {} with that name already exists." +- "Key `{}` is already defined in {}." +- "Argument `{}` is already specified as unnamed argument." +- "Argument `{}` is already specified." +- "No such argument."" +- "Field `{}` is already defined on model `{}`." +- "Field `{}` in model `{}` can't be a list. The current connector does not support lists of primitive types." +- "The index name `{}` is declared multiple times. With the current connector index names have to be globally unique." +- "Value `{}` is already defined on enum `{}`." +- "Attribute not known: `@{}`." +- "Function not known: `{}`." +- "Datasource provider not known: `{}`." +- "shadowDatabaseUrl is the same as url for datasource `{}`. Please specify a different database as shadow database." +- "The preview feature `{}` is not known. Expected one of: {}" +- "`{}` is not a valid value for {}." +- "Type `{}` is neither a built-in type, nor refers to another model, custom type, or enum." +- "Type `{}` is not a built-in type." +- "Unexpected token. Expected one of: {}" +- "Environment variable not found: {}." +- "Expected a {} value, but received {} value `{}`." +- "Expected a {} value, but failed while parsing `{}`: {}." +- "Error validating model `{}`: {}" +- "Error validating field `{}` in model `{}`: {}" +- "Error validating datasource `{datasource}`: \{message}"" +- "Error validating enum `{}`: {}" +- "Error validating: {}" + +#### `P1013` + +"The provided database string is invalid. \{details}" + +#### `P1014` + +"The underlying \{kind} for model `{model}` does not exist." + +#### `P1015` + +"Your Prisma schema is using features that are not supported for the version of the database.
Database version: \{database_version}
Errors:
\{errors}" + +#### `P1016` + +"Your raw query had an incorrect number of parameters. Expected: `{expected}`, actual: `{actual}`." + +#### `P1017` + +"Server has closed the connection." + +### Prisma Client (Query Engine) + +#### `P2000` + +"The provided value for the column is too long for the column's type. Column: \{column_name}" + +#### `P2001` + +"The record searched for in the where condition (`{model_name}.{argument_name} = {argument_value}`) does not exist" + +#### `P2002` + +"Unique constraint failed on the \{constraint}" + +#### `P2003` + +"Foreign key constraint failed on the field: `{field_name}`" + +#### `P2004` + +"A constraint failed on the database: `{database_error}`" + +#### `P2005` + +"The value `{field_value}` stored in the database for the field `{field_name}` is invalid for the field's type" + +#### `P2006` + +"The provided value `{field_value}` for `{model_name}` field `{field_name}` is not valid" + +#### `P2007` + +"Data validation error `{database_error}`" + +#### `P2008` + +"Failed to parse the query `{query_parsing_error}` at `{query_position}`" + +#### `P2009` + +"Failed to validate the query: `{query_validation_error}` at `{query_position}`" + +#### `P2010` + +"Raw query failed. Code: `{code}`. Message: `{message}`" + +#### `P2011` + +"Null constraint violation on the \{constraint}" + +#### `P2012` + +"Missing a required value at `{path}`" + +#### `P2013` + +"Missing the required argument `{argument_name}` for field `{field_name}` on `{object_name}`." + +#### `P2014` + +"The change you are trying to make would violate the required relation '\{relation_name}' between the `{model_a_name}` and `{model_b_name}` models." + +#### `P2015` + +"A related record could not be found. \{details}" + +#### `P2016` + +"Query interpretation error. \{details}" + +#### `P2017` + +"The records for relation `{relation_name}` between the `{parent_name}` and `{child_name}` models are not connected." + +#### `P2018` + +"The required connected records were not found. \{details}" + +#### `P2019` + +"Input error. \{details}" + +#### `P2020` + +"Value out of range for the type. \{details}" + +#### `P2021` + +"The table `{table}` does not exist in the current database." + +#### `P2022` + +"The column `{column}` does not exist in the current database." + +#### `P2023` + +"Inconsistent column data: \{message}" + +#### `P2024` + +"Timed out fetching a new connection from the connection pool. (More info: [http://pris.ly/d/connection-pool](http://pris.ly/d/connection-pool) (Current connection pool timeout: \{timeout}, connection limit: \{connection_limit})" + +#### `P2025` + +"An operation failed because it depends on one or more records that were required but not found. \{cause}" + +#### `P2026` + +"The current database provider doesn't support a feature that the query used: \{feature}" + +#### `P2027` + +"Multiple errors occurred on the database during query execution: \{errors}" + +#### `P2028` + +"Transaction API error: \{error}" + +#### `P2030` + +"Cannot find a fulltext index to use for the search, try adding a @@fulltext([Fields...]) to your schema" + +#### `P2031` + +"Prisma needs to perform transactions, which requires your MongoDB server to be run as a replica set. See details: [https://pris.ly/d/mongodb-replica-set](https://pris.ly/d/mongodb-replica-set)" + +#### `P2033` + +"A number used in the query does not fit into a 64 bit signed integer. Consider using `BigInt` as field type if you're trying to store large integers" + +#### `P2034` + +"Transaction failed due to a write conflict or a deadlock. Please retry your transaction" + +### Prisma Migrate (Schema Engine) + + + +The Schema Engine was previously called Migration Engine. This change was introduced in version [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0). + + + +#### `P3000` + +"Failed to create database: \{database_error}" + +#### `P3001` + +"Migration possible with destructive changes and possible data loss: \{migration_engine_destructive_details}" + +#### `P3002` + +"The attempted migration was rolled back: \{database_error}" + +#### `P3003` + +"The format of migrations changed, the saved migrations are no longer valid. To solve this problem, please follow the steps at: [https://pris.ly/d/migrate](https://pris.ly/d/migrate)" + +#### `P3004` + +"The `{database_name}` database is a system database, it should not be altered with prisma migrate. Please connect to another database." + +#### `P3005` + +"The database schema is not empty. Read more about how to baseline an existing production database: [https://pris.ly/d/migrate-baseline](https://pris.ly/d/migrate-baseline)" + +#### `P3006` + +"Migration `{migration_name}` failed to apply cleanly to the shadow database.
\{error_code}Error:
\{inner_error}" + +#### `P3007` + +"Some of the requested preview features are not yet allowed in schema engine. Please remove them from your data model before using migrations. (blocked: \{list_of_blocked_features})" + +#### `P3008` + +"The migration `{migration_name}` is already recorded as applied in the database." + +#### `P3009` + +"migrate found failed migrations in the target database, new migrations will not be applied. Read more about how to resolve migration issues in a production database: [https://pris.ly/d/migrate-resolve](https://pris.ly/d/migrate-resolve)
\{details}" + +#### `P3010` + +"The name of the migration is too long. It must not be longer than 200 characters (bytes)." + +#### `P3011` + +"Migration `{migration_name}` cannot be rolled back because it was never applied to the database. Hint: did you pass in the whole migration name? (example: \"20201207184859_initial_migration\")" + +#### `P3012` + +"Migration `{migration_name}` cannot be rolled back because it is not in a failed state." + +#### `P3013` + +"Datasource provider arrays are no longer supported in migrate. Please change your datasource to use a single provider. Read more at [https://pris.ly/multi-provider-deprecation](https://pris.ly/multi-provider-deprecation)" + +#### `P3014` + +"Prisma Migrate could not create the shadow database. Please make sure the database user has permission to create databases. Read more about the shadow database (and workarounds) at [https://pris.ly/d/migrate-shadow](https://pris.ly/d/migrate-shadow). + +Original error: \{error_code}
\{inner_error}" + +#### `P3015` + +"Could not find the migration file at \{migration_file_path}. Please delete the directory or restore the migration file." + +#### `P3016` + +"The fallback method for database resets failed, meaning Migrate could not clean up the database entirely. Original error: \{error_code}
\{inner_error}" + +#### `P3017` + +"The migration \{migration_name} could not be found. Please make sure that the migration exists, and that you included the whole name of the directory. (example: \"20201207184859_initial_migration\")" + +#### `P3018` + +"A migration failed to apply. New migrations cannot be applied before the error is recovered from. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve

Migration name: \{migration_name}

Database error code: \{database_error_code}

Database error:
\{database_error} " + +#### `P3019` + +"The datasource provider `{provider}` specified in your schema does not match the one specified in the migration_lock.toml, `{expected_provider}`. Please remove your current migration directory and start a new migration history with prisma migrate dev. Read more: [https://pris.ly/d/migrate-provider-switch](https://pris.ly/d/migrate-provider-switch)" + +#### `P3020` + +"The automatic creation of shadow databases is disabled on Azure SQL. Please set up a shadow database using the `shadowDatabaseUrl` datasource attribute.
Read the docs page for more details: [https://pris.ly/d/migrate-shadow](https://pris.ly/d/migrate-shadow)" + +#### `P3021` + +"Foreign keys cannot be created on this database. Learn more how to handle this: [https://pris.ly/d/migrate-no-foreign-keys](https://pris.ly/d/migrate-no-foreign-keys)" + +#### `P3022` + +"Direct execution of DDL (Data Definition Language) SQL statements is disabled on this database. Please read more here about how to handle this: [https://pris.ly/d/migrate-no-direct-ddl](https://pris.ly/d/migrate-no-direct-ddl)" + +### `prisma db pull` + +#### `P4000` + +"Introspection operation failed to produce a schema file: \{introspection_error}" + +#### `P4001` + +"The introspected database was empty." + +#### `P4002` + +"The schema of the introspected database was inconsistent: \{explanation}" + + + +### Prisma Accelerate + +Prisma Accelerate-related errors start with `P6xxx`. + +#### `P6000` (`ServerError`) + +Generic error to catch all other errors. + +#### `P6001` (`InvalidDataSource`) + +The URL is malformed; for instance, it does not use the `prisma://` protocol. + +#### `P6002` (`Unauthorized`) + +The API Key in the connection string is invalid. + +#### `P6003` (`PlanLimitReached`) + +The included usage of the current plan has been exceeded. This can only occur on the [free plan](https://www.prisma.io/pricing). + +#### `P6004` (`QueryTimeout`) + +The global timeout of Accelerate has been exceeded. You can find the limit [here](/accelerate/limitations#query-timeout-limit). + +#### `P6005` (`InvalidParameters`) + +The user supplied invalid parameters. Currently only relevant for transaction methods. For example, setting a timeout that is too high. You can find the limit [here](/accelerate/limitations#interactive-transactions-query-timeout-limit). + +#### `P6006` (`VersionNotSupported`) + +The chosen Prisma version is not compatible with Accelerate. This may occur when a user uses an unstable development version that we occasionally prune. + +#### `P6008` (`ConnectionError|EngineStartError`) + +The engine failed to start. For example, it couldn't establish a connection to the database. + +#### `P6009` (`ResponseSizeLimitExceeded`) + +The global response size limit of Accelerate has been exceeded. You can find the limit [here](/accelerate/limitations#response-size-limit). diff --git a/docs/200-orm/500-reference/300-environment-variables-reference.mdx b/docs/200-orm/500-reference/300-environment-variables-reference.mdx new file mode 100644 index 0000000000..501092d2ed --- /dev/null +++ b/docs/200-orm/500-reference/300-environment-variables-reference.mdx @@ -0,0 +1,279 @@ +--- +title: 'Environment variables reference' +navTitle: 'Environment variables' +metaTitle: 'Prisma environment variables' +metaDescription: 'This page gives an overview of all environment variables available for use.' +tocDepth: 3 +--- + + + +This document describes different environment variables and their use cases. + + + +## Prisma Client + +### `DEBUG` + +`DEBUG` is used to enable debugging output in Prisma Client. + +Example setting Prisma Client level debugging output: + +```terminal +# enable only `prisma:client`-level debugging output +export DEBUG="prisma:client" +``` + +See [Debugging](/orm/prisma-client/debugging-and-troubleshooting/debugging) for more information. + +### `NO_COLOR` + +`NO_COLOR` if [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) will activate the `colorless` setting for error formatting and strip colors from error messages. + +See [Formatting via environment variables](/orm/prisma-client/setup-and-configuration/error-formatting#formatting-via-environment-variables) for more information. + +## Prisma Studio + +### `BROWSER` + +`BROWSER` is for Prisma Studio to force which browser it should be open in, if not set it will open in the default browser. + +```terminal +BROWSER=firefox prisma studio --port 5555 +``` + +Alternatively you can set this when starting Studio from the CLI as well: + +```terminal +prisma studio --browser firefox +``` + +See [Studio](/orm/reference/prisma-cli-reference#studio) documentation for more information. + +## Prisma CLI + +### `PRISMA_HIDE_PREVIEW_FLAG_WARNINGS` + +`PRISMA_HIDE_PREVIEW_FLAG_WARNINGS` hides the warning message that states that a preview feature flag can be removed. It is a truthy value. + +### `PRISMA_HIDE_UPDATE_MESSAGE` + +`PRISMA_HIDE_UPDATE_MESSAGE` is used to hide the update notification message that is shown when a newer Prisma CLI version is available. It's a truthy value. + +### `PRISMA_GENERATE_SKIP_AUTOINSTALL` + +`PRISMA_GENERATE_SKIP_AUTOINSTALL` can be set to a truthy value to skip the auto-install of `prisma` CLI and `@prisma/client` dependencies (if they are missing), if the `prisma-client-js` generator is defined in the Prisma Schema, when using the `prisma generate` command. + +### `PRISMA_SKIP_POSTINSTALL_GENERATE` + +`PRISMA_SKIP_POSTINSTALL_GENERATE` can be set to a truthy value to skip the auto-generation of Prisma Client when its `postinstall` hook is triggered by a package manager. The `postinstall` hook of the `@prisma/client` package is triggered when the package is installed, or its version is updated. + +### `PRISMA_DISABLE_WARNINGS` + +Disables all CLI warnings generated by `logger.warn`. + +### `PRISMA_GENERATE_NO_ENGINE` + + + +This environment variable is available since version `5.2.0` + + + +`PRISMA_GENERATE_NO_ENGINE` can be set to a truthy value to generate a Prisma Client without an included [query engine](/orm/more/under-the-hood/engines) in order to reduce deployed application size when paired with [Prisma Accelerate](/accelerate). + +### `PRISMA_SCHEMA_DISABLE_ADVISORY_LOCK` + + + +This environment variable is available since version `5.3.0` + + + +`PRISMA_SCHEMA_DISABLE_ADVISORY_LOCK` can be set to a truthy value to disable the [advisory locking](/orm/prisma-migrate/workflows/development-and-production#advisory-locking) used by Prisma Migrate. This might be needed, depending on the database configuration, for example, for a Percona-XtraDB-Cluster or MariaDB Galera Cluster. + +## Proxy environment variables + +The Prisma CLI supports custom HTTP(S) proxies to download the Prisma engines. These can be helpful to use when working behind a corporate firewall. See [Using a HTTP proxy for the CLI](/orm/reference/prisma-cli-reference#using-a-http-proxy-for-the-cli) for more information. + +### `NO_PROXY` + +`NO_PROXY` is a comma-separated list of hostnames or IP addresses that do not require a proxy. + +```env +NO_PROXY=myhostname.com,10.11.12.0/16,172.30.0.0/16 +``` + +### `HTTP_PROXY` + +`HTTP_PROXY` is set with the hostname or IP address of a proxy server. + +```env +HTTP_PROXY=http://proxy.example.com +``` + +### `HTTPS_PROXY` + +`HTTPS_PROXY` is set with the hostname or IP address of a proxy server. + +```env +HTTPS_PROXY=https://proxy.example.com +``` + +## Engine environment variables + +### Configuring Query Engine Type + +#### `PRISMA_CLI_QUERY_ENGINE_TYPE` + +`PRISMA_CLI_QUERY_ENGINE_TYPE` is used to [define the query engine type Prisma CLI downloads and uses](/orm/more/under-the-hood/engines#configuring-the-query-engine). Defaults to `library`, but can be set to `binary`: + +```env +PRISMA_CLI_QUERY_ENGINE_TYPE=binary +``` + +#### `PRISMA_CLIENT_ENGINE_TYPE` + +`PRISMA_CLIENT_ENGINE_TYPE` is used to [define the query engine type Prisma Client downloads and uses](/orm/more/under-the-hood/engines#configuring-the-query-engine). Defaults to `library`, but can be set to `binary`: + +```env +PRISMA_CLIENT_ENGINE_TYPE=binary +``` + +Note: You need to generate your Prisma Client after setting this variable for the configuration to take effect and the libraries to be downloaded. Otherwise, Prisma Client will be missing the appropriate query engine library and you will _have to_ define their location using [`PRISMA_QUERY_ENGINE_LIBRARY`](#prisma_query_engine_library). + +It is the environment variable equivalent for the [`engineType` property of the `generator` block](/orm/more/under-the-hood/engines#configuring-the-query-engine) which enables you to define the same setting in your Prisma Schema. + +### Downloading Engines + +#### `PRISMA_ENGINES_MIRROR` + +`PRISMA_ENGINES_MIRROR` can be used to specify a custom CDN (or server) endpoint to download the engines files for the CLI/Client. The default value is `https://binaries.prisma.sh`, where Prisma hosts the engine files. + +```env +PRISMA_ENGINES_MIRROR=https://example.org/custom-engines/ +``` + +See [Prisma engines](/orm/more/under-the-hood/engines#hosting-engines) for a conceptual overview of how to use this environment variable. + +Note: This environment variable used to be available as `PRISMA_BINARIES_MIRROR`, which was deprecated in Prisma 3.0.1. It is discouraged to use anymore and will be removed in the future. + +#### `PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING` + + + +This environment variable is available since version `4.16.0` + + + +`PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING` can be can be set to a truthy value to ignore problems around downloading & verifying the integrity (via a checksum file) of the Prisma engines. +This is particularly useful when deploying to an offline system environment where the checksum file cannot be downloaded. + +```env +PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1 +``` + +Note: we might change the overall download behavior in a future release in a way that this environment variable will not be needed anymore in a offline environment case. + +### Custom engine file locations + +By default, all engine files are downloaded when you install Prisma CLI, copied when generating Prisma Client, and put into known locations. There are however situations where you may want to use a custom engine file from custom locations: + +#### `PRISMA_QUERY_ENGINE_BINARY` + +`PRISMA_QUERY_ENGINE_BINARY` is used to set a custom location for your own query engine binary. + +```env +PRISMA_QUERY_ENGINE_BINARY=custom/query-engine- +# Example: ./prisma/binaries/query-engine-linux-arm64-openssl-1.0.x +``` + +For Prisma CLI it allows you to define the query engine file to be used. +For Prisma Client, on build time (during `prisma generate`), it defines where the query engine file will be copied from into Prisma Client. At run time (when using the generated Client) it can be used to define the specific query engine file to be used instead of the included one. + +Note: This can only have an effect if the engine type of CLI or Client are set to `binary`. If the engine type is `library` (the default), use PRISMA_QUERY_ENGINE_LIBARY instead. + +#### `PRISMA_QUERY_ENGINE_LIBRARY` + +`PRISMA_QUERY_ENGINE_LIBRARY` is used to set a custom location for your own query engine library. + +```env +PRISMA_QUERY_ENGINE_LIBRARY=custom/libquery_engine-.so.node +# Example: ./prisma/binaries/libquery_engine-linux-arm64-openssl-1.0.x.so.node +``` + +For Prisma CLI it allows you to define the query engine file to be used. +For Prisma Client, on build time (during `prisma generate`), it defines where the query engine file will be copied from into Prisma Client. At run time (when using the generated Client) it can be used to define the specific query engine file to be used instead of the included one. + +Note: This can only have an effect if the engine type of CLI or Client are set to `library` (the default) + +#### `PRISMA_SCHEMA_ENGINE_BINARY` + +`PRISMA_SCHEMA_ENGINE_BINARY` is used to set a custom location for your Schema engine binary. + +```env +PRISMA_SCHEMA_ENGINE_BINARY=custom/my-schema-engine-unix +``` + +#### `PRISMA_MIGRATION_ENGINE_BINARY` + + + +**Deprecated**: `PRISMA_MIGRATION_ENGINE_BINARY` variable is deprecated in [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0) because Migration engine was renamed to Schema Engine. + + + +`PRISMA_MIGRATION_ENGINE_BINARY` is used to set a custom location for your own migration engine binary. + +```env +PRISMA_MIGRATION_ENGINE_BINARY=custom/my-migration-engine-unix +``` + +#### `PRISMA_INTROSPECTION_ENGINE_BINARY` + +`PRISMA_INTROSPECTION_ENGINE_BINARY` is used to set a custom location for your own introspection engine binary. + +```env +PRISMA_INTROSPECTION_ENGINE_BINARY=custom/my-introspection-engine-unix +``` + + + +The Introspection Engine is served by the Migration Engine from [4.9.0](https://github.com/prisma/prisma/releases/tag/4.9.0). Therefore, the `PRISMA_INTROSPECTION_ENGINE` environment variable will not be used. + + + +#### `PRISMA_FMT_BINARY` + + + +This functionality has been removed in Prisma CLI version 4.10.0. It only works in earlier versions. + + + +`PRISMA_FMT_BINARY` is used to set a custom location for your own format engine binary. + +```env +PRISMA_FMT_BINARY=custom/my-custom-format-engine-unix +``` + + + +The `PRISMA_FMT_BINARY` variable is used in versions [4.2.0](https://github.com/prisma/prisma/releases/tag/4.2.0) or lower. + + + +### CLI Binary Targets + +#### `PRISMA_CLI_BINARY_TARGETS` + +`PRISMA_CLI_BINARY_TARGETS` can be used to specify one or more binary targets that Prisma CLI will download during installation (so it must be provided during `npm install` of Prisma CLI and does not affect runtime of Prisma CLI or Prisma Client). + +Use `PRISMA_CLI_BINARY_TARGETS` if you 1) deploy to a specific platform via an upload of a local project that includes dependencies, and 2) your local environment is different from the target (e.g. AWS Lambda is `rhel-openssl-1.0.x`, and your local environment might be macOS arm64 `darwin-arm64`). Using the `PRISMA_CLI_BINARY_TARGETS` environment variable ensures that the target engine files are also downloaded. + +```terminal +PRISMA_CLI_BINARY_TARGETS=darwin-arm64,rhel-openssl-1.0.x npm install +``` + +This is the Prisma CLI equivalent for the [`binaryTargets` property of the `generator` block](/orm/prisma-schema/overview/generators#binary-targets), which enables you to define the same setting for Prisma Client. diff --git a/docs/200-orm/500-reference/350-database-features.mdx b/docs/200-orm/500-reference/350-database-features.mdx new file mode 100644 index 0000000000..18590c5754 --- /dev/null +++ b/docs/200-orm/500-reference/350-database-features.mdx @@ -0,0 +1,113 @@ +--- +title: 'Database features matrix' +metaTitle: 'Database features matrix' +metaDescription: 'Learn which database features are supported in Prisma and how they map to the different Prisma tools.' +wide: true +tocDepth: 3 +--- + + + +This page gives an overview of the features which are provided by the databases that Prisma supports. Additionally, it explains how each of these features can be used in Prisma with pointers to further documentation. + +> **Note**: If a feature is not supported natively by the database, it's also not available in Prisma. + + + +## Relational database features + +This section describes which database features exist on the relational databases that are currently supported by Prisma. The **Prisma schema** column indicates how a certain feature can be represented in the [Prisma schema](/orm/prisma-schema) and links to its documentation. Note that database features can be used in **Prisma Client** even though they might not yet be representable in the Prisma schema. + +### Constraints + +| Constraint | PostgreSQL | Microsoft SQL Server | MySQL | SQLite | CockroachDB | Prisma schema | Prisma Client | Prisma Migrate | +| ------------- | :--------: | :------------------: | :---: | :----: | :---------: | :--------------------------------------------------------------------------------------: | :-----------: | :------------: | +| `PRIMARY KEY` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [`@id` and `@@id`](/orm/prisma-schema/data-model/models#defining-an-id-field) | ✔️ | ✔️ | +| `FOREIGN KEY` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [Relation fields](/orm/prisma-schema/data-model/relations#relation-fields) | ✔️ | ✔️ | +| `UNIQUE` | ✔️ | ✔️† | ✔️ | ✔️ | ✔️ | [`@unique` and `@@unique`](/orm/prisma-schema/data-model/models#defining-a-unique-field) | ✔️ | ✔️ | +| `CHECK` | ✔️ | ✔️ | ✔️\* | ✔️ | ✔️ | Not yet | ✔️ | Not yet | +| `NOT NULL` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [`?`](/orm/prisma-schema/data-model/models#type-modifiers) | ✔️ | ✔️ | +| `DEFAULT` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [`@default`](/orm/prisma-schema/data-model/models#defining-a-default-value) | ✔️ | ✔️ | + +- \*In [MySQL 8 and higher](https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html) +- † [Caveats apply when using the `UNIQUE` constraint with Microsoft SQL Server](/orm/overview/databases/sql-server#data-model-limitations) + +### Referential Actions (Delete and Update behaviors for foreign key references) + +| Deletion behavior | PostgreSQL | Microsoft SQL Server | MySQL | SQLite | CockroachDB | Prisma schema | Prisma Client | Prisma Migrate | +| ----------------- | :--------: | :------------------: | :---: | :----: | :---------: | :-----------: | :-----------: | :------------: | +| `CASCADE` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | **✔️**† | ✔️ | **✔️**† | +| `RESTRICT` | ✔️ | No | ✔️ | ✔️ | ✔️ | **✔️**† | ✔️ | **✔️**† | +| `NO ACTION` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | **✔️**† | ✔️ | **✔️**† | +| `SET DEFAULT` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | **✔️**† | ✔️ | **✔️**† | +| `SET NULL` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | **✔️**† | ✔️ | **✔️**† | + +- † In [2.26.0](https://github.com/prisma/prisma/releases/tag/2.26.0) and later you can define [referential actions](/orm/prisma-schema/data-model/relations/referential-actions) on your relation fields. Referential actions determine what should happen to a record when a related record is deleted or updated. + +### Indexes + +| Index | PostgreSQL | Microsoft SQL Server | MySQL | SQLite | CockroachDB | Prisma schema | Prisma Client | Prisma Migrate | +| -------------- | :--------: | :------------------: | :---: | :----: | :---------: | :---------------------------------------------------------------------------------------------------------------------: | :-----------: | :------------: | +| `UNIQUE` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [`@unique` and `@@unique`](/orm/prisma-schema/data-model/models#defining-a-unique-field) | ✔️ | ✔️ | +| `USING` | ✔️ | No | No | No | ✔️ | [`type`](/orm/prisma-schema/data-model/indexes#configuring-the-access-type-of-indexes-with-type-postgresql) | ✔️ | ✔️ | +| `WHERE` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | Not yet | ✔️ | Not yet | +| `(expression)` | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | Not yet | ✔️ | Not yet | +| `INCLUDE` | ✔️ | ✔️ | No | No | ✔️ | Not yet | ✔️ | Not yet | + +- † Available in preview in 3.6.0 and later and in general availability in 4.0.0 and later, with the PostgreSQL connector only. + +Algorithm specified via `USING`: + +| Index type (Algorithm) | PostgreSQL | Microsoft SQL Server | MySQL | SQLite | CockroachDB | Prisma schema | Prisma Client | Prisma Migrate | +| ---------------------- | :--------: | :------------------: | :---: | :----: | :---------: | :-----------: | :-----------: | :------------: | +| B-tree | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️† | ✔️ | Not yet | +| Hash | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️† | ✔️ | Not yet | +| GiST | ✔️ | ✔️ | No | No | ✔️ | ✔️† | ✔️\* | Not yet | +| GIN | ✔️ | ✔️ | No | No | ✔️ | ✔️† | ✔️\* | Not yet | +| BRIN | ✔️ | ✔️ | No | No | ✔️ | ✔️† | ✔️\* | Not yet | +| SP-GiST | ✔️ | ✔️ | No | No | ✔️ | ✔️† | ✔️\* | Not yet | + +- \* Only available if natively supported by database. +- † Available with the PostgreSQL connector only in Prisma versions `4.0.0` and later. + +### Misc + +| Feature | PostgreSQL | Microsoft SQL Server | MySQL | SQLite | CockroachDB | Prisma schema | Prisma Client | Prisma Migrate | +| --------------------------------- | :--------: | :------------------: | :---: | :----: | :---------: | :--------------------------------------------------------------------------------: | :-----------: | :------------: | +| Autoincrementing IDs | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | [`autoincrement()`](/orm/prisma-schema/data-model/models#defining-a-default-value) | ✔️ | ✔️ | +| Arrays | ✔️ | No | No | No | ✔️ | [`[]`](/orm/prisma-schema/data-model/models#type-modifiers) | ✔️\* | ✔️\* | +| Enums | ✔️ | No | ✔️ | No | ✔️ | [`enum`](/orm/prisma-schema/data-model/models#defining-enums) | ✔️\* | ✔️\* | +| Native database types | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | Not yet | +| SQL Views | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | Not yet | Not yet | Not yet | +| JSON support | ✔️ | **✔️**† | ✔️ | No | ✔️‡ | ✔️\* | ✔️\* | ✔️\* | +| Fuzzy/Phrase full text search | ✔️ | ✔️ | ✔️ | No | ✔️ | Not yet | Not yet | Not yet | +| Table inheritance | ✔️ | ✔️ | No | No | ✔️ | Not yet | ✔️\* | Not yet | +| Authorization and user management | ✔️ | ✔️ | ✔️ | No | ✔️ | Not yet | Not yet | Not yet | + +- \* Only available if natively supported by database. +- † Only supports JSON through SQL functions, but doesn't have a JSON column type. Therefore client JSON operations are not supported. +- ‡ JSON arrays are not yet supported: see the [CockroachDB connector page](/orm/overview/databases/cockroachdb) for details + +## NoSQL database features + +This section describes which database features exist on the NoSQL databases that are currently supported by Prisma. + +### MongoDB + +The following table lists common MongoDB features and describes the level of support offered by Prisma: + +| Feature | Supported by Prisma | Notes | +| ----------------------------------------- | :-----------------: | :---------------------------------------------------------------------------------------------: | +| Embedded documents | ✔️ | | +| Transactions | ✔️ | | +| Indexes | ✔️ with caveats | Indexes can only be introspected if the field they refer to includes at least some data. | +| Autoincrementing IDs | No | | +| Compound IDs | No | MongoDB does not support composite IDs (`@@id`) | +| Generated `ObjectId` | ✔️ | See: [Defining IDs for MongoDB](/orm/prisma-schema/data-model/models#defining-ids-in-mongodb) | +| Arrays | ✔️ | | +| Enums | ✔️ | Implemented at Prisma level | +| Native database types | ✔️ | See: [Field mapping reference](/orm/reference/prisma-schema-reference#model-field-scalar-types) | +| JSON support | ✔️ | Advanced `Json` field filtering is not yet supported. | +| DBrefs | No | +| Change streams | No | +| Direct access to the aggregation pipeline | No | diff --git a/docs/200-orm/500-reference/375-supported-databases.mdx b/docs/200-orm/500-reference/375-supported-databases.mdx new file mode 100644 index 0000000000..fdaaf78d57 --- /dev/null +++ b/docs/200-orm/500-reference/375-supported-databases.mdx @@ -0,0 +1,53 @@ +--- +title: 'Supported databases' +metaTitle: 'Databases supported by Prisma' +metaDescription: 'This page lists all the databases and their versions that are supported by Prisma.' +--- + + + +Prisma currently supports the following databases. + +> See also: [System requirements](/orm/reference/system-requirements). + + + +An asterisk (\*) indicates that the version number is not relevant; either all versions are supported, there is not a public version number, etc. + +## Self-hosted databases + +| Database | Version | +| -------------------- | ------- | +| CockroachDB | 21.2.4+ | +| MariaDB | 10 | +| Microsoft SQL Server | 2017 | +| Microsoft SQL Server | 2019 | +| Microsoft SQL Server | 2022 | +| MongoDB | 4.2+ | +| MySQL | 5.6 | +| MySQL | 5.7 | +| MySQL | 8 | +| PostgreSQL | 9.6 | +| PostgreSQL | 10 | +| PostgreSQL | 11 | +| PostgreSQL | 12 | +| PostgreSQL | 13 | +| PostgreSQL | 14 | +| PostgreSQL | 15 | +| SQLite | \* | + +Note that a fixed version of SQLite is shipped with every Prisma release. + +## Managed databases + +| Database | Version | +| ---------------------------- | ------- | +| AWS Aurora | \* | +| AWS Aurora Serverless ¹ | \* | +| Azure SQL | \* | +| CockroachDB-as-a-Service | \* | +| MongoDB Atlas | \* | +| Neon Serverless Postgres | \* | +| PlanetScale | \* | + +¹ This does not include support for [Data API for Aurora Serverless](https://github.com/prisma/prisma/issues/1964). diff --git a/docs/200-orm/500-reference/380-connection-urls.mdx b/docs/200-orm/500-reference/380-connection-urls.mdx new file mode 100644 index 0000000000..28b6dbbbf0 --- /dev/null +++ b/docs/200-orm/500-reference/380-connection-urls.mdx @@ -0,0 +1,119 @@ +--- +title: 'Connection URLs' +metaTitle: 'Connection URLs (Reference)' +metaDescription: 'Learn about the format and syntax Prisma uses for defining database connection URLs for PostgreSQL, MySQL and SQLite.' +tocDepth: 3 +--- + + + +Prisma needs a connection URL to be able to connect to your database, e.g. when sending queries with [Prisma Client](/orm/prisma-client) or when changing the database schema with [Prisma Migrate](/orm/prisma-migrate). + +The connection URL is provided via the `url` field of a `datasource` block in your Prisma schema. It generally consists of the following components (except for SQLite): + +- **User**: The name of your database user +- **Password**: The password for your database user +- **Host**: The IP or domain name of the machine where your database server is running +- **Port**: The port on which your database server is running +- **Database name**: The name of the database you want to use + +Make sure you have this information at hand when getting started with Prisma. If you don't have a database server running yet, you can either use a local SQLite database file (see the [Quickstart](/getting-started/quickstart)) or [setup a free PostgreSQL database on Supabase](https://dev.to/prisma/set-up-a-free-postgresql-database-on-supabase-to-use-with-prisma-3pk6). + + + +## Format + +The format of the connection URL depends on the _database connector_ you're using. Prisma generally supports the standard formats for each database. You can find out more about the connection URL of your database on the dedicated docs page: + +- [PostgreSQL](/orm/overview/databases/postgresql) +- [MySQL](/orm/overview/databases/mysql) +- [SQLite](/orm/overview/databases/sqlite) +- [MongoDB](/orm/overview/databases/mongodb) +- [Microsoft SQL Server](/orm/overview/databases/sql-server) +- [CockroachDB](/orm/overview/databases/cockroachdb) + +### Special characters + +For MySQL, PostgreSQL and CockroachDB you must [percentage-encode special characters](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) in any part of your connection URL - including passwords. For example, `p@$$w0rd` becomes `p%40%24%24w0rd`. + +For Microsoft SQL Server, you must [escape special characters](/orm/overview/databases/sql-server#connection-details) in any part of your connection string. + +## Examples + +Here are examples for the connection URLs of the databases Prisma supports: + +### PostgreSQL + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = "postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample" +} +``` + +### MySQL + +```prisma file=schema.prisma +datasource db { + provider = "mysql" + url = "mysql://janedoe:mypassword@localhost:3306/mydb" +} +``` + +### Microsoft SQL Server + +```prisma file=schema.prisma +datasource db { + provider = "sqlserver" + url = "sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;" +} +``` + +### SQLite + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} +``` + +### CockroachDB + +```prisma file=schema.prisma +datasource db { + provider = "cockroachdb" + url = "postgresql://janedoe:mypassword@localhost:26257/mydb?schema=public" +} +``` + +### MongoDB + +```prisma file=schema.prisma +datasource db { + provider = "mongodb" + url = "mongodb+srv://root:@cluster0.ab1cd.mongodb.net/myDatabase?retryWrites=true&w=majority" +} +``` + +## .env + +You can also provide the connection URL as an environment variable: + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +You can then either set the environment variable in your terminal or by providing a [dotenv](https://github.com/motdotla/dotenv) file named `.env`. This will automatically be picked up by the Prisma CLI. + +Prisma reads the connection URL from the dotenv file in the following situations: + +- When it updates the schema during build time +- When it connects to the database during run time + +``` +DATABASE_URL=postgresql://janedoe:mypassword@localhost:5432/mydb +``` diff --git a/docs/200-orm/500-reference/400-system-requirements.mdx b/docs/200-orm/500-reference/400-system-requirements.mdx new file mode 100644 index 0000000000..ae53a63eae --- /dev/null +++ b/docs/200-orm/500-reference/400-system-requirements.mdx @@ -0,0 +1,131 @@ +--- +title: 'System requirements' +metaTitle: 'System requirements (Reference)' +metaDescription: 'System requirements for running Prisma' +tocDepth: 3 +--- + + + +This page provides an overview of the system requirements for Prisma. + + + +## System requirements + +This section lists the software that Prisma requires and the supported operating systems, along with runtime dependency requirements for specific operating systems. + +### Software requirements + +The latest version of Prisma requires the following software: + +| | Minimum required version | +| :-------------------- | :----------------------- | +| Node.js | 16.13 / 18.X / 20.X | +| TypeScript (optional) | 4.7.X | +| Yarn (optional) | 1.19.2 | + +Notes: + +- Prisma supports and tests all _Active LTS_ and _Maintenance LTS_ **Node.js** releases. [Releases that are not in these states like _Current_, and also odd-numbered versions](https://nodejs.org/en/about/releases/) probably also work, but are not recommended for production use. +- **TypeScript** is only required for TypeScript users. +- When using **Yarn 1**, `1.19.2` is the minimum version compatible with Prisma Client. + +See also: [Supported database versions](/orm/reference/supported-databases) + +### Operating systems + +Prisma is supported on MacOS, Windows and most Linux distributions. + +#### Linux runtime dependencies + +Prisma requires the following system libraries to be installed to work: + +- OpenSSL 1.0.x, 1.1.x or 3.x +- zlib (`libz.so.1`) +- libgcc (`libgcc_s.so.1`) +- C standard library (glibc on most Linux distributions or musl libc on Alpine Linux) + +The following two tables show the supported Linux distro families, OpenSSL versions and C standard libraries for each CPU architecture. + +On `AMD64` (`x86_64`) architecture: + +| Distro family | OpenSSL version | libc version | +| ---------------- | ----------------- | ------------ | +| Alpine | 1.1.x, 3.x | musl 1.2.x | +| RHEL | 1.0.x, 1.1.x, 3.x | glibc 2.17+ | +| Debian or others | 1.0.x | glibc 2.19+ | +| Debian or others | 1.1.x, 3.x | glibc 2.24+ | + +On `ARM64` (`aarch64`) architecture: + +| Distro family | OpenSSL version | libc version | +| ---------------- | ----------------- | ------------ | +| Alpine | 1.1.x, 3.x | musl 1.2.x | +| RHEL | 1.0.x, 1.1.x, 3.x | glibc 2.24+ | +| Debian or others | 1.0.x, 1.1.x, 3.x | glibc 2.24+ | + +When Prisma can not resolve the OpenSSL version on a system (e.g. because it is not installed), it will default to OpenSSL 1.1.x. + +Systems that can run the supported Node.js versions will most likely have zlib and libgcc available. One notable exception is Google's Distroless images, where `libz.so.1` needs to be copied from a compatible Debian system. + +#### Windows runtime dependencies + +On Windows [Microsoft Visual C++ Redistributable 2015](https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe) or newer must be installed (which is by default the case on most modern installations). + +#### macOS runtime dependencies + +Prisma supports macOS 10.15 or newer. There are no additional platform-specific requirements on macOS other than what is listed for all platforms in the [Software requirements](#software-requirements) section. + +## Troubleshooting + +There are some common problems caused by using outdated versions of the system requirements: + +### Unable to build a TypeScript project with `@prisma/client` + +#### Problem + +You see the following error when you try type-checking a project after you run `prisma generate`. + +```terminal wrap +./node_modules/.prisma/client/index.d.ts:10:33 +Type error: Type expected. + 8 | export type PrismaPromise = Promise & {[prisma]: true} + 9 | type UnwrapTuple = { +> 10 | [K in keyof Tuple]: K extends `${number}` ? Tuple[K] extends PrismaPromise ? X : never : never + | ^ + 11 | }; + 12 | + 13 | +``` + +#### Solution + +Upgrade the TypeScript dependency in your project to a [version supported by Prisma](#software-requirements). `npm install -D typescript`. + +### Unable to use `groupBy` preview feature + +#### Problem + +You see the following console error when you attempt to run an app that uses the `groupBy` feature: + +```terminal wrap +server.ts:6:25 - error TS2615: Type of property 'OR' circularly references itself in mapped type '{ [K in keyof { AND?: Enumerable; OR?: Enumerable; ... 4 more ...; category?: string | StringWithAggregatesFilter; }]: Or<...> extends 1 ? { ...; }[K] extends infer TK ? GetHavingFields<...> : never : {} extends FieldPaths<...> ? never : K...'. +6 const grouped = await prisma.product.groupBy({ + ~~~~~~~~~~~~~~~~~~~~~~~~ +7 by: ['category'] + ~~~~~~~~~~~~~~~~~~~~ +8 }); + ~~~~ +server.ts:6:48 - error TS2554: Expected 0 arguments, but got 1. +6 const grouped = await prisma.product.groupBy({ + ~ +7 by: ['category'] + ~~~~~~~~~~~~~~~~~~~~ +8 }); + ~~~ +``` + +#### Solution + +Upgrade the TypeScript dependency in your project to a [version supported by Prisma](#software-requirements). `npm install -D typescript`. diff --git a/docs/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx b/docs/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx new file mode 100644 index 0000000000..07053bff87 --- /dev/null +++ b/docs/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx @@ -0,0 +1,92 @@ +--- +title: 'Prisma Client & Prisma schema' +metaTitle: 'Prisma Client & Prisma schema' +metaDescription: 'Prisma Client and Prisma schema features that are currently in Preview.' +--- + + + +When we release a new Prisma Client or Prisma schema feature, it often starts in Preview so that you can test it and submit your feedback. After we improve the feature with your feedback and are satisfied with the internal test results, we promote the feature to general availability. + +For more information, see [ORM releases and maturity levels](/orm/more/releases). + + + +## Currently active Preview features + +The following [Preview](/orm/more/releases#preview) feature flags are available for Prisma Client and Prisma schema: + +| Feature | Released into Preview | Feedback issue | +| -------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :-------------------------------------------------------------------: | +| [`fullTextSearch`](/orm/prisma-client/queries/full-text-search) | [2.30.0](https://github.com/prisma/prisma/releases/tag/2.30.0) | [Submit feedback](https://github.com/prisma/prisma/issues/8877) | +| [`fullTextIndex`](/orm/prisma-schema/data-model/indexes#full-text-indexes-mysql-and-mongodb) | [3.6.0](https://github.com/prisma/prisma/releases/tag/3.6.0) | [Submit feedback](https://github.com/prisma/prisma/issues/10539) | +| [`metrics`](/orm/prisma-client/observability-and-logging/metrics) | [3.15.0](https://github.com/prisma/prisma/releases/tag/3.15.0) | [Submit feedback](https://github.com/prisma/prisma/issues/13579) | +| [`tracing`](/orm/prisma-client/observability-and-logging/opentelemetry-tracing) | [4.2.0](https://github.com/prisma/prisma/releases/tag/4.2.0) | [Submit feedback](https://github.com/prisma/prisma/issues/14640) | +| [`multiSchema`](https://github.com/prisma/prisma/issues/1122#issuecomment-1231773471) | [4.3.0](https://github.com/prisma/prisma/releases/tag/4.3.0) | [Submit feedback](https://github.com/prisma/prisma/issues/15077) | +| [`postgresqlExtensions`](/orm/prisma-schema/postgresql-extensions) | [4.5.0](https://github.com/prisma/prisma/releases/tag/4.5.0) | [Submit feedback](https://github.com/prisma/prisma/issues/15835) | +| [`deno`](/orm/prisma-client/deployment/edge/deploy-to-deno-deploy) | [4.5.0](https://github.com/prisma/prisma/releases/tag/4.5.0) | [Submit feedback](https://github.com/prisma/prisma/issues/15844) | +| [`views`](/orm/prisma-schema/data-model/views) | [4.9.0](https://github.com/prisma/prisma/releases/tag/4.9.0) | [Submit feedback](https://github.com/prisma/prisma/issues/17335) | +| `driverAdapters` | [5.4.0](https://github.com/prisma/prisma/releases/tag/5.4.0) | [Submit feedback](https://github.com/prisma/prisma/issues/3108) | +| `relationJoins` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22288) | +| `nativeDistinct` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22287) | + +To enable a Preview feature, [add the feature flag to the `generator` block](#enabling-a-prisma-client-preview-feature) in your `schema.prisma` file. [Share your feedback on all Preview features on GitHub](https://github.com/prisma/prisma/issues/3108). + +## Enabling a Prisma Client Preview feature + +To enable a Prisma Client Preview feature: + +1. Add the Preview feature flag to the `generator` block: + + ```prisma + generator client { + provider = "prisma-client-js" + previewFeatures = ["fullTextSearch"] + } + ``` + +2. Re-generate Prisma Client: + + ```terminal + npx prisma generate + ``` + +3. If you are using Visual Studio Code and the Preview feature is not available in your `.ts` file after generating Prisma Client, run the **TypeScript: Restart TS server** command. + +## Preview features promoted to general availability + +In the list below, you can find a history of Prisma Client and Prisma schema features that were in Preview and are now in general availability. The features are sorted by the most recent version in which they were promoted to general availability. + +| Feature | Released into Preview | Released into General Availability | +| -------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------: | +| `jsonProtocol` | [4.11.0](https://github.com/prisma/prisma/releases/tag/4.11.0) | [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0) | +| [`extendedWhereUnique`](/orm/reference/prisma-client-reference#filter-on-non-unique-fields-with-userwhereuniqueinput) | [4.5.0](https://github.com/prisma/prisma/releases/tag/4.5.0) | [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0) | +| [`fieldReference`](/orm/reference/prisma-client-reference#compare-columns-in-the-same-table) | [4.3.0](https://github.com/prisma/prisma/releases/tag/4.3.0) | [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0) | +| [`clientExtensions`](/orm/prisma-client/client-extensions) | [4.7.0](https://github.com/prisma/prisma/releases/tag/4.7.0) | [4.16.0](https://github.com/prisma/prisma/releases/tag/4.16.0) | +| [`filteredRelationCount`](/orm/prisma-client/queries/aggregation-grouping-summarizing#filter-the-relation-count) | [4.3.0](https://github.com/prisma/prisma/releases/tag/4.3.0) | [4.16.0](https://github.com/prisma/prisma/releases/tag/4.16.0) | +| [`orderByNulls`](/orm/prisma-client/queries/filtering-and-sorting#sort-with-null-records-first-or-last) | [4.1.0](https://github.com/prisma/prisma/releases/tag/4.1.0) | [4.16.0](https://github.com/prisma/prisma/releases/tag/4.16.0) | +| [`referentialIntegrity`](/orm/prisma-schema/data-model/relations/relation-mode) | [3.1.1](https://github.com/prisma/prisma/releases/tag/3.1.1) | [4.7.0](https://github.com/prisma/prisma/releases/tag/4.7.0) | +| [`interactiveTransactions`](/orm/prisma-client/queries/transactions#interactive-transactions) | [2.29.0](https://github.com/prisma/prisma/releases/tag/2.29.0) |
  • [4.7.0](https://github.com/prisma/prisma/releases/tag/4.7.0)
  • with Prisma Accelerate [5.1.1](https://github.com/prisma/prisma/releases/tag/5.1.1)
| +| [`extendedIndexes`](/orm/prisma-schema/data-model/indexes) | [3.5.0](https://github.com/prisma/prisma/releases/tag/3.5.0) | [4.0.0](https://github.com/prisma/prisma/releases/tag/4.0.0) | +| [`filterJson`](/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-a-json-field) | [2.23.0](https://github.com/prisma/prisma/releases/tag/2.23.0) | [4.0.0](https://github.com/prisma/prisma/releases/tag/4.0.0) | +| [`improvedQueryRaw`](/orm/prisma-client/queries/raw-database-access/raw-queries#raw-query-type-mapping) | [3.14.0](https://github.com/prisma/prisma/releases/tag/3.14.0) | [4.0.0](https://github.com/prisma/prisma/releases/tag/4.0.0) | +| [`cockroachdb`](/orm/overview/databases/cockroachdb) |
  • [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0)
  • migrations in CockroachDB in [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0)
| [3.14.0](https://github.com/prisma/prisma/releases/tag/3.14.0) | +| [`mongodb`](/orm/overview/databases/mongodb) |
  • [2.27.0](https://github.com/prisma/prisma/releases/tag/2.27.0)
  • introspection of MongoDB in [3.2.0](https://github.com/prisma/prisma/releases/tag/3.2.0)
  • introspection of embedded documents in [3.4.0](https://github.com/prisma/prisma/releases/tag/3.4.0)
  • MongoDB embedded documents in [3.10.0](https://github.com/prisma/prisma/releases/tag/3.10.0)
  • introspection of embedded documents in [3.10.0](https://github.com/prisma/prisma/releases/tag/3.10.0)
  • raw query support for MongoDB in [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0)
  • filters in embedded documents as an Experimental Feature in [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0)
  • order by embedded documents in [3.11.0](https://github.com/prisma/prisma/releases/tag/3.11.0)
| [3.12.0](https://github.com/prisma/prisma/releases/tag/3.12.0) | +| [`microsoftSqlServer`](/orm/overview/databases/sql-server) | [2.10.0](https://github.com/prisma/prisma/releases/tag/2.10.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`namedConstraints`](/orm/prisma-schema/data-model/database-mapping#constraint-and-index-names) | [2.29.0](https://github.com/prisma/prisma/releases/tag/2.29.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`referentialActions`](/orm/prisma-schema/data-model/relations/referential-actions) | [2.26.0](https://github.com/prisma/prisma/releases/tag/2.26.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`orderByAggregateGroup`](/orm/prisma-client/queries/aggregation-grouping-summarizing#order-by-aggregate-group) | [2.21.0](https://github.com/prisma/prisma/releases/tag/2.21.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`orderByRelation`](/orm/prisma-client/queries/filtering-and-sorting#sort-by-relation) |
  • [2.16.0](https://github.com/prisma/prisma/releases/tag/2.16.0)
  • order by aggregates of relations in [2.19.0](https://github.com/prisma/prisma/releases/tag/2.19.0)
| [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`selectRelationCount`](/orm/prisma-client/queries/aggregation-grouping-summarizing#count-relations) | [2.20.0](https://github.com/prisma/prisma/releases/tag/2.20.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| `napi` | [2.20.0](https://github.com/prisma/prisma/releases/tag/2.20.0) | [3.0.1](https://github.com/prisma/prisma/releases/tag/3.0.1) | +| [`groupBy`](/orm/reference/prisma-client-reference#groupby) | [2.14.0](https://github.com/prisma/prisma/releases/tag/2.14.0) | [2.20.0](https://github.com/prisma/prisma/releases/tag/2.20.0) | +| [`createMany`](/orm/reference/prisma-client-reference#createmany) | [2.16.0](https://github.com/prisma/prisma/releases/tag/2.16.0) | [2.20.0](https://github.com/prisma/prisma/releases/tag/2.20.0) | +| [`nativeTypes`](/orm/prisma-schema/data-model/models#native-types-mapping) | [2.11.0](https://github.com/prisma/prisma/releases/tag/2.11.0) | [2.17.0](https://github.com/prisma/prisma/releases/tag/2.17.0) | +| [`uncheckedScalarInputs`](/orm/prisma-client/queries/relation-queries#create-a-single-record-and-multiple-related-records) | [2.11.0](https://github.com/prisma/prisma/releases/tag/2.11.0) | [2.15.0](https://github.com/prisma/prisma/releases/tag/2.15.0) | +| [`transactionApi`](/orm/prisma-client/queries/transactions#the-transaction-api) | [2.1.0](https://github.com/prisma/prisma/releases/tag/2.1.0) | [2.11.0](https://github.com/prisma/prisma/releases/tag/2.11.0) | +| [`connectOrCreate`](/orm/reference/prisma-client-reference#connectorcreate) | [2.1.0](https://github.com/prisma/prisma/releases/tag/2.1.0) | [2.11.0](https://github.com/prisma/prisma/releases/tag/2.11.0) | +| [`atomicNumberOperations`](/orm/reference/prisma-client-reference#atomic-number-operations) | [2.6.0](https://github.com/prisma/prisma/releases/tag/2.6.0) | [2.10.0](https://github.com/prisma/prisma/releases/tag/2.10.0) | +| [`insensitiveFilters` (PostgreSQL)](/orm/prisma-client/queries/filtering-and-sorting#case-insensitive-filtering) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | [2.8.0](https://github.com/prisma/prisma/releases/tag/2.8.0) | +| [`middlewares`](/orm/prisma-client/client-extensions/middleware) | [2.3.0](https://github.com/prisma/prisma/releases/tag/2.3.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | +| [`aggregateApi`](/orm/prisma-client/queries/aggregation-grouping-summarizing#aggregate) | [2.2.0](https://github.com/prisma/prisma/releases/tag/2.2.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | +| [`distinct`](/orm/reference/prisma-client-reference#distinct) | [2.3.0](https://github.com/prisma/prisma/releases/tag/2.3.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | diff --git a/docs/200-orm/500-reference/500-preview-features/080-cli-preview-features.mdx b/docs/200-orm/500-reference/500-preview-features/080-cli-preview-features.mdx new file mode 100644 index 0000000000..bbd3d3fe95 --- /dev/null +++ b/docs/200-orm/500-reference/500-preview-features/080-cli-preview-features.mdx @@ -0,0 +1,29 @@ +--- +title: 'Prisma CLI' +metaTitle: 'Prisma CLI' +metaDescription: Prisma CLI features that are currently in Preview. +tocDepth: 3 +--- + + + +When we release a new Prisma CLI feature, it often starts in Preview so that you can test it and submit your feedback. After we improve the feature with your feedback and are satisfied with the internal test results, we promote the feature to general availability. + +For more information, see [ORM releases and maturity levels](/orm/more/releases). + + + +## Currently active Preview features + +There are currently no [Preview](/orm/more/releases#preview) features for Prisma CLI. + +## Preview features promoted to general availability + +In the list below, you can find a history of Prisma CLI features that were in Preview and are now in general availability. The features are sorted by the most recent version in which they were promoted to general availability. + +| Features | Released in Preview | Released in general availability | +| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | +| [`prisma migrate diff`](/orm/prisma-migrate/workflows/patching-and-hotfixing#fixing-failed-migrations-with-migrate-diff-and-db-execute) | [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) | [3.13.0](https://github.com/prisma/prisma/releases/tag/3.13.0) | +| [`prisma db execute`](/orm/prisma-migrate/workflows/patching-and-hotfixing#fixing-failed-migrations-with-migrate-diff-and-db-execute) | [3.9.0](https://github.com/prisma/prisma/releases/tag/3.9.0) | [3.13.0](https://github.com/prisma/prisma/releases/tag/3.13.0) | +| [`prisma db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) | [2.10.0](https://github.com/prisma/prisma/releases/tag/2.10.0) | [2.22.0](https://github.com/prisma/prisma/releases/tag/2.22.0) | +| [`prisma migrate`](/orm/prisma-migrate) | [2.13.0](https://github.com/prisma/prisma/releases/tag/2.13.0) | [2.19.0](https://github.com/prisma/prisma/releases/tag/2.19.0) | diff --git a/docs/200-orm/500-reference/500-preview-features/index.mdx b/docs/200-orm/500-reference/500-preview-features/index.mdx new file mode 100644 index 0000000000..a66e14815b --- /dev/null +++ b/docs/200-orm/500-reference/500-preview-features/index.mdx @@ -0,0 +1,17 @@ +--- +title: 'Preview features' +metaTitle: 'Preview features (Reference)' +metaDescription: 'Previews are typically available behind a feature flag or require some form of opt-in.' +hiddenPage: false +--- + + + +Some Prisma features are released as [Previews](/orm/more/releases#preview). [Share your feedback on all Preview features on GitHub](https://github.com/prisma/prisma/issues/3108). For information about available preview features and how to enable them, see: + +- [Prisma Client and Prisma schema preview features](client-preview-features) +- [Prisma CLI preview features](cli-preview-features) + +For information regarding upgrading Prisma and enabling Preview features see [Upgrading to use Preview features](/orm/more/upgrade-guides/upgrading-to-use-preview-features). + + diff --git a/docs/200-orm/500-reference/index.mdx b/docs/200-orm/500-reference/index.mdx new file mode 100644 index 0000000000..3991d92a30 --- /dev/null +++ b/docs/200-orm/500-reference/index.mdx @@ -0,0 +1,17 @@ +--- +title: 'Reference' +metaTitle: 'Reference' +metaDescription: 'Reference for Prisma Client API, Prisma CLI, errors & more.' +staticLink: true +toc: false +--- + + + +The reference section of the documentation is a collection of reference pages that describe the Prisma APIs and database implementations. + + + +## In this section + + diff --git a/docs/200-orm/800-more/100-under-the-hood/100-engines.mdx b/docs/200-orm/800-more/100-under-the-hood/100-engines.mdx new file mode 100644 index 0000000000..3942087a19 --- /dev/null +++ b/docs/200-orm/800-more/100-under-the-hood/100-engines.mdx @@ -0,0 +1,238 @@ +--- +title: 'Engines' +metaTitle: 'Engines' +metaDescription: "Prisma's query engine manages the communication with the database when using Prisma Client. Learn how it works on this page." +--- + + + +From a technical perspective, Prisma Client consists of three major components: + +- JavaScript client library +- TypeScript type definitions +- A query engine + +All of these components are located in the [generated `.prisma/client` folder](/orm/prisma-client/setup-and-configuration/generating-prisma-client#the-prismaclient-npm-package) after you ran `prisma generate`. + +This page covers relevant technical details about the query engine. + + + +## Prisma engines + +At the core of each module, there typically is a [Prisma engine](https://github.com/prisma/prisma-engines) that implements the core set of functionality. Engines are implemented in [Rust](https://www.rust-lang.org/) and expose a low-level API that is used by the higher-level interfaces. + +A Prisma engine is the **direct interface to the database**, any higher-level interfaces always communicate with the database _through_ the engine-layer. + +As an example, Prisma Client connects to the [query engine](/orm/more/under-the-hood/engines) in order to read and write data in a database: + +![Prisma engine](typical-flow-query-engine-at-runtime.png) + +### Using custom engine libraries or binaries + +By default, all engine files are automatically downloaded into the `node_modules/@prisma/engines` folder when you install or update `prisma`, the Prisma CLI package. The [query engine](/orm/more/under-the-hood/engines) is also copied to the generated Prisma Client when you call `prisma generate`. +You might want to use a [custom library or binary](https://github.com/prisma/prisma-engines) file if: + +- Automated download of engine files is not possible. +- You have created your own engine library or binary for testing purposes, or for an OS that is not officially supported. + +Use the following environment variables to specify custom locations for your binaries: + +- [`PRISMA_QUERY_ENGINE_LIBRARY`](/orm/reference/environment-variables-reference#prisma_query_engine_library) (Query engine, library) +- [`PRISMA_QUERY_ENGINE_BINARY`](/orm/reference/environment-variables-reference#prisma_query_engine_binary) (Query engine, binary) +- [`PRISMA_SCHEMA_ENGINE_BINARY`](/orm/reference/environment-variables-reference#prisma_schema_engine_binary) (Schema engine) +- [`PRISMA_MIGRATION_ENGINE_BINARY`](/orm/reference/environment-variables-reference#prisma_migration_engine_binary) (Migration engine) +- [`PRISMA_INTROSPECTION_ENGINE_BINARY`](/orm/reference/environment-variables-reference#prisma_introspection_engine_binary) (Introspection engine) + + + +- `PRISMA_MIGRATION_ENGINE_BINARY` variable is deprecated in [5.0.0](https://github.com/prisma/prisma/releases/tag/5.0.0). +- The Introspection Engine is served by the Migration Engine from [4.9.0](https://github.com/prisma/prisma/releases/tag/4.9.0). Therefore, the `PRISMA_INTROSPECTION_ENGINE` environment variable will not be used. +- The `PRISMA_FMT_BINARY` variable is used in versions [4.2.0](https://github.com/prisma/prisma/releases/tag/4.2.0) or lower. + + + +#### Setting the environment variable + +You can define environment variables globally on your machine or in the `.env` file. + +##### a) The `.env` file + +Add the environment variable to the [`.env` file](/orm/more/development-environment/environment-variables/env-files). + + + + + +``` +PRISMA_QUERY_ENGINE_BINARY=custom/my-query-engine-unix +``` + + + + +``` +PRISMA_QUERY_ENGINE_BINARY=c:\custom\path\my-query-engine-binary.exe +``` + + + + +> **Note**: It is possible to [use an `.env` file in a location outside the `prisma` folder](/orm/more/development-environment/environment-variables/managing-env-files-and-setting-variables). + +##### b) Global environment variable + +Run the following command to set the environment variable globally (in this example, `PRISMA_QUERY_ENGINE_BINARY`): + + + + + +```terminal +export PRISMA_QUERY_ENGINE_BINARY=/custom/my-query-engine-unix +``` + + + + + +```terminal +set PRISMA_QUERY_ENGINE_BINARY=c:\custom\my-query-engine-windows.exe +``` + + + + + +#### Test your environment variable + +Run the following command to output the paths to all binaries: + +```terminal +npx prisma -v +``` + +The output shows that the query engine path comes from the `PRISMA_QUERY_ENGINE_BINARY` environment variable: + + + + + +```terminal highlight=2;normal +Current platform : darwin +Query Engine : query-engine d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at /custom/my-query-engine-unix) +Migration Engine : migration-engine-cli d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at /myproject/node_modules/@prisma/engines/migration-engine-unix) +Introspection Engine : introspection-core d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at /myproject/node_modules/@prisma/engines/introspection-engine-unix) +``` + + + + +```terminal highlight=2;normal +Current platform : windows +Query Engine : query-engine d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at c:\custom\my-query-engine-windows.exe) +Migration Engine : migration-engine-cli d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at c:\myproject\node_modules\@prisma\engines\migration-engine-windows.exe) +Introspection Engine : introspection-core d6ff7119649922b84e413b3b69660e2f49e2ddf3 (at c:\myproject\node_modules\@prisma\engines\introspection-engine-windows.exe) +``` + + + + + +### Hosting engines + +The [`PRISMA_ENGINES_MIRROR`](/orm/reference/environment-variables-reference#prisma_engines_mirror) environment variable allows you to host engine files via a private server, AWS bucket or other cloud storage. +This can be useful if you have a custom OS that requires custom-built engines. + +```terminal +PRISMA_ENGINES_MIRROR=https://my-aws-bucket +``` + +## The query engine file + +The **query engine file** is different for each operating system. It is named `query-engine-PLATFORM` or `libquery_engine-PLATFORM` where `PLATFORM` corresponds to the name of a compile target. Query engine file extensions depend on the platform as well. As an example, if the query engine must run on a [Darwin]() operating system such as macOS Intel, it is called `libquery_engine-darwin.dylib.node` or `query-engine-darwin`. You can find an overview of all supported platforms [here](/orm/reference/prisma-schema-reference#binarytargets-options). + +The query engine file is downloaded into the `runtime` directory of the generated Prisma Client when `prisma generate` is called. + +Note that the query engine is implemented in Rust. The source code is located in the [`prisma-engines`](https://github.com/prisma/prisma-engines/) repository. + +## The query engine at runtime + +By default, Prisma Client loads the query engine as a [Node-API library](https://nodejs.org/api/n-api.html). You can alternatively [configure Prisma to use the query engine compiled as an executable binary](#configuring-the-query-engine), which is run as a sidecar process alongside your application. +The Node-API library approach is recommended since it reduces the communication overhead between Prisma Client and the query engine. + +![Diagram showing the query engine and Node.js at runtime](query-engine-node-js-at-runtime.png) + +The query engine is started when the first Prisma Client query is invoked or when the [`$connect()`](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management) method is called on your `PrismaClient` instance. Once the query engine is started, it creates a [connection pool](/orm/prisma-client/setup-and-configuration/databases-connections/connection-pool) and manages the physical connections to the database. From that point onwards, Prisma Client is ready to send [queries](/orm/prisma-client/queries/crud) to the database (e.g. `findUnique`, `findMany`, `create`, ...). + +The query engine is stopped and the database connections are closed when [`$disconnect()`](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management) is invoked. + +The following diagram depicts a "typical flow": + +1. `$connect()` is invoked on Prisma Client +1. The query engine is started +1. The query engine establishes connections to the database and creates connection pool +1. Prisma Client is now ready to send queries to the database +1. Prisma Client sends a `findMany()` query to the query engine +1. The query engine translates the query into SQL and sends it to the database +1. The query engine receives the SQL response from the database +1. The query engine returns the result as plain old JavaScript objects to Prisma Client +1. `$disconnect()` is invoked on Prisma Client +1. The query engine closes the database connections +1. The query engine is stopped + +![Typical flow of the query engine at run time](typical-flow-query-engine-at-runtime.png) + +## Responsibilities of the query engine + +The query engine has the following responsibilities in an application that uses Prisma Client: + +- manage physical database connections in connection pool +- receive incoming queries from the Prisma Client Node.js process +- generate SQL queries +- send SQL queries to the database +- process responses from the database and send them back to Prisma Client + +## Debugging the query engine + +You can access the logs of the query engine by setting the [`DEBUG`](/orm/prisma-client/debugging-and-troubleshooting/debugging) environment variable to `engine`: + +```terminal +export DEBUG="engine" +``` + +You can also get more visibility into the SQL queries that are generated by the query engine by setting the [`query` log level](/orm/reference/prisma-client-reference#log-levels) in Prisma Client: + +```ts +const prisma = new PrismaClient({ + log: ['query'], +}) +``` + +Learn more about [Debugging](/orm/prisma-client/debugging-and-troubleshooting/debugging) and [Logging](/orm/prisma-client/observability-and-logging/logging). + +## Configuring the query engine + +### Defining the query engine type for Prisma Client + +[As described above](#the-query-engine-at-runtime) the default query engine is a Node-API library that is loaded into Prisma Client, but there is also an alternative implementation as an executable binary that runs in its own process. You can configure the query engine type by providing the `engineType` property to the Prisma Client `generator`: + +```prisma +generator client { + provider = "prisma-client-js" + engineType = "binary" +} +``` + +Valid values for `engineType` are `binary` and `library`. You can also use the environment variable [`PRISMA_CLIENT_ENGINE_TYPE`](/orm/reference/environment-variables-reference#prisma_client_engine_type) instead. + + + +- Until Prisma 3.x the default and only engine type available was `binary`, so there was no way to configure the engine type to be used by Prisma Client and Prisma CLI. +- From versions [2.20.0](https://github.com/prisma/prisma/releases/2.20.0) to 3.x the `library` engine type was available and used by default by [activating the preview feature flag](/orm/reference/preview-features/client-preview-features#enabling-a-prisma-client-preview-feature) "`nApi`" or using the `PRISMA_FORCE_NAPI=true` environment variable. + + + +### Defining the query engine type for Prisma CLI + +Prisma CLI also uses its own query engine for its own needs. You can configure it to use the binary version of the query engine by defining the environment variable [`PRISMA_CLI_QUERY_ENGINE_TYPE=binary`](/orm/reference/environment-variables-reference#prisma_cli_query_engine_type). diff --git a/docs/200-orm/800-more/100-under-the-hood/index.mdx b/docs/200-orm/800-more/100-under-the-hood/index.mdx new file mode 100644 index 0000000000..2c2dfae346 --- /dev/null +++ b/docs/200-orm/800-more/100-under-the-hood/index.mdx @@ -0,0 +1,19 @@ +--- +title: 'Under the hood' +metaTitle: 'Under the hood' +metaDescription: 'Learn about Prisma internals and how it works "under the hood". Prisma tools are based on an engine-layer which manages the communication with the database.' +--- + + + +This page gives an overview of the Prisma internals and how it works "under the hood". + +Note that **this page does not contain any practical information that is relevant for _using_ Prisma**. It rather aims at providing a _mental model_ for what the Prisma toolkit _actually_ is and how the different tools that are available to developers are structured. + +If you're new to Prisma, be sure to check out the [Quickstart](/getting-started/quickstart) and [Introduction](/orm/overview/introduction/what-is-prisma) pages first. + + + +## In this section + + diff --git a/docs/200-orm/800-more/100-under-the-hood/query-engine-node-js-at-runtime.png b/docs/200-orm/800-more/100-under-the-hood/query-engine-node-js-at-runtime.png new file mode 100644 index 0000000000..820b4672dd Binary files /dev/null and b/docs/200-orm/800-more/100-under-the-hood/query-engine-node-js-at-runtime.png differ diff --git a/docs/200-orm/800-more/100-under-the-hood/typical-flow-query-engine-at-runtime.png b/docs/200-orm/800-more/100-under-the-hood/typical-flow-query-engine-at-runtime.png new file mode 100644 index 0000000000..4d802fb17c Binary files /dev/null and b/docs/200-orm/800-more/100-under-the-hood/typical-flow-query-engine-at-runtime.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/001-rejectonnotfound-changes.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/001-rejectonnotfound-changes.mdx new file mode 100644 index 0000000000..048748088e --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/001-rejectonnotfound-changes.mdx @@ -0,0 +1,177 @@ +--- +title: 'rejectOnNotFound changes' +metaTitle: 'How to handle removal of rejectOnNotFound in Prisma 5' +metaDescription: 'Sub-guide explaining how to update your code due to the removal of rejectOnNotFound in Prisma 5' +tocDepth: 2 +toc: true +--- + + + +As of Prisma version 5.0.0, the deprecated parameter `rejectOnNotFound` has been removed. Depending on if your project used `rejectOnNotFound` per query or globally, there will be be different ways of updating your code. + +If you are using the `rejectOnNotFound` parameter on a per-query basis, then follow our steps for [updating your code at the query level](#replacing-rejectonnotfound-enabled-at-the-query-level). + +If instead you have set up the `rejectOnNotFound` parameter at the client level, you will need to follow [the steps for updating your code at the client level](#replacing-rejectonnotfound-enabled-at-the-client-level). + +A full list of Prisma 5 changes can be found [in our release notes](https://github.com/prisma/prisma/releases/tag/5.0.0). + + + +## Replacing `rejectOnNotFound` enabled at the query level + +If you previously enabled `rejectOnNotFound` on a per-query basis, you will need to replace your usage at the _query level_. You can use our `*OrThrow` query variants, `findFirstOrThrow` or `findUniqueOrThrow` instead of supplying the parameter to `findFirst` and `findUnique`. + +### Simple `rejectOnNotFound` usage + +The following example: + +```js +prisma.user.findFirst({ + where: { name: 'Alice' }, + rejectOnNotFound: true, +}) +``` + +needs to be converted to: + +```js +prisma.user.findFirstOrThrow({ + where: { name: 'Alice' }, +}) +``` + +### `rejectOnNotFound` usage with custom error handler + +If you use a custom error handler like the following: + +```js +prisma.user.findFirst({ + where: { name: 'Alice' }, + rejectOnNotFound: () => new UserNotFoundError(), +}) +``` + +You will need to modify your code to handle the errors thrown by `...OrThrow` methods. + +```js +try { + await prisma.user.findFirstOrThrow({ + where: { name: 'Alice' }, + }) +} catch (err) { + if (err.code === 'P2025') { + throw new UserNotFoundError() + } + throw err +} +``` + +If your error handler is used in multiple places, you can also create a reusable error adapter which could then be used within a `.catch()` called on your function. + +```js +const adaptError = (customThrowFn) => (error) => { + if (error.code === 'P2025') { + throw customThrowFn() + } + throw error +} + +const user = await prisma.user.findFirstOrThrow({ + where: { name: 'Alice' }, +}).catch(adaptError(() => new MyCustomError()) +``` + +## Replacing `rejectOnNotFound` enabled at the Client level + +### `rejectOnNotFound` via Prisma Client Constructor + +If you previously enabled `rejectOnNotFound` globally via configuration in the Prisma Client constructor, like in these examples: + +```js +// Example 1 +const prisma = new PrismaClient({ + rejectOnNotFound: true, +}) + +// Example 2 +const prisma = new PrismaClient({ + rejectOnNotFound: { + findUnique: true, + }, +}) +``` + +You will need to update your codebase to use `findUniqueOrThrow` and `findFirstOrThrow` instead of `findUnique` and `findFirst`, depending on which calls you would like to throw. + +### `rejectOnNotFound` via Prisma Client Constructor with custom error handler + +If instead you use a custom error handler with the `rejectOnNotFound` property, like these examples: + +```js +// Example 3 +const prisma = new PrismaClient({ + rejectOnNotFound: (err) => new Error('something'), +}) + +// Example 4 +const prisma = new PrismaClient({ + rejectOnNotFound: { + findUnique: (err) => new Error('something'), + }, +}) + +// Example 5 +const prisma = new PrismaClient({ + rejectOnNotFound: { + findFirst: { + User: (err) => new Error('User error'), + Post: (err) => new Error('Post error'), + }, + findUnique: { + User: (err) => new Error('User error'), + Post: (err) => new Error('Post error'), + }, + }, +}) +``` + +You will need to update your method usage to `...OrThrow` and then use a [Client Extension](/orm/prisma-client/client-extensions) in order to get the same behavior. + +As an example, the following extension would give the same behavior in Prisma 5 that `Example 5` gave in Prisma 4 and lower. + +```js +import { PrismaClient } from '@prisma/client'; + +const customErrorFunc = async (model, query, args) => { + try { + await query(args) + } catch (error: any) { + if (error.code === 'P2025') { + throw new Error(`${model} error`) + } + throw error; + } +} + +const prisma = (new PrismaClient()).$extends({ + query: { + user: { + async findFirstOrThrow({ model, query, args }) { + return await customErrorFunc(model, query, args) + }, + async findUniqueOrThrow({ model, query, args }) { + return await customErrorFunc(model, query, args) + }, + }, + post: { + async findFirstOrThrow({ model, query, args }) { + return await customErrorFunc(model, query, args) + }, + async findUniqueOrThrow({ model, query, args }) { + return await customErrorFunc(model, query, args) + }, + }, + }, +}) +``` diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/101-jsonprotocol-changes.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/101-jsonprotocol-changes.mdx new file mode 100644 index 0000000000..8cc31d5a49 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/101-jsonprotocol-changes.mdx @@ -0,0 +1,336 @@ +--- +title: 'jsonProtocol changes' +metaTitle: 'Upgrade to jsonProtocol in Prisma 5' +metaDescription: 'Changes that need to be made to your app in Prisma 5 due to the jsonProtocol' +tocDepth: 2 +toc: true +--- + + + +As of Prisma version 5.0.0, the new `jsonProtocol` is the default. There are some changes that directly result from this change and a few changes that are related to the new protocol. + +A full list of Prisma 5 changes can be found [in our release notes](https://github.com/prisma/prisma/releases/tag/5.0.0). + + + +## `jsonProtocol` specific changes + +Below are changes that result directly from the `jsonProtocol` feature becoming the default in Prisma 5. + +### Removal of `jsonProtocol` Preview Feature + +In Prisma 5, `jsonProtocol` is the default and only protocol in Prisma. The `jsonProtocol` Preview feature is no longer needed. + +Prisma 4 and lower: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["jsonProtocol"] +} +``` + +Prisma 5: + +```prisma +generator client { + provider = "prisma-client-js" +} +``` + +### Improved error messages + +Due to the switch to the new protocol, several error messages have been improved. For example, the following error message in Prisma 4 and below: + +```terminal +Failed to validate the query: `Unable to match input value to any allowed input type for the field. Parse errors: [Query parsing/validation error at `Mutation.createOneUser.data.UserCreateInput.person.PersonCreateNestedOneWithoutUserInput.create`: Unable to match input value to any allowed input type for the field. Parse errors: [Query parsing/validation error at `Mutation.createOneUser.data.UserCreateInput.person.PersonCreateNestedOneWithoutUserInput.create.PersonCreateWithoutUserInput.hubspot_id`: A value is required but not set., Query parsing/validation error at `Mutation.createOneUser.data.UserCreateInput.person.PersonCreateNestedOneWithoutUserInput.create.PersonUncheckedCreateWithoutUserInput.hubspot_id`: A value is required but not set.], Query parsing/validation error at `Mutation.createOneUser.data.UserUncheckedCreateInput.person`: Field does not exist on enclosing type.]` at `Mutation.createOneUser.data` +``` + +becomes the following in Prisma 5: + +```terminal +Invalid `prisma.user.create()` invocation in +/Users/prismo/projects/prisma/reproductions/workbench/index.ts:21:36 + + 18 const prisma = new PrismaClient() + 19 + 20 for (const u of userData) { +→ 21 const user = await prisma.user.create({ + data: { + email: "eugene.albright@gallaudet.edu", + person: { + create: { + first_name: "William", + last_name: "Albright", + + hubspot_id: String + } + } + } + }) + +Argument `hubspot_id` must not be null. +``` + +## `jsonProtocol` related changes + +Below are changes that are related to the switch to the new protocol. If you were using the `jsonProtocol` Preview Feature, you most likely ran into these issues. + +### Removal of array shortcuts + +Several array shortcuts were removed as a part of this major update. These shortcuts were a way to add a single element as a value to an array-based operator. + +#### `OR` operators + +The following code in Prisma 4 and lower: + +```js +prisma.user.findMany({ + where: { + OR: { email: 'foo@example.com' }, + }, +}) +``` + +Will need to be changed to the following in Prisma 5: + +```js highlight=3;normal +prisma.user.findMany({ + where: { + OR: [{ email: 'foo@example.com' }], + }, +}) +``` + +`OR` operators will only accept array values. + +#### `in` and `notIn` operators + +Similar to `OR`, `in` and `notIn` require array values. + +Prisma 4 and lower: + +```js +prisma.user.findMany({ + where: { + id: { in: 123 }, + }, +}) + +prisma.user.findMany({ + where: { + id: { notIn: 123 }, + }, +}) +``` + +Prisma 5: + +```js highlight=4,12;normal +prisma.user.findMany({ + where: { + id: { + in: [123], + }, + }, +}) + +prisma.user.findMany({ + where: { + id: { + notIn: [123], + }, + }, +}) +``` + +
+Suggestion for single elements + +If your `in` and `notIn` values are only one element, you can also update your code to not use these operators at all: + +```js highlight=3,9;normal +prisma.user.findMany({ + where: { + id: 123, + }, +}) + +prisma.user.findMany({ + where: { + id: { not: 123 }, + }, +}) +``` + +
+ +#### `path` argument for filtering on JSON fields in PostgreSQL + +[When filtering on JSON fields in a PostgreSQL model](/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-a-json-field) the `path` argument now only accepts an array. + +When using the following schema: + +```prisma +model User { + id String @id + settings Json +} +``` + +Prisma 4 and lower: + +```js +prisma.user.findMany({ + where: { + settings: { + path: 'someSetting', + equals: someValue, + }, + }, +}) +``` + +Prisma 5: + +```js highlight=4;normal +prisma.user.findMany({ + where: { + settings: { + path: ['someSetting'], + equals: someValue, + }, + }, +}) +``` + + + +Note: This `path` argument change only affects PostgreSQL databases. MySQL databases are not affected as they use a different syntax. + + + +#### Scalar lists + +[Scalar list](/orm/prisma-schema/data-model/models#scalar-fields) values must be arrays in all operations. + +With the following schema: + +```prisma +model Post { + id String @id @default(uuid()) + tags String[] +} +``` + +Prisma 4 and lower: + +```js +prisma.post.create({ + data: { + tags: 'databases', + }, +}) + +prisma.post.findMany({ + where: { + tags: 'databases', + }, +}) +``` + +Prisma 5: + +```js highlight=3,9;normal +prisma.post.create({ + data: { + tags: ['databases'], + }, +}) + +prisma.post.findMany({ + where: { + tags: ['databases'], + }, +}) +``` + +#### Composite lists + +Operations on lists of [Composite types](/orm/prisma-schema/data-model/models#defining-composite-types) (for [MongoDB](/orm/overview/databases/mongodb)) now only accept array values. + +With the following schema: + +```prisma +model Post { + id String @id @default(uuid()) + commentsList Comment[] +} + +type Comment { + text String +} +``` + +Prisma 4 and lower: + +```js +prisma.post.findMany({ + where: { + commentsList: { + equals: { text: 'hello' }, + }, + }, +}) +``` + +Prisma 5: + +```js highlight=4;normal +prisma.post.findMany({ + where: { + commentsList: { + equals: [{ text: 'hello' }], + }, + }, +}) +``` + +
+Shorthand notation usage + +If you use the shorthand notation and exclude `equals`, you still must supply an array value for composite list fields. + +Prisma 4 and lower: + +```js +prisma.post.create({ + data: { + commentsList: { text: 'hello' }, + }, +}) + +prisma.post.findMany({ + where: { + commentsList: { text: 'hello' }, + }, +}) +``` + +Prisma 5: + +```js highlight=3,8;normal +prisma.post.create({ + data: { + commentsList: [{ text: 'hello' }], + }, +}) + +prisma.post.findMany({ + where: { + commentsList: [{ text: 'hello' }], + }, +}) +``` + +
diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/index.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/index.mdx new file mode 100644 index 0000000000..beda145577 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/600-upgrading-to-prisma-5/index.mdx @@ -0,0 +1,362 @@ +--- +title: 'Upgrade to Prisma 5' +metaTitle: 'Upgrade to Prisma 5' +metaDescription: 'Guides on how to upgrade to Prisma 5' +tocDepth: 3 +toc: true +--- + + + +Prisma 5.0.0 introduces a number of changes, including the usage of our new JSON Protocol, [which make Prisma faster by default](https://www.prisma.io/blog/prisma-5-f66prwkjx72s). A full list of these changes can be found [in our release notes](https://github.com/prisma/prisma/releases/tag/5.0.0). + +This guide explains how upgrading might affect your application and gives instructions on how to handle breaking changes within Prisma 5. + + + +## Upgrade the `prisma` and `@prisma/client` packages to Prisma 5 + +To upgrade to Prisma 5 from an earlier version, you need to update both the `prisma` and `@prisma/client` packages. + + + + + +```terminal +npm install @prisma/client@5 +npm install -D prisma@5 +``` + + + + + +```terminal +yarn up prisma@5 @prisma/client@5 +``` + + + + + +```terminal +pnpm upgrade prisma@5 @prisma/client@5 +``` + + + + + + + +Before you upgrade, check each breaking change below to see how the upgrade might affect your application. + + + +## Version changes + +Prisma 5 includes some minimum version changes for Node.js, TypeScript, and PostgreSQL. To use Prisma version 5.0.0 and up, you will need to have at least the minimum versions below: +See our [system requirements](/orm/reference/system-requirements) for all minimum version requirements. + +### Node.js minimum version change + +From Prisma version 5.0.0, the minimum version of Node.js supported is 16.13.0. If your project uses an earlier version of Node.js, you will need to upgrade it. + + + +Node.js v16.x is reaching [end-of-life on 11 September 2023](https://nodejs.org/en/blog/announcements/nodejs16-eol) in order to coincide with the end-of-life of OpenSSL 1.1.1. For that reason, we recommend upgrading to the current Node.js LTS, v18.x. Please note that Prisma 5 will be the last major version of Prisma to support Node.js v16. + + + +### TypeScript minimum version change + +From Prisma version 5.0.0, the minimum version of TypeScript supported is 4.7. If your project uses an earlier version of TypeScript, you will need to upgrade it. + +### PostgreSQL minimum version change + +From Prisma version 5.0.0, the minimum version of PostgreSQL supported is 9.6. If your project uses an earlier version of PostgreSQL, you will need to upgrade it. + + + +While Prisma supports PostgreSQL versions 9.6 and above, we **strongly** recommend updating to a version that is currently supported and still receiving updates. Please check [PostgreSQL's versioning policy](https://www.postgresql.org/support/versioning/) to determine which versions are currently supported. + + + +### Prisma Client embedded SQLite version updated + +With Prisma version 5.0.0, we have upgraded the embedded version of SQLite from `3.35.4` to `3.41.2`. We did not see any breaking changes and don't anticipate any changes needed in user projects, but if you are using SQLite, especially with raw queries that might go beyond Prisma's functionality, make sure to check [the SQLite changelog](https://www.sqlite.org/changes.html). + +## Primary changes + +This section gives an overview of the main breaking changes in Prisma 5. + +### Removal of `rejectOnNotFound` parameter + +With Prisma 5, the deprecated parameter `rejectOnNotFound` has been removed. Depending on if your project used `rejectOnNotFound` per query or globally, there will be be different ways of updating your code. + +If you are using the `rejectOnNotFound` parameter on a per-query basis, then follow our steps for [updating your code at the query level](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/rejectonnotfound-changes#replacing-rejectonnotfound-enabled-at-the-query-level). + +If instead you have set up the `rejectOnNotFound` parameter at the client level, you will need to follow [the steps for updating your code at the client level](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/rejectonnotfound-changes#replacing-rejectonnotfound-enabled-at-the-client-level). + +### `jsonProtocol` out of Preview + +The `jsonProtocol` preview feature is now Generally Available. This new protocol leads to [significantly improved startup times](https://www.prisma.io/blog/prisma-5-f66prwkjx72s#improved-startup-performance-in-prisma-client) when compared to our previous GraphQL-based protocol. When upgrading to Prisma 5, make sure to remove `jsonProtocol` from your preview features, if added. + +Prisma 4 and lower: + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["jsonProtocol"] +} +``` + +Prisma 5: + +```prisma +generator client { + provider = "prisma-client-js" +} +``` + +Please review our [jsonProtocol changes guide](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes) to learn how to update your app to account for the new protocol in Prisma 5. You will need to: + +- [Remove the `jsonProtocol` Preview Feature](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#removal-of-jsonprotocol-preview-feature) +- [Remove usage of certain array shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#removal-of-array-shortcuts) + +### Removal of array shortcuts + +Prisma 5 drops support for a number of "array shortcuts". These shortcuts were a way to add a single element as a value to an array-based operator instead of wrapping that one element in an array. To make our typings more consistent and logical and to conform to the new JSON Protocol, we now require array values for these operators. + +In most cases, the fix will be as simple as wrapping the existing value in an array. The shortcuts removed in Prisma 5 are: + +- [`OR` shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#or-operators) +- [`in` and `notIn` shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#in-and-notin-operators) +- [PostgreSQL JSON `path` field shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#path-argument-for-filtering-on-json-fields-in-postgresql) +- [Scalar list shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#scalar-lists) +- [MongoDB Composite list shortcuts](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5/jsonprotocol-changes#composite-lists) + +While `OR`, `in`, and `notIn` operators are affected, `AND` and `NOT` are not affected by this change. + +### `cockroachdb` provider is now required when connecting to a CockroachDB database + +With Prisma version 5.0.0, we require the `cockroachdb` provider to be used when connecting to CockroachDB databases. Previously, we had accepted `postgresql` as well, but we are removing that option. + +If you were using [native database types](/orm/reference/prisma-schema-reference#postgresql) and also the `postgresql` provider, you will need to [baseline your database](/getting-started/setup-prisma/add-to-existing-project/relational-databases/baseline-your-database-typescript-cockroachdb) from PostgreSQL to CockroachDB: + +1. Backup your existing `schema.prisma` file (e.g. use version control) +2. Update your `datasource` provider from `postgresql` to `cockroachdb` +3. Use `npx prisma db pull --force` in order to overwrite your existing Prisma schema file (including native types) to those that are on your CockroachDB instance. +4. Review changes between your Prisma schema backup and the new Prisma schema generated by `db pull`. You can either use the new schema as is, or update it to include your preferred spacing, comments, etc. +5. Delete your existing migrations. We will be [performing a baseline](/getting-started/setup-prisma/add-to-existing-project/relational-databases/baseline-your-database-typescript-cockroachdb) in order to make your local setup agree with your existing CockroachDB instance. +6. Perform the [baselining steps](/getting-started/setup-prisma/add-to-existing-project/relational-databases/baseline-your-database-typescript-cockroachdb). After these steps, you'll have migrated successfully from the `postgresql` provider to the `cockroachdb` provider! + +### Removal of `runtime/index.js` from generated client + +The `runtime/index.js` file has been removed from Prisma Client. + +#### Using public APIs from `@prisma/client/runtime` + +Importing from `@prisma/client/runtime` is no longer available in Prisma 5. If you were using public APIs available in this namespace before, you can instead import `Prisma` and access them. For example: + +```js +import { Decimal, NotFoundError } from '@prisma/client/runtime' +const num = new Decimal(24.454545) +const notFound = new NotFoundError() +``` + +will need to be changed to + +```js +import { Prisma } from '@prisma/client' +const num = new Prisma.Decimal(24.454545) +const notFound = new Prisma.NotFoundError() +``` + +#### Using private APIs for a specific runtime + +We highly discourage the use of internal private APIs as they can change without warning and are not guaranteed to be supported. If your usage requires a private API that was previous available [please reach out to us on GitHub.](https://github.com/prisma/prisma/discussions/new?category=q-a) + +### Generated type changes + +#### Changes to `RelationFilterInput` to account for nullability + +Prior to Prisma 5, there was a long-standing bug that caused nullable reverse relations to not be marked as nullable in our generated types. For example, take the following schema: + +```prisma +model User { + id Int @id + + addressId Int @unique + address Address @relation(fields: [addressId], references: [id]) + + post Post[] +} + +model Address { + id Int @id + + user User? +} + +model Post { + id Int @id + + userId Int + user User @relation(fields: [userId], references: [id]) +} +``` + +In the generated types, `Address.user` and `Post.user` would use the same type, `UserRelationFilter`. This is obviously unintended as `Address.user` is nullable while `Post.user` is not. In Prisma 5, the type of `Address.user` would be `UserNullableRelationFilter`, resolving this issue. + +If you import generated types in your code, you will need to update instances like this to utilize the new `Nullable` types. + +#### Changes to `UncheckedUpdateManyInput` to avoid name collisions + +In certain instances it was possible for name collisions to occur when one model had two foreign keys to two other models that had the same property name for the reverse relation. As an example, the following schema: + +```prisma +model Invoice { + InvoiceId Int @id @default(autoincrement()) + + invoice_items InvoiceItem[] +} + +model InvoiceItem { + InvoiceLineId Int @id @default(autoincrement()) + + InvoiceItemInvoiceId Int @map("InvoiceId") + invoices Invoice @relation(fields: [InvoiceItemInvoiceId], references: [InvoiceId]) + + TrackId Int + tracks Track @relation(fields: [TrackId], references: [TrackId]) +} + +model Track { + TrackId Int @id @default(autoincrement()) + Name String + + invoice_items InvoiceItem[] +} +``` + +Would lead to conflicting names between the two relations on `InvoiceItem`. The reverse relations, that is `Invoice.invoice_items` and `Track.invoice_items` would both get the type `InvoiceItemUncheckedUpdateManyWithoutInvoice_itemsInput`. In Prisma 5, this is resolved and Prisma will generate `InvoiceItemUncheckedUpdateManyWithoutInvoicesInput` and `InvoiceItemUncheckedUpdateManyWithoutTracksInput` respectively. + +If you import generated types in your code, you will need to update instances like this to the corrected types. + +## Other changes + +The following changes may cause an application to initially throw an error message after upgrading to Prisma 5. Fortunately, they are easy to solve, as the underlying functionality has been removed for a while or the change is a simple string replace. + +### Removal of deprecated Prisma CLI flags + +Several deprecated CLI flags have been removed. All following flags are from previous APIs and are no longer needed: + +- `--preview-feature` used in `db execute`, `db seed`, and `db diff` +- `--experimental` and `--early-access-feature` used in `migrate` +- `--force`/`-f` used in `db push` +- `--experimental-reintrospection` and `--clean` used in `db pull` + +The outdated use of `db push --force` can be replaced with the newer implementation `db push --accept-data-loss`. + +All other flags are from previous APIs and are no longer needed. + +### Removal of the `beforeExit` hook from the library engine + +The `beforeExit` hook has been removed from the Prisma library engine. While this functionality is still required for the Prisma binary engine in order to run last minute queries or perform shutdown related operations, it provides no benefit over native Node.js exit hooks in the library engine. Instead of this hook we recommend using built-in Node.js exit events. + +The following code with Prisma 4: + +```js +const exitHandler = () => { + // your exit handler code +} + +prisma.$on('beforeExit', exitHandler) +``` + +Could become: + +```js +const exitHandler = () => { + // your exit handler code +} + +process.on('exit', exitHandler) +process.on('beforeExit', exitHandler) +process.on('SIGINT', exitHandler) +process.on('SIGTERM', exitHandler) +process.on('SIGUSR2', exitHandler) +``` + +If you're using the `beforeExit` hook in NestJS, you can upgrade to Prisma 5 by removing the custom `enableShutdownHooks` method in your service: + +```diff file="prisma.service.ts" +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit { + async onModuleInit() { + await this.$connect() + } + +- async enableShutdownHooks(app: INestApplication) { +- this.$on('beforeExit', async () => { +- await app.close() +- }) +- } +} +``` + +Instead, use the built-in `enableShutdownHooks` method in NestJS if you need to handle lifecycle events: + +```diff file="main.ts" +- prismaService.enableShutdownHooks(app) ++ app.enableShutdownHooks() +``` + +### Removal of deprecated `prisma2` executable + +When we released Prisma 2, the `prisma2` executable was used in order to differentiate from Prisma 1. In a later release, the `prisma2` cli took over the `prisma` executable name. + +Needless to say, the `prisma2` executable has been deprecated for some time and is now removed. If your scripts use Prisma CLI as `prisma2`, please replace it with simply `prisma`. + +### Removal of deprecated `experimentalFeatures` property + +The `previewFeatures` field of the [generator block](/orm/reference/prisma-schema-reference#fields-1) used to be called `experimentalFeatures`. We are removing that deprecated property. + +In Prisma 5, you will need to update references of `experimentalFeatures` to `previewFeatures` manually or use the new code action in the Prisma VSCode extension. + +### `migration-engine` renamed to `schema-engine` + +The engine responsible for commands like `prisma migrate` and `prisma db` has been renamed from `migration-engine` to `schema-engine` to better describe its use. For many users, no changes will be required. However, if you need to explicitly include or exclude this engine file, or refer to the engine name for any other reason, you will need to update your code references. + +#### Example with the Serverless Framework + +One example we have seen is projects using the Serverless Framework. In these instances, you will need to update any patterns that reference `migration-engine` to instead reference `schema-engine`. + +```yaml highlight=6;delete|7;add +package: + patterns: + - '!node_modules/.prisma/client/libquery_engine-*' + - 'node_modules/.prisma/client/libquery_engine-rhel-*' + - '!node_modules/prisma/libquery_engine-*' + -- '!node_modules/prisma/migration-engine-*' + -- '!node_modules/prisma/schema-engine-*' +``` + +
+Serverless Framework pattern suggestion + +The [recommended rule from our documentation](/orm/prisma-client/deployment/serverless/deploy-to-aws-lambda#lambda-functions-with-arm64-architectures) is not affected by this change as it excludes all non desired engine files. + +```yaml highlight=6;normal +package: + patterns: + - '!node_modules/.prisma/client/libquery_engine-*' + - 'node_modules/.prisma/client/libquery_engine-rhel-*' + - '!node_modules/prisma/libquery_engine-*' + -- '!node_modules/@prisma/engines/**' +``` + +
+ +Enjoy Prisma 5! diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/700-upgrading-to-prisma-4.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/700-upgrading-to-prisma-4.mdx new file mode 100644 index 0000000000..a4443f11ce --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/700-upgrading-to-prisma-4.mdx @@ -0,0 +1,493 @@ +--- +title: 'Upgrade to Prisma 4' +metaTitle: 'Upgrade to Prisma 4' +metaDescription: 'Guides on how to upgrade to Prisma 4' +tocDepth: 3 +toc: true +--- + + + +Prisma 4 introduces a number of **breaking changes** when you upgrade from an earlier Prisma version. This guide explains how this upgrade might affect your application and gives instructions on how to handle any changes. + + + +## Breaking changes + +This section gives an overview of breaking changes in Prisma 4, grouped under [general changes](#general-changes) that affect both the Prisma Schema and Prisma Client, [Schema changes](#schema-changes) and [Client changes](#client-changes). + +We recommend that you first address any Prisma schema validation errors, then pull your database to reflect new Prisma schema capabilities, and finally fix any type errors in Prisma Client and validate by running your test suite. + +### Upgrade your Prisma Schema + +1. Carefully skim the list of changes and check if you are impacted by a breaking change. +2. Review the Prisma schema validation errors (via `npx prisma validate`, or via the Prisma VS Code extension). + 1. If you don't have validation errors, continue with step 3. + 2. If you have validation errors: + 1. Try to map the validation error to a change from the list below to understand which change caused the invalid Prisma schema, and read the linked instructions for how to upgrade. It can only come from: + - [Explicit unique constraints for 1:1 relations](#explicit-unique-constraints-on-one-to-one-relations) + - [Removed support for usage of `references` on implicit many-to-many relations](#disallow-references-syntax-for-implicit-many-to-many-relations) + - [Enforced uniqueness of referenced fields in the `references` argument in one-to-one and one-to-many relations for MySQL and MongoDB](#enforced-use-of-unique-or-id-attribute-for-one-to-one-and-one-to-many-relations-mysql-and-mongodb) + - Removal of undocumented support for the `type` alias + - Removal of the `sqlite` protocol for SQLite URLs + - [Better grammar for string literals](#better-grammar-for-string-literals) +3. Repeat until your Prisma schema is valid. +4. Run `npx prisma db pull` to upgrade the Prisma schema to all new capabilities (e.g. `extendedIndexes`). +5. Review changes of the Prisma schema and verify validity. +6. Continue with Prisma Client steps. + +### Upgrade your use of Prisma Client + +1. Carefully skim the [list of changes](#client-changes) to understand if you are impacted by a breaking change. + 1. If yes, read the detailed upgrade instructions. + 2. If no, proceed with 2. +2. Some API changes in Prisma Client are impacting runtime behavior, so please run your test suite. + +Enjoy Prisma 4! + +### General changes + +This section includes changes that affect both the Prisma Schema and Prisma Client. + +#### Node.js minimum version change + +From Prisma version 4.0.0, the minimum version of Node.js that we support is 14.17.x. If you use an earlier version of Node.js, you will need to update it. + +See our [system requirements](/orm/reference/system-requirements) for all minimum version requirements. + +### Schema changes + +This section includes changes that affect the Prisma Schema. + +#### Index configuration + +In Prisma 4, the `extendedIndexes` Preview feature will now become generally available. This includes the following index configuration options: + +- Length configuration of indexes, unique constraints and primary key constraints for MySQL (in Preview in versions 3.5.0 and later) +- Sort order configuration of indexes, unique constraints and primary key constraints (in Preview in versions 3.5.0 and later) +- New index types for PostgreSQL: Hash (in Preview in versions 3.6.0 and later) and GIN, GiST, SP-GiST and BRIN (in Preview in versions 3.14.0 and later) +- Index clustering for SQL Server (in Preview in versions 3.13.0 and later) + +See our documentation on [Index configuration](/orm/prisma-schema/data-model/indexes#index-configuration) for more details of these features. + +##### Upgrade path + +These can all be breaking changes if you were previously configuring these properties at the database level. In this case, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) +1. run `npx prisma db pull` afterwards to retrieve any existing configuration of indexes and constraints. This needs to be done before running any `npx prisma db push` or `npx prisma migrate dev` command, or you may lose any configuration that was defined in the database but not previously represented in the Prisma schema. + +For more details, see the [Upgrading from previous versions](/orm/prisma-schema/data-model/indexes#upgrading-from-previous-versions) section of our index configuration documentation. + +#### Scalar list defaults + +For database connectors that support scalar lists (PostgreSQL, CockroachDB and MongoDB), Prisma 4 introduces the ability to set a default value in your Prisma schema file with the `@default` attribute: + + + + +```prisma highlight=4;normal +model User { + id Int @id @default(autoincrement()) + posts Post[] + favoriteColors String[] @default(["red", "yellow", "purple"]) +} +``` + + + + +```prisma highlight=4;normal +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + posts Post[] + favoriteColors String[] @default(["red", "yellow", "purple"]) +} +``` + + + + +##### Upgrade path + +This is a breaking change if you previously had defaults defined for scalar lists at the database level. In this case, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) +1. run `npx prisma db pull` afterwards to retrieve any existing configuration of indexes and constraints. This needs to be done before running any `npx prisma db push` or `npx prisma migrate dev` command, or you will lose any defaults that are defined in the database but not previously represented in the Prisma schema. + +#### Explicit `@unique` constraints on one-to-one relations + +When using one-to-one relations in Prisma 4, you will need to explicitly add the `@unique` attribute to the relation scalar field. For example, for this one-to-one relation between a `User` and a `Profile` model, you will need to add the `@unique` attribute to the `profileId` field: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + profile Profile? @relation(fields: [profileId], references: [id]) + profileId Int? @unique // <-- include this explicitly +} + +model Profile { + id Int @id @default(autoincrement()) + user User? +} +``` + + + + +```prisma +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + profile Profile? @relation(fields: [profileId], references: [id]) + profileId String? @unique @db.ObjectId // <-- include this explicitly +} + +model Profile { + id String @id @default(auto()) @map("_id") @db.ObjectId + user User? +} +``` + + + + +##### Upgrade path + +After you upgrade to Prisma 4, any one-to-one relations without a `@unique` attribute on the relation scalar will trigger a validation error. To upgrade, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) + +1. manually fix the validation errors in your Prisma schema by adding the explicit `@unique` or `@id` attribute to your data model. +1. push the changes to your database using `prisma db push` for MongoDB or `prisma migrate dev` for MySQL. + +#### Enforced use of `@unique` or `@id` attribute for one-to-one and one-to-many relations (MySQL and MongoDB) + +When you use one-to-one and one-to-many relations in Prisma 4, you will need to use a `@unique` attribute on the relation field to guarantee that the singular side(s) of the relation has only one record. This is now enforced for MySQL and MongoDB, bringing them into line with other connectors. Missing `@unique` attributes will now trigger a validation error. + +In the following example of a _one-to-many relation_ between a `User` and `Post` model, the `@unique` attribute must be added to the `email` field: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique // <-- we enforce this attribute + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + authorEmail String + author User @relation(fields: [authorEmail], references: [email]) +} +``` + + + + +```prisma +model User { + id Int @id @default(auto()) @map("_id") @db.ObjectId + email String @unique // <-- we enforce this attribute + posts Post[] +} + +model Post { + id Int @id @default(auto()) @map("_id") @db.ObjectId + authorEmail String + author User @relation(fields: [authorEmail], references: [email]) +} +``` + + + + +In the following example of a _one-to-one relation_ between a `User` and `Profile` model, the `@unique` attribute must be added to the `email` field: + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique // <- we enforce this unique attribute + profile Profile @relation(fields: [profileId], references: [id]) + profileId Int +} + +model Profile { + id Int @id @default(autoincrement()) + userEmail String? @unique + user User? +} +``` + + + + +```prisma +model User { + id Int @id @default(auto()) @map("_id") @db.ObjectId + email String @unique // <- we enforce this unique attribute + profile Profile @relation(fields: [profileId], references: [id]) + profileId Int @db.ObjectId +} + +model Profile { + id Int @id @default(auto()) @map("_id") @db.ObjectId + userEmail String? @unique + user User? @relation(fields: [userEmail], references: [email]) +} +``` + + + + +##### Upgrade path + +After you upgrade to Prisma 4, any one-to-one or one-to-many relations without a `@unique` or `@id` attribute on the relation field will trigger a validation error. To upgrade, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) +1. manually fix the validation errors in your Prisma schema. Alternatively, if you have an up-to-date live database, running `npx prisma db pull` will add the `@unique` attributes automatically. + +#### Disallow `references` syntax for implicit many-to-many relations + +When using [implicit many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) in Prisma 4, you will no longer be able to use the `references` argument, which was previously optional. For example, the following relation would now trigger a validation error: + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + categories Category[] @relation("my-relation", references: [id]) // <-- validation error +} + +model Category { + id Int @id @default(autoincrement()) + posts Post[] @relation("my-relation", references: [id]) // <-- validation error +} +``` + +Instead, you can write: + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + categories Category[] @relation("my-relation") +} + +model Category { + id Int @id @default(autoincrement()) + posts Post[] @relation("my-relation") +} +``` + +This is because the only valid value for `references` was `id`, so removing this argument makes it clearer what can and cannot be changed. + +##### Upgrade path + +After you upgrade to Prisma 4, any implicit many-to-many relations with a `references` argument will trigger a validation error. To upgrade, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) +1. manually fix the validation errors in your Prisma schema. Alternatively, if you have an up-to-date live database, running `npx prisma db pull` will remove the `references` arguments automatically. + +#### Better grammar for string literals + +String literals in your Prisma Schema now need to follow the same rules as strings in JSON. This mostly changes the escaping of some special characters. More details can be found in [the JSON specification](https://www.ietf.org/rfc/rfc4627.txt) or on the [JSON website](https://www.json.org/json-en.html). + +##### Upgrade path + +This is a breaking change for some existing schemas. After you upgrade to Prisma 4, incorrectly escaped characters will trigger a validation error. To upgrade, you will need to: + +1. upgrade to the new Prisma 4 packages following [these instructions](#upgrade-the-prisma-and-prismaclient-packages-to-prisma-4) +1. manually fix the validation errors in your Prisma schema. + +### Client changes + +This section includes changes that affect Prisma Client. + +#### Raw query type mapping: scalar values are now deserialized as their correct JavaScript types + +In versions 3.14.x and 3.15.x, [raw query type mapping](/orm/prisma-client/queries/raw-database-access/raw-queries#raw-query-type-mapping) was available with the Preview feature `improvedQueryRaw`. In version 4.0.0, we have made raw query type mapping Generally Available. You do not need to use `improvedQueryRaw` to get this functionality in versions 4.0.0 and later. + +Raw queries now deserialize scalar values to their corresponding JavaScript types. Note that Prisma infers types from the values themselves and not from the Prisma Schema types. + +Example query and response: + +```ts +const res = + await prisma.$queryRaw`SELECT bigint, bytes, decimal, date FROM "Table";` +console.log(res) // [{ bigint: BigInt("123"), bytes: Buffer.from([1, 2]), decimal: new Prisma.Decimal("12.34"), date: Date("") }] +``` + +##### Upgrade path + +From version 4.0.0, some data types returned by `queryRaw` or `queryRawUnsafe` are different, as follows: + +| Data type | Before version 4.0.0 | From version 4.0.0 | +| ---------- | --------------------- | --------------------- | +| `DateTime` | Returned as `String` | Returned as `Date` | +| `Numeric` | Returned as `Float` | Returned as `Decimal` | +| `Bytes` | Returned as `String` | Returned as `Buffer` | +| `Int64` | Returned as `Integer` | Returned as `BigInt` | + +If you use `queryRaw` or `queryRawUnsafe` to return any of the above data types, then you must change your code to handle the new types. + +For example, if you return `DateTime` data, then you need to take into account the following: + +- You no longer need to manually instantiate a `DateTime` object for the returned data. +- If your code currently uses the returned `String` data, then you now need to convert the `DateTime` object to a `String`. + +You must make equivalent code changes for the other data types in the table above. + +#### Raw query mapping: PostgreSQL type-casts + +In versions 3.14.x and 3.15.x, [raw query type mapping](/orm/prisma-client/queries/raw-database-access/raw-queries#raw-query-type-mapping) was available with the Preview feature `improvedQueryRaw`. In version 4.0.0, we have made raw query type mapping Generally Available. You do not need to use `improvedQueryRaw` to get this functionality in versions 4.0.0 and later. + +Before version 4.0.0, many PostgreSQL type-casts did not work. We have tightened the type coercion rules so that all type-casts now work. As a result, some implicit casts now fail. + +##### Upgrade path + +We recommend that you re-test your use of `$queryRaw` to ensure that the types you pass into your raw queries match the types that PostgreSQL expects. + +For example, in version 4.0.0, the following query fails: + +```js +await prisma.$queryRaw`select length(${42});` +// ERROR: function length(integer) does not exist +// HINT: No function matches the given name and argument types. You might need to add explicit type casts. +``` + +This is because PostgreSQL’s `length` function expects `text` as input. Prisma used to silently coerce `42` to `text`, but does not do this in version 4.0.0. To fix this, explicitly cast `42` to `text` as follows: + +```js +await prisma.$queryRaw`select length(${42}::text);` +``` + +#### Raw query mapping: PostgreSQL and JavaScript integers + +In versions 3.14.x and 3.15.x, [raw query type mapping](/orm/prisma-client/queries/raw-database-access/raw-queries#raw-query-type-mapping) was available with the Preview feature `improvedQueryRaw`. In version 4.0.0, we have made raw query type mapping Generally Available. You do not need to use `improvedQueryRaw` to get this functionality in versions 4.0.0 and later. + +Prisma sends JavaScript integers to PostgreSQL as `INT8`. This might conflict with your user-defined functions that accept only `INT4` as input. + +##### Upgrade path + +If you use `$queryRaw` or parametrized `$queryRawUnsafe`queries with a PostgreSQL database, do one of the following: + +- Update the input types of any integers in your user-defined functions to `INT8`, or +- Cast any integers in your query parameters to `INT4`. + +#### `DbNull`, `JsonNull` and `AnyNull` are now objects + +JavaScript `null` is ambiguous for JSON columns, so Prisma uses `DbNull`, `JsonNull`, and `AnyNull` to distinguish between the database `NULL` value and the JSON `null` value. Before version 4.0.0, `DbNull`, `JsonNull`, and `AnyNull` were string constants. From version 4.0.0, they are objects. + +See [Filtering by null values](/orm/prisma-client/special-fields-and-types/working-with-json-fields#filtering-by-null-values) for more information. + +##### Upgrade path + +1. If you use literal strings to address these values, then you must replace them with the following named constants: + + - `DbNull`: replace with `Prisma.DbNull` + - `JsonNull`: replace with `Prisma.JsonNull` + - `AnyNull`: replace with `Prisma.AnyNull` + + If you already use these named constants, then you do not need to take any action. + +1. If you now get a type error when you pass `Prisma.DbNull` as the value of a JSON field, then this probably indicates a bug in your code that our types did not catch before version 4.0.0. The field where you tried to store `DbNull` is probably not nullable in your schema. As a result, a literal `DbNull` string was stored in the database instead of `NULL`. +1. You might now encounter a type error or runtime validation error when you use `Prisma.DbNull`, `Prisma.JsonNull`, or `Prisma.AnyNull` with MongoDB. This was never valid, but was silently accepted prior to Prisma 4. You need to review your data and change these fields to `null`. +1. If you pass in dynamic JSON to a JSON column in Prisma Client (for example `prisma.findMany({where: { jsonColumn: someJson } })`), then you must check that `someJson`cannot be the string "DBNull", "JsonNull", or "AnyNull". If it is any of these values, then the query will return different results in version 4.0.0. + +#### Default fields on composite types in MongoDB + +From version 4.0.0, if you carry out a database read on a composite type when all of the following conditions are true, then Prisma Client inserts the default value into the result. + +Conditions: + +- A field on the composite type is required, and +- this field has a default value, and +- this field is not present in the returned document or documents. + +This behavior is now consistent with the behavior for model fields. + +To learn more, see [Default values for required fields on composite types](/orm/prisma-client/special-fields-and-types/composite-types#default-values-for-required-fields-on-composite-types). + +##### Upgrade path + +If you currently rely on a return value of `null`, then you need to refactor your code to handle the default value that is now returned in Prisma 4. + +#### Rounding errors on big numbers in SQLite + +SQLite is a loosely-typed database. If your schema has a field with type `Int`, then Prisma prevents you from inserting a value larger than an integer. However, nothing prevents the database from directly accepting a bigger number. These manually-inserted big numbers cause rounding errors when queried. + +To avoid this problem, Prisma version 4.0.0 and later checks numbers on the way out of the database to verify that they fit within the boundaries of an integer. If a number does not fit, then Prisma throws a P2023 error, such as: + +``` +Inconsistent column data: Conversion failed: +Value 9223372036854775807 does not fit in an INT column, +try migrating the 'int' column type to BIGINT +``` + +##### Upgrade path + +If you use Prisma in conjunction with SQLite, then you need to find any code that queries `Int` fields and ensure that it handles any P2023 errors that might be returned. + +#### Prisma no longer exports `Prisma.dmmf.schema` into the generated Prisma Client + +From version 4.0.0, Prisma no longer exports `Prisma.dmmf.schema` into the generated Prisma Client. This makes the generated Prisma Client much more efficient, and also avoids some memory leaks with Jest. + +Note: + +- This change does not affect the DMMF that Prisma passes to the generators. +- You can use `getDmmf()`from `@prisma/internals` to access the schema property. +- We still export `Prisma.dmmf.datamodel` into the generated Prisma Client. + +## Upgrade the `prisma` and `@prisma/client` packages to Prisma 4 + +To upgrade to Prisma 4 from an earlier version, you need to update both the `prisma` and `@prisma/client` packages. Both the `prisma` and `@prisma/client` packages install with a caret `^` in their version number. This allows upgrades to new minor versions, but not major versions, to safeguard against breaking changes. + +To ignore the caret `^` and upgrade across major versions, you can use the `@4` tag when you upgrade with `npm`, or `yarn`: + + + +Before you upgrade, check each **breaking change** to see how the upgrade might affect your application. + + + + + + + +```terminal +npm install prisma@4 @prisma/client@4 +``` + + + + + +```terminal +yarn up prisma@4 @prisma/client@4 +``` + + + + + +## Video guide + +For a video walkthrough of the upgrade process and examples of upgrade scenarios, see our recorded livestream on upgrading to Prisma 4: + +
+ + + +
diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/100-named-constraints.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/100-named-constraints.mdx new file mode 100644 index 0000000000..28fb6b4216 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/100-named-constraints.mdx @@ -0,0 +1,162 @@ +--- +title: 'Named constraints upgrade path' +metaTitle: 'Named constraints upgrade path' +metaDescription: 'Guides on how to upgrade depending on your workflow using Prisma Introspect or Prisma Migrate' +tocDepth: 3 +toc: true +--- + + + +After upgrading to Prisma 3, the default naming convention for constraint and index names will change and your primary and foreign key names will now be part of the schema for databases that support them. Therefore the meaning of your existing Prisma schema will change. + +Before you continue to evolve your schema and your database, you should decide which names for constraints and indexes you want to use on your project going forward. + +You can either keep the names as they exist in your database or you can switch to use the names generated by Prisma, which follow the new naming convention. + +This page describes the manual upgrade steps that you need to perform after upgrading to Prisma 3. You can pick either of the two options: + +- **Option 1**: [I want to maintain my existing constraint and index names](#option-1-i-want-to-maintain-my-existing-constraint-and-index-names) +- **Option 2**: [I want to use Prisma's default constraint and index names](#option-2-i-want-to-use-prismas-default-constraint-and-index-names) + + + +## Option 1: I want to maintain my existing constraint and index names + +If you want to keep your database unchanged and keep the existing names for constraints and indexes you need to pull them into your schema so Prisma is aware of them. + +Reasons to keep your existing names might be: + +- Naming conventions you have to follow +- Other tooling relying on the names +- Personal preference + +To keep existing names, run `prisma db pull` against the target environment. This will result in all names that do not match Prisma's naming convention for constraint and index names being pulled into your schema as `map` arguments on their respective attributes. + +1. Example schema: + + ```prisma + model User { + id Int @id @default(autoincrement()) + name String @unique + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name]) + } + ``` + +1. Introspect your **development database** to populate the Prisma schema with constraint and index names in your underlying database _that do not match Prisma's naming convention_: + + ```terminal + npx prisma db pull + ``` + + In this example, the highlighted constraints did not conform to Prisma's default naming convention and now include the `map` attribute field: + + ```prisma highlight=11;normal + model User { + id Int @id(map: "Custom_Constraint_Name") @default(autoincrement()) + name String @unique + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name], map: "Custom_Foreign_Key_Constraint") + } + ``` + +## Option 2: I want to use Prisma's default constraint and index names + +If you want to keep your Prisma Schema clean and if you have no reasons preventing you from renaming constraints and indexes in your database, then you can create a migration to update the names. + +Run `prisma migrate dev` to create a migration updating the constraint names to Prisma's defaults. + +Afterwards, do not forget to `prisma migrate deploy` against your production environment if you have one to also update the names there. The schema below has no explicit constraint or index names spelled out, so Prisma will infer them. + +1. Example schema: + + ```prisma + model User { + name String @id //inferred as User_pkey + posts Post[] + } + + model Post { + id Int @id @default(autoincrement()) //inferred as Post_pkey + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name]) //inferred as Post_authorName_fkey + } + ``` + +1. Run the `prisma migrate dev` command to generate a new migration: + + ```terminal + npx prisma migrate dev + ``` + + This migration renames any constraints that do not currently follow Prisma's naming convention. + +1. Run the [`prisma migrate deploy`](/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate) command to apply the migration to your production environment: + + ```terminal + npx prisma migrate deploy + ``` + +## Dealing with cases where more than one database environment is used for the same application + +### Checking whether your environments use identical names + +Since Prisma did not offer a way to define constraint or index names explicitly in the past, you can face situations where your different database environments have differing constraint or index names. + +In order to detect this: + +- Create a backup of your current `schema.prisma` file. +- Run `prisma db pull` against each database environment, by saving the results to their own separate files using the `--schema` option. [See reference](/orm/reference/prisma-cli-reference#arguments-1) + +Then you can either manually inspect both files or use a `diff` tool in your IDE or in the terminal. If you see differences in constraint names, your production and local environments are out of sync and should be aligned. + +In the following example, the `Post` model has a foreign key constraint with a custom name in production that does not match development. + +#### Development environment: + +```prisma highlight=5;normal +model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name], map: "Custom_Foreign_Key_Constraint") +} +``` + +#### Production environment: + +```prisma highlight=5;normal +model Post { + id Int @id @default(autoincrement()) + title String + authorName String @default("Anonymous") + author User? @relation(fields: [authorName], references: [name], map: "Custom_Production_Name") +} +``` + +### Aligning your environments if their constraint or index names differ + +If the names in your environments differ, the safest option is to align your development environment with the names in your production environment. This makes sure that no changes need to be performed on your production database. + +In order to achieve this: + +- Run `prisma db pull` against your production environment to pull in the constraint and index names +- Switch to development and run `prisma migrate dev` to create a new migration. You can call that migration `migration-to-sync-names` +- Switch to production, and run `prisma migrate resolve --applied migration-to-sync-names` to mark the migration as applied on production + +Your migration history now contains a migration to ensure that the names of any new environments you spin up contain the same names as your production database. And Prisma knows not to apply this migration to production since you already marked it as applied. + +Your environments are now in sync and you can proceed to the [upgrade paths for migrate users](#option-2-i-want-to-use-prismas-default-constraint-and-index-names). These let you choose your future naming scheme. diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/150-referential-actions.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/150-referential-actions.mdx new file mode 100644 index 0000000000..4c97bb2ed8 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/150-referential-actions.mdx @@ -0,0 +1,202 @@ +--- +title: 'Referential actions upgrade path' +metaTitle: 'Referential actions upgrade path' +metaDescription: 'Guides on how to upgrade depending on your workflow using Prisma Introspect or Prisma Migrate' +tocDepth: 4 +toc: true +--- + + + +Prisma version 2.x prevents deletion of connected records in some Prisma Client functions, and does not let you configure referential actions in your Prisma Schema to change that behavior. + +Prisma version 3.x and later lets you control what should happen when deleting or updating records by explicitly setting referential actions on your models' relations. After the upgrade, Prisma Client will not enforce any referential actions anymore, and any action written to the database foreign keys will define the behavior when deleting or updating records. + +Prisma Migrate 3.x will use the actions previously done by Prisma Client as the new default when writing the foreign key constraints to the database. + + + +## Prisma 2.x behavior + +When invoking the [`delete()`](/orm/prisma-client/queries/crud#delete-a-single-record) or [`deleteAll()`](/orm/prisma-client/queries/crud#delete-all-records) methods using Prisma Client on required relations, a runtime check is performed and the deletion of records prevented if they are referencing related objects. **This prevents cascade behavior, no matter how the foreign key is defined**. + +The behavior in Prisma 2, without upgrading, does not allow setting referential actions at all. [See Prisma 2.x default referential actions](#prisma-2x-default-referential-actions) + +If you need to actually use the cascade behavior configured in the database, you _can_ use [`raw`](/orm/prisma-client/queries/raw-database-access/raw-queries) SQL queries to [delete multiple referenced records](/orm/prisma-client/queries/crud#deleting-all-data-with-raw-sql--truncate). This is because Prisma Client will **not** perform runtime checks on raw queries. + +### Prisma 2.x default referential actions + +Below are the default referential actions written to the database foreign keys when using Prisma Migrate versions 2.x: + +| Clause | Optional relations | Mandatory relations | +| :--------- | :----------------- | :------------------ | +| `onDelete` | `SetNull` | `Cascade` | +| `onUpdate` | `Cascade` | `Cascade` | + +On top of the database referential actions, the following actions are enforced in Prisma Client versions 2.x: + +| Clause | Optional relations | Mandatory relations | +| :--------- | :----------------- | :------------------ | +| `onDelete` | `SetNull` | `Restrict` | +| `onUpdate` | `Cascade` | `Cascade` | + +## Upgrade paths + +There are a couple of paths you can take when upgrading which will give different results depending on the desired outcome. + +If you currently use the migration workflow, you can run `prisma db pull` to check how the defaults are reflected in your schema. You can then manually update your database if you need to. + +You can also decide to skip checking the defaults and run a migration to update your database with the new default values. + +### Using Introspection + +If you [Introspect](/orm/prisma-schema/introspection) your database, the referential actions configured at the database level will be reflected in your Prisma Schema. If you have been using Prisma Migrate or `prisma db push` to manage the database schema, these are likely to be the [\<=2.25.0 default values](#prisma-2x-default-referential-actions). + +When you run an Introspection, Prisma compares all the foreign keys in the database with the schema, if the SQL statements `ON DELETE` and `ON UPDATE` do **not** match the default values, they will be explicitly set in the schema file. + +After introspecting, you can review the non-default clauses in your schema. The most important clause to review is `onDelete`, which defaults to `Cascade` in version 2.25.0 and earlier. + + + +If you are using either the [`delete()`](/orm/prisma-client/queries/crud#delete-a-single-record) or [`deleteAll()`](/orm/prisma-client/queries/crud#delete-all-records) methods, **cascading deletes will now be performed, as the safety net in Prisma Client that previously prevented cascading deletes at runtime is removed**. Be sure to check your code and make any adjustments accordingly. + + + +Make sure you are happy with every case of `onDelete: Cascade` in your schema. If not, either: + +- Modify your Prisma schema and `db push` or `dev migrate` to change the database _or_ +- Manually update the underlying database if you only use `prisma db pull` in your workflow + +The following example would result in a cascading delete, meaning that if the `User` is deleted then all of their `Post`'s will be deleted too. + +#### A blog schema example + +```prisma highlight=4;add +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade) + authorId Int +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +### Using Migration + +When running a [Migration](/orm/prisma-migrate) (or the [`prisma db push`](/orm/prisma-migrate/workflows/prototyping-your-schema) command) the [new defaults](/orm/prisma-schema/data-model/relations/referential-actions#referential-action-defaults) will be applied to your database. + + + +Unlike when you run `prisma db pull` for the first time, the new referential actions clause and property will **not** automatically be added to your Prisma schema by the Prisma VSCode extension. +You will have to manually add them if you wish to use anything other than the new defaults. + + + +Explicitly defining referential actions in your Prisma schema is optional. If you do not explicitly define a referential action for a relation, Prisma uses the [new defaults](/orm/prisma-schema/data-model/relations/referential-actions#referential-action-defaults). + +Note that referential actions can be added on a case by case basis. This means that you can add them to one single relation and leave the rest set to the defaults by not manually specifying anything. + +### Checking for errors + +**Before** upgrading to version 3.0.1 (or versions 2.26.0 and above with the `referentialActions` feature flag enabled), Prisma prevented the deletion of records while using `delete()` or `deleteMany()` to preserve referential integrity. A custom runtime error would be thrown by Prisma Client with the error code `P2014`. + +**After** upgrading, Prisma no longer performs runtime checks. You can instead specify a custom referential action to preserve the referential integrity between relations. + +When you use [`NoAction`](/orm/prisma-schema/data-model/relations/referential-actions#noaction) or [`Restrict`](/orm/prisma-schema/data-model/relations/referential-actions#restrict) to prevent the deletion of records, the error messages will be different in versions 3.0.1 and above (or 2.26.0 with the `referentialActions` feature flag enabled) compared to versions prior to that. This is because they are now triggered by the database and **not** Prisma Client. The new error code that can be expected is `P2003`, so you should check your code to make adjustments accordingly. + +#### Example of catching errors + +The following example uses the below blog schema with a 1-m relationship between `Post` and `User` and sets a [`Restrict`](/orm/prisma-schema/data-model/relations/referential-actions#restrict) referential actions on the `author` field. + +This means that if a user has a post, that user (and their posts) **cannot** be deleted. + +```prisma file=schema.prisma +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id], onDelete: Restrict) + authorId String +} + +model User { + id Int @id @default(autoincrement()) + posts Post[] +} +``` + +Prior to upgrading, the error code you would receive when trying to delete a user which has posts would be `P2014` and it's message: + +> "The change you are trying to make would violate the required relation '\{relation_name}' between the \{model_a_name} and \{model_b_name} models." + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + try { + await prisma.user.delete({ + where: { + id: 'some-long-id', + }, + }) + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2014') { + console.log(error.message) + } + } + } +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + +To make sure you are checking for the correct errors in your code, modify your check to look for `P2003`, which will deliver the message: + +> "Foreign key constraint failed on the field: \{field_name}" + +```ts highlight=14;delete|15;add +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function main() { + try { + await prisma.user.delete({ + where: { + id: 'some-long-id' + } + }) + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2014') { + if (error.code === 'P2003') { + console.log(error.message) + } + } + } +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/index.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/index.mdx new file mode 100644 index 0000000000..a1cf229660 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/800-upgrading-to-prisma-3/index.mdx @@ -0,0 +1,161 @@ +--- +title: 'Upgrade to Prisma 3' +metaTitle: 'Upgrade to Prisma 3' +metaDescription: 'Guides on how to upgrade to Prisma 3' +tocDepth: 3 +toc: true +--- + + + +Prisma 3 introduces a number of **breaking changes** if you are upgrading from an earlier version (any 2.x version), therefore, it is important to understand how this upgrade might affect your application and make any needed adjustments to ensure a smooth transition. + +Below you will find a list of the breaking changes and how to handle them. + + + +## Breaking changes + +### [Referential actions](/orm/prisma-schema/data-model/relations/referential-actions) + +The introduction of referential actions in version 3.x removes the safety net in Prisma Client that had previously prevented cascading deletes at runtime. + +As a result, depending on which workflow you are using to work on your application, you could be impacted. We advise you to check your schema and decide if you need to define referential actions explicitly. + +See [Referential action upgrade path](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3/referential-actions) to understand how to proceed. + +### [Named constraints](/orm/prisma-schema/data-model/database-mapping) + +We changed the convention followed by Prisma to name constraints and indexes. We also introduced a clear distinction between the `map` attribute (database-level name) and `name` attribute (Prisma Client API name) in the PSL to explicitly control how constraints are defined in the Prisma schema. + +This means that you will notice an impact when running Prisma `migrate` or `db pull` which will follow this new convention. We advise you to adjust your schema to reflect the names of your constraints and indexes appropriately. + +You can check out the [Named constraints upgrade path](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3/named-constraints) for more information on how to proceed. + +### [$queryRaw](/orm/prisma-client/queries/raw-database-access/raw-queries) + +From version 3.x onwards, the `$queryRaw` method now only supports a template literal. + +This means that if your application relied on `$queryRaw` calls using _strings_, those calls will **not** work anymore. We advise you to use template literals wherever possible for security reasons or resort to `$queryRawUnsafe` otherwise, after carefully escaping queries to prevent SQL injections. + +You can learn more about the new `$queryRaw` and `$queryRawUnsafe` methods in the [Raw database access](/orm/prisma-client/queries/raw-database-access/raw-queries) section of the docs. + +### [Json Null Equality](/orm/prisma-client/special-fields-and-types/working-with-json-fields#filtering-by-null-values) + +You cannot filter a `Json` field by a null value. [See this GitHub issue](https://github.com/prisma/prisma/issues/8399). +This is because `{ equals: null }` checks if the column value in the database is `NULL`, not if the JSON value inside the column equals `null`. + +To fix this problem, we decided to split null on Json fields into `JsonNull`, `DbNull` and `AnyNull`. + +- **JsonNull**: Selects the null value in JSON. +- **DbNull**: Selects the NULL value in the database. +- **AnyNull:** Selects both null JSON values and NULL database values. + +Given the following model in your Prisma Schema: + +```ts +model Log { + id Int @id + meta Json +} +``` + +Starting in 3.0.1, you'll see a TypeError if you try to filter by null on a `Json` field: + +```ts +prisma.log.findMany({ + where: { + data: { + meta: { + equals: null + ^ TypeError: Type 'null' is not assignable to type + } + }, + }, +}); +``` + +To fix this, you'll import and use one of the new null types: + +```ts highlight=7;normal +import { Prisma } from '@prisma/client' + +prisma.log.findMany({ + where: { + data: { + meta: { + equals: Prisma.AnyNull, + }, + }, + }, +}) +``` + +This also applies to `create`, `update` and `upsert`. To insert a `null` value +into a `Json` field, you would write: + +```ts highlight=5;normal +import { Prisma } from '@prisma/client' + +prisma.log.create({ + data: { + meta: Prisma.JsonNull, + }, +}) +``` + +And to insert a database `NULL` into a Json field, you would write: + +```ts highlight=5;normal +import { Prisma } from '@prisma/client' + +prisma.log.create({ + data: { + meta: Prisma.DbNull, + }, +}) +``` + + + +This API change does not apply to the MongoDB connector where there is not a difference between a JSON null and a database NULL. + +They also do not apply to the `array_contains` operator because there can only be a JSON null within an JSON array. Since there cannot be a database NULL within a JSON array, `{ array_contains: null }` is not ambiguous. + + + +## Specific upgrade paths + + + +## Upgrading the `prisma` and `@prisma/client` packages to Prisma 3 + +To upgrade from version 2.x to 3.x, you need to update both the `prisma` and `@prisma/client` packages. Both the `prisma` and `@prisma/client` packages install with a caret `^` in their version number to safe guard against breaking changes. + +To ignore the caret `^` and upgrade across major versions, you can use the `@3` tag when upgrading with `npm`, or `yarn` . + + + +Before upgrading, check each **breaking change** to see how the upgrade might affect your application. + + + + + + + +```terminal +npm install prisma@3 @prisma/client@3 +``` + + + + + +```terminal +yarn up prisma@3 @prisma/client@3 +``` + + + + diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/900-codemods.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/900-codemods.mdx new file mode 100644 index 0000000000..e28bab5e4f --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/900-codemods.mdx @@ -0,0 +1,43 @@ +--- +title: 'Codemods' +metaTitle: 'Codemods (Guides)' +metaDescription: 'Use codemods to upgrade your codebase as Prisma changes and evolves.' +--- + + + +The `@prisma/codemods` package helps you to upgrade your codebase as Prisma evolves and changes. + + + +You can checkout the repository, here: https://github.com/prisma/codemods + + + + + +## Usage + +```terminal + $ npx @prisma/codemods <...options> +``` + +- `` - See [Transforms](#transforms) for available choices +- `` - The directory of your app. i.e ./my-awesome-project + +## Options + +- `(-f)orce` - Bypass Git safety checks and forcibly run codemods +- `(-s)chemaPath` - Specify a path to your ./prisma/schema.prisma +- `(-d)ry` - Dry run (no changes are made to files) +- `(-p)rint` - Print transformed files to your terminal +- `--instanceNames=myClient` - Useful when importing an already instantiated client (i.e import myClient from './myClient') + +## Transforms + +| `` | Description | Example | +| ------------- | ----------------------------------------------------------- | ----------------------------------------------- | +| `namespace` | Codemod for `@prisma/client` namespace change | `npx @prisma/codemods namespace ./my-project` | +| `findUnique` | Converts `prisma.x.findOne` to `prisma.x.findUnique` | `npx @prisma/codemods findUnique ./my-project` | +| `to$` | to\$: Converts deprecated `prisma.x` methods to `prisma.$x` | `npx @prisma/codemods to$ ./my-project` | +| `update-2.12` | Includes `namespace`/`findUnique`/`to$` | `npx @prisma/codemods update-2.12 ./my-project` | diff --git a/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/index.mdx b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/index.mdx new file mode 100644 index 0000000000..df31bf3d41 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/200-upgrading-versions/index.mdx @@ -0,0 +1,55 @@ +--- +title: 'Upgrading versions' +metaTitle: 'Upgrading versions (Guides)' +metaDescription: 'Upgrading your project to the latest version of Prisma.' +--- + + + +To upgrade to the latest version of Prisma: + +1. Review [release notes](https://github.com/prisma/prisma/releases) on GitHub for breaking changes and new features. +1. Upgrade **both** of the following packages to the same version: + + - [`prisma`](https://www.npmjs.com/package/prisma) + - [`@prisma/client`](https://www.npmjs.com/package/@prisma/client) + +1. Upgrade your codebase where applicable. Breaking changes may require you to change your Prisma schema or the way you use Prisma Client. + +:::tip + +[codemods](codemods) help you refactor your code to account for breaking changes - for example, the 2.12.0 codemod automatically renames `findOne` to `findUnique`. + +::: + + + +## Common upgrade paths + +### Prisma 2 and onwards + +- [Upgrade to Prisma 5](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5) +- [Upgrade to Prisma 4](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-4) +- [Upgrade to Prisma 3](/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-3) + +### Prisma 1 + +- [Upgrade from Prisma 1](/orm/more/upgrade-guides/upgrade-from-prisma-1) + +## Testing new features, without upgrading + +### `dev` distribution tag + +The `dev` [Npm distribution tag](http://npm.github.io/publishing-pkgs-docs/updating/using-tags.html) points to the most recent development version of the package, which is published for each commit to the main branch of `prisma/prisma`. You can use the `dev` distribution tag to verify a fix or test a feature before it is officially released. + +To install the latest `dev` distribution tag: + +```terminal +npm install @prisma/client@dev prisma@dev +``` + + + +Do not use the `dev` distribution tag in production - wait until the official release that contains the features and fixes you are interested in is released. For example, fixes present `@prisma/client@2.23.0-dev.25` will eventually be released as part of `@prisma/client@2.23.0`. + + diff --git a/docs/200-orm/800-more/300-upgrade-guides/250-upgrading-to-use-preview-features.mdx b/docs/200-orm/800-more/300-upgrade-guides/250-upgrading-to-use-preview-features.mdx new file mode 100644 index 0000000000..793f39ca0b --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/250-upgrading-to-use-preview-features.mdx @@ -0,0 +1,25 @@ +--- +title: 'Upgrading to use Preview features' +metaTitle: 'Upgrading to use Preview features (Guides)' +metaDescription: 'Upgrading your project to use a Preview feature.' +toc: false +--- + + + +Preview features are new features that can only be used by opting in using a corresponding feature flag. + + + +## Enabling preview features + +Some releases include Preview features that are not considered production-ready, and must be enabled before you can use them. For more information about enabling Preview features, refer to the following documentation: + +- [Enable Prisma Client and schema preview features](/orm/reference/preview-features/client-preview-features) +- [Enable Prisma CLI preview features](/orm/reference/preview-features/cli-preview-features) + + + +We do not recommend using Preview features in production. + + diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/01-how-to-upgrade.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/01-how-to-upgrade.mdx new file mode 100644 index 0000000000..673c434a07 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/01-how-to-upgrade.mdx @@ -0,0 +1,137 @@ +--- +title: 'How to upgrade' +metaTitle: 'How to upgrade from Prisma 1 to Prisma version 2.x and later' +metaDescription: 'Learn how to upgrade your Prisma 1 project to Prisma version 2.x and later' +--- + +## Overview + +This page helps you make an informed decision on when and how to upgrade from Prisma 1 to Prisma version 2._x_ and later. + +## Upgrade documentation + +The upgrade documentation consists of several pages, here's an overview of how to use them: + +- **How to upgrade** (_you are here_): Starting point to learn about the upgrade process in general. +- [Schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql): A _reference_ page about the schema incompatibilities between Prisma 1 and Prisma 2._x_ (and later versions). Reading this page is optional but it will give you a better understanding of certain steps in the upgrade process. + +In addition to these two pages, there are various _practical guides_ that walk you through an example scenario of the upgrade process: + +- [Upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql): No matter what your Prisma 1 setup looks like, you should **always start your upgrade process by following this guide**. + +Once you're done with that guide, you can choose **one of the following four guides to upgrade your application layer**: + +- [Old to new Nexus](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-nexus-prisma-to-nexus): Choose this guide if you're currently running Prisma 1 with GraphQL Nexus. +- [prisma-binding to Nexus](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-prisma-binding-to-nexus): Choose this guide if you're currently running Prisma 1 with `prisma-binding` and want to upgrade to [Nexus](https://www.nexusjs.org/#/). +- [prisma-binding to SDL-first](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-prisma-binding-to-sdl-first): Choose this guide if you're currently running Prisma 1 with `prisma-binding` and want to upgrade to an [SDL-first](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3) GraphQL server. +- [REST API](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-a-rest-api): Choose this guide if you're currently running Prisma 1 using Prisma Client 1 and are building a REST API. + +## Main differences between Prisma 1 and Prisma version 2._x_ and later + +On a high-level, the biggest differences between Prisma 1 and Prisma versions 2._x_ and later are summarized below. + +Prisma 2._x_ and later versions: + +- don't require hosting a database proxy server (i.e., the [Prisma server](https://v1.prisma.io/docs/1.34/prisma-server/)). +- make the features of Prisma 1 more modular and splits them into dedicated tools: + - Prisma Client: An improved version of Prisma Client 1.0 + - Prisma Migrate: Data modeling and migrations (formerly `prisma deploy`). +- use the [Prisma schema](/orm/prisma-schema), a merge of Prisma 1 datamodel and `prisma.yml`. +- use its own [modeling language](https://github.com/prisma/specs/tree/master/schema) instead of being based on GraphQL SDL. +- don't expose ["a GraphQL API for your database"](https://www.prisma.io/blog/prisma-and-graphql-mfl5y2r7t49c) anymore, but only allows for _programmatic access_ via the Prisma Client API. + - don't support Prisma binding any more. +- allows connecting Prisma 2._x_ and later version to any existing database, via more powerful introspection + +## Feature parity + +Prisma 2._x_ and later versions do not yet have full feature parity with Prisma 1. The biggest feature that is still missing from Prisma versions 2._x_ and later is real-time subscriptions. + +- **Real-time API (Subscriptions)**: Prisma version 2._x_ and later currently [doesn't have a way to subscribe to events happening in the database](https://github.com/prisma/prisma/issues/298) and get notified in real time. It is currently unclear if, when, and in what form a real-time API will be added to Prisma versions 2._x_ and later. For the time being, you can implement real-time functionality using native database triggers, or if you're using GraphQL subscriptions you can consider triggering subscriptions manually inside your _mutation resolvers_. + +## Schema incompatibilities + +The database schema that is created when running `prisma deploy` in Prisma 1 is only partially compatible with the one that Prisma versions 2._x_ and later creates. This section gives a quick overview of the general incompatibilities and the potential workarounds. - + +> **Note**: For a detailed explanation of the problems and respective workarounds, please refer to the [Schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) page. + +Here's an overview of the different columns: + +- **Problem**: A short description of the problem when upgrading from Prisma 1 to Prisma versions 2._x_ and later +- **SQL**: Can this be solved by making a non-breaking change to the SQL schema? +- **Prisma schema**: Can this be solved by making a non-breaking change to the schema in Prisma versions 2._x_ and later? +- **Breaking Prisma 1**: Do the SQL statements break the Prisma 1 setup? This is only relevant when you're choosing the gradual side-by-side [upgrade strategy](#upgrade-strategies). + +| Problem | SQL | Prisma schema | Breaking Prisma 1 | +| ------------------------------------------------------------------------ | ------- | ------------- | ---------------------------------- | +| Default values aren't represented in database | Yes | Yes | No | +| Generated CUIDs as ID values aren't represented in database | No | Yes | No | +| `@createdAt` isn't represented in database | Yes | Yes | No | +| `@updatedAt` isn't represented in database | No | Yes | No | +| Inline 1-1 relations are recognized as 1-n (missing `UNIQUE` constraint) | Yes | No | No | +| _All_ non-inline relations are recognized as m-n | Yes | No | Yes | +| Json type is represented as `TEXT` in database | Yes | No | No (MySQL)
Yes (PostgreSQL) | +| Enums are represented as `TEXT` in database | Yes | No | No (MySQL)
Yes (PostgreSQL) | +| Required 1-1 relations are not represented in database | No | Yes | No | +| `@db` attributes from Prisma 1 are not transferred to the Prisma schema | No | Yes | No | +| Mismatching CUID length | Yes | No | No | +| Scalar lists (arrays) are maintained with extra table | Depends | No | Depends | + +> **Note**: A general drawback with the workarounds in the Prisma schema is that [changes to the Prisma schema get lost after re-introspecting the database](https://github.com/prisma/prisma/issues/2425) and need to be re-added manually after each introspection run. + +## Prisma 1 Upgrade CLI + +The [Prisma 1 Upgrade CLI](https://github.com/prisma/prisma1-upgrade) helps you apply the workarounds that are explained on the [Schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) page. It generates the SQL statements to fix the database schema and make it compatible with Prisma versions 2._x_ and later. Note that you are in full control over the operations that are executed against your database, the Upgrade CLI only generates and prints the statements for you. The Upgrade CLI also takes care of the workarounds in the Prisma schema. + +On a high-level, the upgrade workflow using the Upgrade CLI looks as follows. + +For the **initial setup**: + +1. You set up Prisma by installing the Prisma versions 2._x_ and later CLI and running `npx prisma init`. +1. You connect to your database and introspect it with `npx prisma db pull`. + +![Prisma CLI introspection flow](images/prisma-cli-introspection-flow.png) + +For **fixing the schema incompatibilities**: + +1. You invoke the Upgrade CLI with `npx prisma-upgrade`. +1. The Upgrade CLI generates SQL commands for you to run on your database. +1. You run the SQL commands against your database. +1. You run the `prisma db pull` command again. +1. You run the `npx prisma-upgrade` command again. +1. The Upgrade CLI adjusts the Prisma schema (version 2._x_ and later) by adding missing attributes. + +![Fixing the schema incompatibilities](images/fix-schema-incompatibilities.png) + +Note that the Upgrade CLI is designed in a way that **you can stop and re-start the process at any time**. Once you ran a SQL command that was generated by the Upgrade CLI against your database, the SQL command will not show up the next time you invoke the Upgrade CLI. That way, you can gradually resolve all schema incompatibilities when it's convenient for you. + +## Upgrade strategies + +There are two main upgrade strategies: + +- **Upgrade all at once**: Entirely remove Prisma 1 from your project and move everything over to Prisma version 2._x_ or later at once. +- **Gradual upgrade side-by-side**: Add Prisma version 2._x_ and later to the existing Prisma 1 project and gradually replace existing Prisma 1 features with the newer Prisma features while running them side-by-side. + +Note that if you are planning to run Prisma 1 and Prisma 2._x_ or later version side-by-side, you must not yet resolve the [schema compatibilities](#schema-incompatibilities) that are breaking the Prisma 1 setup. + +### When to choose which strategy + +If your project is not yet running in production or has little traffic and user data, the **all at once** strategy is recommended. + +In case your project already sees a lot of traffic and has a lot of user data stored in the database, you might want to consider the **gradual** upgrade strategy where you're running Prisma 1 and Prisma 2 or later side-by-side for a certain amount of time until you've replace all former Prisma 1 functionality with Prisma 2 or later version. + +Note that you won't be able to fix the [schema incompatibilities](#schema-incompatibilities) that require a "Breaking Prisma 1" change if you choose the gradual upgrade strategy and intend to run Prisma 1 and Prisma version 2._x_ or later side-by-side. That's because these data migrations are breaking the schema that Prisma 1 expects. This means that your Prisma Client API might not feel as idiomatic as it could, but you still get the full feature set of Prisma Client. + +### Upgrade path + +No matter which of the strategies you choose, on a high-level the envisioned upgrade path looks as follows: + +1. Install the new Prisma version 2._x_ or later CLI as a development dependency +1. Create your Prisma schema and configure the database connection URL +1. Use the Prisma version 2._x_ or later CLI to introspect your Prisma 1 database and generate your Prisma schema +1. Run the [Prisma 1 Upgrade CLI](https://github.com/prisma/prisma1-upgrade) to "fix" the Prisma schema +1. Install and generate Prisma Client version 2._x_ or later +1. Adjust your application code, specifically replace the API calls from the Prisma Client 1.0 with those of Prisma Client version 2._x_ or later + +## Next steps + +Once you've made the decision to upgrade, continue with the [Upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql) guide. diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/02-schema-incompatibilities.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/02-schema-incompatibilities.mdx new file mode 100644 index 0000000000..3a1b5efb50 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/02-schema-incompatibilities.mdx @@ -0,0 +1,856 @@ +--- +title: 'Schema incompatibilities' +# metaTitle: 'Problems and workarounds for Prisma 1 and 2.x and later schemas' +# metaDescription: 'Learn about the schema incompatibilities between Prisma 1 and Prisma versions 2 and later, and how to resolve them with several workarounds.' +dbSwitcher: ['postgresql', 'mysql'] +techMetaTitles: + [ + { name: 'mysql', value: 'Schema Incompatibilities | MySQL' }, + { name: 'postgresql', value: 'Schema Incompatibilities | PostgreSQL' }, + ] +techMetaDescriptions: + [ + { + name: 'mysql', + value: 'Problems and workarounds for Prisma 1 and 2.0 schemas with MySQL', + }, + { + name: 'postgresql', + value: 'Problems and workarounds for Prisma 1 and 2.0 schemas with PostgreSQL', + }, + ] +--- + +## Overview + +Each section on this page describes a potential problem when upgrading from Prisma 1 to Prisma 2._x_ and later and explains the available workarounds. + +## Default values aren't represented in database + +### Problem + +When adding the `@default` directive in a Prisma 1 datamodel, the default values for this field are generated by the Prisma 1 server at runtime. There's no `DEFAULT` constraint added to the database column. Because this constraint is not reflected in the database itself, the Prisma 2._x_ and later versions of introspection can't recognize it. + +### Example + +#### Prisma 1 datamodel + +```graphql +type Post { + id: ID! @id + published: Boolean @default(value: false) +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "Post" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + published BOOLEAN NOT NULL +); +``` + +#### Result of introspection in Prisma versions 2._x_ and later + +```prisma file=schema.prisma +model Post { + id String @id + published Boolean +} +``` + +Because the `DEFAULT` constraint has not been added to the database when mapping the Prisma 1 datamodel to the database with `prisma deploy`, Prisma v2 (and later versions) doesn't recognize it during introspection. + +### Workarounds + +#### Manually add a `DEFAULT` constraint to the database column + +You can alter the column to add the `DEFAULT` constraint as follows: + + + +```sql +ALTER TABLE "Post" + ALTER COLUMN published SET DEFAULT false; +``` + + + + + +```sql +ALTER TABLE `Post` + ALTER COLUMN published SET DEFAULT false; +``` + + + +After this adjustment, you can re-introspect your database and the `@default` attribute will be added to the `published` field: + +```prisma line-number file=schema.prisma highlight=3;normal +model Post { + id String @id + published Boolean @default(false) +} +``` + +#### Manually add a `@default` attribute to the Prisma model + +You can add the `@default` attribute to the Prisma model: + +```prisma line-number file=schema.prisma highlight=3;add +model Post { + id String + published Boolean @default(false) +} +``` + +If the `@default` attribute is set in the Prisma schema and you run `prisma generate`, the resulting Prisma Client code will generate the specified default values at runtime (similar to what the Prisma 1 server did in Prisma 1). + +## Generated CUIDs as ID values aren't represented in database + +### Problem + +Prisma 1 auto-generates ID values as CUIDs for `ID` fields when they're annotated with the `@id` directive. These CUIDs are generated by the Prisma 1 server at runtime. Because this behavior is not reflected in the database itself, the introspection in Prisma 2._x_ and later can't recognize it. + +### Example + +#### Prisma 1 datamodel + +```graphql +type Post { + id: ID! @id +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "Post" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); +``` + +#### Result of introspection in Prisma versions 2._x_ and later + +```prisma file=schema.prisma +model Post { + id String @id +} +``` + +Because there's no indication of the CUID behavior in the database, Prisma's introspection doesn't recognize it. + +### Workaround + +As a workaround, you can manually add the `@default(cuid())` attribute to the Prisma model: + +```prisma line-number file=schema.prisma highlight=2;add +model Post { + id String @id @default(cuid()) +} +``` + +If the `@default` attribute is set in the Prisma schema and you run `prisma generate`, the resulting Prisma Client code will generate the specified default values at runtime (similar to what the Prisma 1 server did in Prisma 1). + +Note that you'll have to re-add the attribute after each introspection because introspection removes it (as the previous version of the Prisma schema is overwritten)! + +## `@createdAt` isn't represented in database + +### Problem + +Prisma 1 auto-generates values for `DateTime` fields when they're annotated with the `@createdAt` directive. These values are generated by the Prisma 1 server at runtime. Because this behavior is not reflected in the database itself, the introspection in Prisma 2._x_ and later can't recognize it. + +### Example + +#### Prisma 1 datamodel + +```graphql line-number highlight=3;normal +type Post { + id: ID! @id + createdAt: DateTime! @createdAt +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "Post" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + "createdAt" TIMESTAMP NOT NULL +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model Post { + id String @id + createdAt DateTime +} +``` + +### Workarounds + +#### Manually add `DEFAULT CURRENT_TIMESTAMP` to the database column + +You can alter the column to add the `DEFAULT` constraint as follows: + +```sql +ALTER TABLE "Post" + ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP; +``` + +After this adjustment, you can re-introspect your database and the `@default` attribute will be added to the `createdAt` field: + +```prisma file=schema.prisma +model Post { + id String + createdAt DateTime @default(now()) +} +``` + +#### Manually add the `@default(now())` attribute to the Prisma model + +As a workaround, you can manually add the `@default(now())` attribute to the Prisma model: + +```prisma line-number file=schema.prisma highlight=3;normal +model Post { + id String @id + createdAt DateTime @default(now()) +} +``` + +If the `@default` attribute is set in the Prisma schema and you run `prisma generate`, the resulting Prisma Client code will generate the specified default values at runtime (similar to what the Prisma 1 server did in Prisma 1). + +Note that you'll have to re-add the attribute after each introspection because introspection removes it (as the previous version of the Prisma schema is overwritten)! + +## `@updatedAt` isn't represented in database + +### Problem + +Prisma 1 auto-generates values for `DateTime` fields when they're annotated with the `@updatedAt` directive. These values are generated by the Prisma 1 server at runtime. Because this behavior is not reflected in the database itself, the introspection in Prisma 2._x_ and later can't recognize it.. + +### Example + +#### Prisma 1 datamodel + +```graphql +type Post { + id: ID! @id + updatedAt: DateTime! @updatedAt +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "Post" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + updatedAt TIMESTAMP +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model Post { + id String @id + updatedAt DateTime +} +``` + +### Workarounds + +#### Manually add the `@updatedAt` attribute to the Prisma model + +As a workaround, you can manually add the `@updatedAt` attribute to the Prisma model: + +```prisma line-number file=schema.prisma highlight=3;add +model Post { + id String @id + updatedAt DateTime @updatedAt +} +``` + +If the `@updatedAt` attribute is set in the Prisma schema and you run `prisma generate`, the resulting Prisma Client code will automatically generate values for this column when an existing record is updated (similar to what the Prisma 1 server did in Prisma 1). + +Note that you'll have to re-add the attribute after each introspection because introspection removes it (as the previous version of the Prisma schema is overwritten)! + +## Inline 1-1 relations are recognized as 1-n (missing `UNIQUE` constraint) + +### Problem + +In the [datamodel v1.1](https://www.prisma.io/blog/datamodel-v11-lrzqy1f56c90) that was introduced in Prisma v1.31, 1-1 relations can be declared as _inline_. In that case, the relation will not be maintained via a [relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) but via a single foreign key on one of the two tables involved. + +When this approach is used, Prisma doesn't add a `UNIQUE` constraint to the foreign key column which means that after introspection in Prisma version 2._x_ and later, this former 1-1 relation will be added as a 1-n relation to the Prisma schema. + +### Example + +#### Prisma datamodel v1.1 (available from Prisma v1.31) + +```graphql +type User { + id: ID! @id + profile: Profile @relation(link: INLINE) +} + +type Profile { + id: ID! @id + user: User +} +``` + +Note that omitting the `@relation` directive in this case would result in the same behavior because `link: INLINE` is the _default_ for 1-1 relations. + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); + +CREATE TABLE "Profile" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + "user" VARCHAR(25), + FOREIGN KEY ("user") REFERENCES "User"(id) +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model User { + id String @id + Profile Profile[] +} + +model Profile { + id String @id + user String? + User User? @relation(fields: [user], references: [id]) +} +``` + +Because there's no `UNIQUE` constraint defined on the `user` column (which represents the foreign key in this relation), Prisma's introspection recognizes the relation as 1-n. + +### Workaround + +#### Manually add `UNIQUE` constraint to the foreign key column + +You can alter the foreign key column to add the `UNIQUE` constraint as follows: + + + +```sql +ALTER TABLE "Profile" + ADD CONSTRAINT userId_unique UNIQUE ("user"); +``` + + + + + +```sql +ALTER TABLE `Profile` + ADD CONSTRAINT userId_unique UNIQUE (`user`); +``` + + + +After this adjustment, you can re-introspect your database and the 1-1 relation will be properly recognized: + +```prisma line-number file=schema.prisma highlight=3;normal +model User { + id String @id + Profile Profile? +} + +model Profile { + id String @id + user String? @unique + User User? @relation(fields: [user], references: [id]) +} +``` + +## _All_ non-inline relations are recognized as m-n + +### Problem + +Prisma 1 represents relations as relation tables most of the time: + +- All relations in the Prisma 1 **datamodel v1.0** are represented as relation tables +- In **datamodel v1.1**, all m-n relations as well as the 1-1 and 1-n relations declared as `link: TABLE` are represented as relation tables. + +Because of this representation, introspection in Prisma version 2._x_ and later will recognize all these relations as m-n relations, even though they might have been declared as 1-1 or 1-n in Prisma 1. + +### Example + +#### Prisma 1 datamodel + +```graphql +type User { + id: ID! @id + posts: [Post!]! +} + +type Post { + id: ID! @id + author: User! @relation(link: TABLE) +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); + +CREATE TABLE "Post" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); + +CREATE TABLE "_PostToUser" ( + "A" VARCHAR(25) NOT NULL REFERENCES "Post"(id) ON DELETE CASCADE, + "B" VARCHAR(25) NOT NULL REFERENCES "User"(id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX "_PostToUser_AB_unique" ON "_PostToUser"("A" text_ops,"B" text_ops); +CREATE INDEX "_PostToUser_B" ON "_PostToUser"("B" text_ops); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model User { + id String @id + Post Post[] @relation(references: [id]) +} + +model Post { + id String @id + User User[] @relation(references: [id]) +} +``` + +Because the relation table that was created by Prisma 1 uses the same [conventions for relation tables](/orm/prisma-schema/data-model/relations/many-to-many-relations#conventions-for-relation-tables-in-implicit-m-n-relations) as in Prisma version 2._x_ and later, the relation now gets recognized as a m-n relation. + +### Workaround + +As a workaround, you can migrate the data into a structure that's compatible with Prisma's 1-n relation: + + + +1. Create new column `authorId` on the `Post` table. This column should be a _foreign key_ that references the `id` field of the `User` table: + ```sql + ALTER TABLE "Post" ADD COLUMN "authorId" VARCHAR(25); + ALTER TABLE "Post" + ADD CONSTRAINT fk_author + FOREIGN KEY ("authorId") + REFERENCES "User"("id"); + ``` +1. Write a SQL query that reads all the rows from the `_PostToUser` relation table and for each row: + 1. Finds the respective `Post` record by looking up the value from column `A` + 1. Inserts the value from column `B` as the value for `authorId` into that `Post` record + ```sql + UPDATE "Post" post + SET "authorId" = post_to_user."B" + FROM "_PostToUser" post_to_user + WHERE post_to_user."A" = post."id"; + ``` +1. Delete the `_PostToUser` relation table + ```sql + DROP TABLE "_PostToUser"; + ``` + + + + + +1. Create new column `authorId` on the `Post` table. This column should be a _foreign key_ that references the `id` field of the `User` table: + ```sql + ALTER TABLE `Post` ADD COLUMN `authorId` VARCHAR(25); + ALTER TABLE `Post` ADD FOREIGN KEY (`authorId`) REFERENCES `User` (`id`); + ``` +1. Write a SQL query that reads all the rows from the `_PostToUser` relation table and for each row: + 1. Finds the respective `Post` record by looking up the value from column `A` + 1. Inserts the value from column `B` as the value for `authorId` into that `Post` record + ```sql + UPDATE Post, _PostToUser + SET Post.authorId = _PostToUser.B + WHERE Post.id = _PostToUser.A + ``` +1. Delete the `_PostToUser` relation table + ```sql + DROP TABLE `_PostToUser`; + ``` + + + +After that you can introspect your database and the relation will now be recognized as 1-n: + +```prisma line-number file=schema.prisma highlight=3,8,9;normal +model User { + id String @id + Post Post[] +} + +model Post { + id String @id + User User @relation(fields: [authorId], references: [id]) + authorId String +} +``` + +## `Json` type is represented as `TEXT` in database + +### Problem + +Prisma 1 supports the `Json` data type in its datamodel. However, in the underlying database, fields of type `Json` are actually stored as plain strings using the `TEXT` data type of the underlying database. Any parsing and validation of the stored JSON data is done by the Prisma 1 server at runtime. + +### Example + +#### Prisma 1 datamodel + +```graphql +type User { + id: ID! @id + jsonData: Json +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + jsonData TEXT +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model User { + id String @id + jsonData String? +} +``` + +### Workaround + +You can manually change the type of the column to `JSON` + + + +```sql +ALTER TABLE "User" ALTER COLUMN "jsonData" TYPE JSON USING "jsonData"::json; +``` + + + + + +```sql +ALTER TABLE User MODIFY COLUMN jsonData JSON; +``` + + + +After this adjustment, you can re-introspect your database and the field will now be recognized as `Json`: + +```prisma line-number file=schema.prisma highlight=3;normal +model User { + id String @id + jsonData Json? +} +``` + +## Enums are represented as `TEXT` in database + +### Problem + +Prisma 1 supports the `enum` data type in its datamodel. However, in the underlying database, types declared as `enum` are actually stored as plain strings using the `TEXT` data type of the underlying database. Any validation of the stored `enum` data is done by the Prisma 1 server at runtime. + +### Example + +#### Prisma 1 datamodel + +```graphql +type User { + id: ID! @id + role: Role +} + +enum Role { + ADMIN + CUSTOMER +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL, + role TEXT +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model User { + id String @id + role String? +} +``` + +### Workaround + +You can manually turn the `role` column into an enum with your desired values: + +1. Create an `enum` in your database that mirrors the `enum` you defined in the Prisma 1 datamodel: + ```sql + CREATE TYPE "Role" AS ENUM ('CUSTOMER', 'ADMIN'); + ``` +1. Change the type from `TEXT` to your new `enum`: + ```sql + ALTER TABLE "User" ALTER COLUMN "role" TYPE "Role" + USING "role"::text::"Role"; + ``` + +After introspection, the type is now properly recognized as an enum: + +```prisma line-number file=schema.prisma highlight=3,6-9;normal +model User { + id String @id + role Role? +} + +enum Role { + ADMIN + CUSTOMER +} +``` + +## Mismatching CUID length + +### Problem + +Prisma 1 uses CUIDs as ID values for all database records. In the underlying database, these IDs are represented as strings with a maximum size of 25 characters (as `VARCHAR(25)`). However, when configuring default CUIDs in your Prisma 2._x_ (or later versions) schema with `@default(cuid())` the generated ID values might exceed the limit of 25 characters (the maximum length might be 30 characters). To make your IDs proof for Prisma 2._x_ (or later versions), you therefore need to adjust the column type to `VARCHAR(30)`. + +### Example + +#### Prisma 1 datamodel + +```graphql +type User { + id: ID! @id +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); +``` + +#### Result of introspection in Prisma 2._x_ and later versions + +```prisma file=schema.prisma +model User { + id String @id +} +``` + +### Workaround + +You can manually turn the `VARCHAR(25)` columns into `VARCHAR(30)`: + + + +```sql +ALTER TABLE "User" ALTER COLUMN "id" SET DATA TYPE character varying(30); +``` + + + + + +```sql +SET FOREIGN_KEY_CHECKS=0; +ALTER TABLE `User` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; +SET FOREIGN_KEY_CHECKS=1; +``` + + + +> **Note**: When fixing this issue with the [Upgrade CLI](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#prisma-1-upgrade-cli), the generated SQL statements will keep appearing in the Upgrade CLI even after you have changed the column types in the underlying database. This is a currently a limitation in the Upgrade CLI. + +## Scalar lists (arrays) are maintained with extra table + +### Problem + +In Prisma 1, you can define lists of _scalar_ types on your models. Under the hood, this is implemented with an extra table that keeps track of the values in the list. + +To remove the approach with the extra table which incurred hidden performance costs, Prisma 2._x_ and later versions only support scalar lists only when they're natively supported by the database you use. At the moment, only [PostgreSQL supports scalar lists (arrays) natively](https://www.postgresql.org/docs/9.1/arrays.html). + +With PostgreSQL, you therefore can keep using scalar lists in Prisma 2._x_ and later versions, but you'll need to perform a data migration to transfer the data from the extra table from Prisma 1 into an actual PostgreSQL array. + +### Example + +#### Prisma 1 datamodel + +```graphql +type User { + id: ID! @id + coinflips: [Boolean!]! @scalarList(strategy: RELATION) +} +``` + +#### Prisma 1 generated SQL migration + +```sql +CREATE TABLE "User" ( + id VARCHAR(25) PRIMARY KEY NOT NULL +); + +CREATE TABLE "User_coinflips" ( + "nodeId" VARCHAR(25) REFERENCES "User"(id), + position INTEGER, + value BOOLEAN NOT NULL, + CONSTRAINT "User_coinflips_pkey" PRIMARY KEY ("nodeId", position) +); +CREATE UNIQUE INDEX "User_coinflips_pkey" ON "User_coinflips"("nodeId" text_ops,position int4_ops); +``` + +#### Result of Prisma 2 introspection + +```prisma file=schema.prisma +model User { + id String @id + User_coinflips User_coinflips[] +} + +model User_coinflips { + nodeId String + position Int + value Boolean + User User @relation(fields: [nodeId], references: [id]) + + @@id([nodeId, position]) +} +``` + +Note that you can now generate Prisma Client and you'll be able to access the data from the scalar lists through the extra table. PostgreSQL users can alternatively migrate the data into a native PostgreSQL array and continue to benefit from the slicker Prisma Client API for scalar lists (read the section below for more info). + +
+ +Expand for sample Prisma Client API calls + +To access the coinflips data, you will now have to always [`include`](/orm/prisma-client/queries/select-fields#include-relations-and-select-relation-fields) it in your queries: + +```ts +const user = await prisma.user.findUnique({ + where: { id: 1 }, + include: { + coinflips: { + orderBy: { position: 'asc' }, + }, + }, +}) +``` + +> **Note**: The `orderBy` is important to retain the order of the list. + +This is the `result of the query: + +```js +{ + id: 1, + name: 'Alice', + coinflips: [ + { id: 1, position: 1000, value: false }, + { id: 2, position: 2000, value: true }, + { id: 3, position: 3000, value: false }, + { id: 4, position: 4000, value: true }, + { id: 5, position: 5000, value: true }, + { id: 6, position: 6000, value: false } + ] +} +``` + +To access just the boolean values from the list, you can `map` over the `coinflips` on `user` as follows: + +```ts +const currentCoinflips = user!.coinflips.map((cf) => cf.value) +``` + +> **Note**: The exclamation mark above means that you're _force unwrapping_ the `user` value. This is necessary because the `user` returned from the previous query might be `null`. + +Here's the value of `currentCoinflips` after the call to `map`: + +```json5 +[false, true, false, true, true, false] +``` + +
+ +### Workaround + +The following workaround is only available for PostgreSQL users! + +As scalar lists (i.e. [arrays](https://www.postgresql.org/docs/9.1/arrays.html)) are available as a native PostgreSQL feature, you can keep using the same notation of `coinflips: Boolean[]` in your Prisma schema. + +However, in order to do so you need to manually migrate the underlying data from the `User_coinflips` table into a PostgreSQL array. Here's how you can do that: + +1. Add the new `coinflips` column to the `User` tables: + ```sql + ALTER TABLE "User" ADD COLUMN coinflips BOOLEAN[]; + ``` +1. Migrate the data from `"User_coinflips".value` to `"User.coinflips"`: + ```sql + UPDATE "User" + SET coinflips = t.flips + FROM ( + SELECT "nodeId", array_agg(VALUE ORDER BY position) AS flips + FROM "User_coinflips" + GROUP BY "nodeId" + ) t + where t."nodeId" = "User"."id"; + ``` +1. To cleanup, you can delete the `User_coinflips` table: + ```sql + DROP TABLE "User_coinflips"; + ``` + +You can now introspect your database and the `coinflips` field will be represented as an array in your new Prisma schema: + +```prisma file=schema.prisma +model User { + id String @id + coinflips Boolean[] +} +``` + +You can keep using Prisma Client as before: + +```ts +const user = await prisma.user.findUnique({ + where: { id: 1 }, +}) +``` + +This is the result from the API call: + +```js +{ + id: 1, + name: 'Alice', + coinflips: [ false, true, false, true, true, false ] +} +``` diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/03-upgrading-the-prisma-layer.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/03-upgrading-the-prisma-layer.mdx new file mode 100644 index 0000000000..314eadff69 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/03-upgrading-the-prisma-layer.mdx @@ -0,0 +1,1618 @@ +--- +title: 'Upgrading the Prisma layer' +# metaTitle: 'Upgrading the Prisma layer to Prisma 2' +# metaDescription: 'Learn how to upgrade the Prisma layer to Prisma 2 and create your Prisma schema.' +dbSwitcher: ['postgresql', 'mysql'] +techMetaTitles: + [ + { name: 'mysql', value: 'Upgrading the Prisma layer to Prisma 2 | MySQL' }, + { + name: 'postgresql', + value: 'Upgrading the Prisma layer to Prisma 2 | PostgreSQL', + }, + ] +techMetaDescriptions: + [ + { + name: 'mysql', + value: 'Learn how to upgrade the Prisma layer to Prisma 2 and create your Prisma schema with MySQL', + }, + { + name: 'postgresql', + value: 'Learn how to upgrade the Prisma layer to Prisma 2 and create your Prisma schema with PostgreSQL', + }, + ] +--- + +## Overview + +This page explains the first step of your upgrade process: Taking your Prisma 1 configuration and upgrading it to Prisma 2. Concretely, you will learn how to: + +1. Add the Prisma 2 CLI as a development dependency +1. Create your Prisma 2 schema +1. Determine your connection URL and connect to your database +1. Introspect your database (that was so far managed with Prisma 1) +1. Use the [Prisma 1 Upgrade CLI](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#prisma-1-upgrade-cli) to resolve the [schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) in the new Prisma 2 data model +1. Install and generate Prisma Client + +Once done with these steps, you can move on to the next guide that explains how you can upgrade the application layer to use Prisma Client for your database queries. + +> **Note**: During the upgrade process it can be helpful to get a graphical view on your database. It is therefore recommended to use a graphical database client to connect to your database, such as [TablePlus](https://tableplus.com/) or [Postico](https://eggerapps.at/postico/). + +## 1. Install Prisma 2 CLI + +The Prisma 2 CLI is available as the [`prisma`](https://www.npmjs.com/package/prisma) package on npm and is invoked via the `prisma` command. + +Note that the former `prisma` command for Prisma 1 has been renamed to `prisma1`. You can learn more about this [here](https://www.prisma.io/blog/prisma-2-beta-b7bcl0gd8d8e#renaming-the-prisma2-cli). + +You can install the Prisma 2 CLI in your Node.js project as follows (be sure to invoke this command in the directory where your `package.json` is located): + +```terminal copy +npm install prisma --save-dev +``` + +> **Note**: With Prisma 1, it was usually recommended to install the CLI globally. We now recommend to [install the Prisma CLI locally](/orm/tools/prisma-cli#installation) to prevent version conflicts. + +You can now use the local installation of the `prisma` CLI by prefixing it with `npx`: + +```terminal +npx prisma +``` + +If you're upgrading your entire project [all at once](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#upgrade-strategies), you can now also uninstall the Prisma 1 CLI (otherwise expand below): + +```terminal +# remove global installation +npm uninstall -g prisma1 + +# remove local installation +npm uninstall prisma1 +``` + +
+ +
+ +Expand if you want to keep using your Prisma 1 CLI side-by-side + +If you want to keep using the Prisma 1 CLI, it is recommend to remove your global installation of it and add the `prisma1` CLI as a development dependency: + +```terminal +# installs v1.34 of the Prisma 1 CLI +npm uninstall -g prisma +npm install prisma1 --save-dev +``` + +You can now invoke it as follows: + +```terminal +npx prisma1 +``` + +Note that if you need a CLI version smaller than 1.34 (e.g. 1.30), you can install it as follows: + +```terminal +# installs v1.30 of the Prisma 1 CLI +npm uninstall -g prisma@1.30 +npm install prisma@1.30 --save-dev +``` + +You can now invoke it as follows: + +```terminal +npx prisma +``` + +
+ +## 2. Create your Prisma 2 schema + +For this guide, you'll first create a new Prisma schema using the `prisma init` command and then "fill" it with a data model using [introspection](/orm/prisma-schema/introspection). + +Run the following command to create your Prisma schema (note that this throws an error if you already have a folder called `prisma`): + +```terminal copy +npx prisma init +``` + +If you're seeing the following error, you need to rename your current `prisma` directory: + +```no-lines +ERROR A folder called prisma already exists in your project. + Please try again in a project that is not yet using Prisma. +``` + +You can rename the current `prisma` directory to `prisma1` to make it clear that this holds the former Prisma 1 configuration: + +```terminal copy +mv prisma prisma1 +``` + +Now you can run `init` and it will succeed: + +```terminal copy +npx prisma init +``` + +It should print the following output: + +```no-lines wrap +✔ Your Prisma schema was created at prisma/schema.prisma. + You can now open it in your favorite editor. + +Next steps: +1. Set the `DATABASE_URL` in the `.env` file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started +2. Set the `provider` of your `datasource` block in `schema.prisma` to match your database: `postgresql`, `mysql` or `sqlite`. +3. Run `prisma db pull` to turn your database schema into a Prisma data model. +4. Run `prisma generate` to install Prisma Client. You can then start querying your database. + +More information in our documentation: +https://pris.ly/d/getting-started +``` + +The command created a new folder called `prisma`, and two files: + +- `prisma/schema.prisma`: Your Prisma schema file that specifies the [data source](/orm/prisma-schema/overview/data-sources), [generator](/orm/prisma-schema/overview/generators) and [data model](/orm/prisma-schema/data-model/models) (note that the data model doesn't exist yet, it will be generated via introspection). +- `.env`: A [dotenv](https://github.com/motdotla/dotenv#readme) file to configure your database [connection URL](/orm/reference/connection-urls). + +Your initial Prisma schema looks as follows: + +```prisma file=schema.prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +With Prisma 1, you specify which language variant of Prisma Client you wanted to use in your `prisma.yml`. With Prisma 2, this information is now specified inside the Prisma schema via a `generator` block. + +> **Note**: Unlike Prisma 1, the TypeScript and JavaScript variants of Prisma Client 2.0 use the _same_ generator called `prisma-client-js`. The generated types in `index.d.ts` are _always_ included, even in plain JavaScript projects. This enables feature like autocompletion in VS Code even when not using TypeScript. + +## 3. Determine your connection URL and connect to your database + +With Prisma 1, the database connection is configured in the Docker Compose file that's used to launch the Prisma server. The Prisma server then exposes a GraphQL endpoint (via HTTP) that proxies all database requests from the Prisma Client application code. That HTTP endpoint is specified in your `prisma.yml`. + +With Prisma 2, the HTTP layer isn't exposed any more and Prisma Client 2.0 is configured to run requests "directly" against the database (that is, requests are proxied by Prisma's [query engine](/orm/more/under-the-hood/engines), but there isn't an extra server any more). + +So, as a next step you'll need to tell Prisma 2 _what_ kind of database you use (MySQL or PostgreSQL) and _where_ it is located. + +First, you need to ensure that that `provider` field on the `datasource` block inside `schema.prisma` is configured to use the right database: + +- If you're using PostgreSQL, it needs to define the value `"postgresql"` in the `provider` field. +- If you're using MySQL, it needs to define the value `"mysql"` in the `provider` field. + +Switch around with the tabs in the code block to see examples of both: + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + + + + +```prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} +``` + + + + +With the `provider` field set, you can go ahead and configure the connection URL inside the `.env` file. + +Assume the database configuration in your Docker Compose file that you used to deploy your Prisma server looks as follows: + + + +```yml file=docker-compose.yml +PRISMA_CONFIG: | + port: 4466 + databases: + default: + connector: postgres + host: postgres + port: 5432 + user: prisma + password: prisma +``` + +Also assume your `endpoint` in `prisma.yml` is configured as follows: + +```yml file=prisma.yml +endpoint: http://localhost:4466/myproject/dev +``` + +Based on these connection details, you need to configure the `DATABASE_URL` environment variable inside your `.env` file as follows: + +```bash file=.env +DATABASE_URL="postgresql://janedoe:randompassword@localhost:5432/prisma?schema=myproject$dev" +``` + +Note that the `schema` argument is typically composed of your _service name_ and _service stage_ (which are part of the `endpoint` in `prisma.yml`), separated by the `$` character. + +Sometimes no service name and stage are specified in `prisma.yml`: + +```yml file=prisma.yml +endpoint: http://localhost:4466/ +``` + +In that case, the `schema` must be specified as follows: + +```bash file=.env +DATABASE_URL="postgresql://janedoe:randompassword@localhost:5432/prisma?schema=default$default" +``` + + + + + +```yml file=docker-compose.yml +PRISMA_CONFIG: | + port: 4466 + databases: + default: + connector: mysql + host: mysql + port: 3306 + user: root + password: randompassword +``` + +Also assume your `endpoint` in `prisma.yml` is configured as follows: + +```yml file=prisma.yml +endpoint: http://localhost:4466/myproject/dev +``` + +Based on these connection details, you need to configure the `DATABASE_URL` environment variable inside your `.env` file as follows: + +```bash file=.env +DATABASE_URL="mysql://root:randompassword@localhost:3306/myproject@dev" +``` + +Note that the _database name_ in the connection URL is typically composed of your _service name_ and _service stage_ (which are part of the `endpoint` in `prisma.yml`), separated by the `@` character. + +Sometimes no service name and stage are specified in `prisma.yml`: + +```yml file=prisma.yml +endpoint: http://localhost:4466/ +``` + +In that case, the database name must be specified as follows: + +```bash file=.env +DATABASE_URL="mysql://root:randompassword@localhost:3306/default@default" +``` + + + +Learn more on the [Connection URLs](/orm/reference/connection-urls) page. + +## 4. Introspect your database + +For the purpose of this guide, we'll use the following Prisma 1 data model (select the **SQL** tab below to see what the data model maps to in SQL): + + + + +```graphql +type User { + id: ID! @id + email: String @unique + name: String! + role: Role! @default(value: CUSTOMER) + jsonData: Json + profile: Profile + posts: [Post!]! +} + +type Post { + id: ID! @id + createdAt: DateTime! @createdAt + updatedAt: DateTime! @updatedAt + title: String! + content: String + published: Boolean! @default(value: false) + author: User @relation(link: TABLE) + categories: [Category!]! +} + +type Profile { + id: ID! @id + bio: String + user: User! @relation(link: INLINE) +} + +type Category { + id: ID! @id + name: String! + posts: [Post!]! +} + +enum Role { + ADMIN + CUSTOMER +} +``` + + + + +```sql +CREATE TABLE"User" ( + id character varying(25) PRIMARY KEY, + email text, + name text NOT NULL, + role text NOT NULL, + "jsonData" text +); +CREATE UNIQUE INDEX "User_pkey" ON"User"(id text_ops); +CREATE UNIQUE INDEX "default$default.User.email._UNIQUE" ON"User"(email text_ops); + +CREATE TABLE"Post" ( + id character varying(25) PRIMARY KEY, + title text NOT NULL, + published boolean NOT NULL, + "createdAt" timestamp(3) without time zone NOT NULL, + "updatedAt" timestamp(3) without time zone NOT NULL, + content text +); +CREATE UNIQUE INDEX "Post_pkey" ON"Post"(id text_ops); + +CREATE TABLE"Profile" ( + id character varying(25) PRIMARY KEY, + bio text, + user character varying(25) REFERENCES"User"(id) ON DELETE SET NULL +); +CREATE UNIQUE INDEX "Profile_pkey" ON"Profile"(id text_ops); + +CREATE TABLE"Category" ( + id character varying(25) PRIMARY KEY, + name text NOT NULL +); +CREATE UNIQUE INDEX "Category_pkey" ON"Category"(id text_ops); + +CREATE TABLE"_PostToUser" ( + "A" character varying(25) NOT NULL REFERENCES"Post"(id) ON DELETE CASCADE, + "B" character varying(25) NOT NULL REFERENCES"User"(id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX "_PostToUser_AB_unique" ON"_PostToUser"("A" text_ops,"B" text_ops); +CREATE INDEX "_PostToUser_B" ON"_PostToUser"("B" text_ops); + +CREATE TABLE"_CategoryToPost" ( + "A" character varying(25) NOT NULL REFERENCES"Category"(id) ON DELETE CASCADE, + "B" character varying(25) NOT NULL REFERENCES"Post"(id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX "_CategoryToPost_AB_unique" ON"_CategoryToPost"("A" text_ops,"B" text_ops); +CREATE INDEX "_CategoryToPost_B" ON"_CategoryToPost"("B" text_ops); +``` + + + + +Note that this data model has three [relations](/orm/prisma-schema/data-model/relations): + +- 1-1: `User` ↔ `Profile` +- 1-n: `User` ↔ `Post` (maintained via the `_PostToUser` relation table) +- m-n: `Post` ↔ `Category` (maintained via the `_CategoryToPost` relation table) + +Now you can run Prisma's introspection against your database with the following command: + +```terminal copy +npx prisma db pull +``` + +Here's a graphical illustration for what happens when `db pull` is invoked: + +![Introspect your database with Prisma](/img/prisma-db-pull-generate-schema.png) + +For the above Prisma 1 datamodel, this results in the following Prisma 2 schema (note that the models have been reordered to match the initial order of the Prisma 1 datamodel): + +```prisma file=schema.prisma +model User { + id String @id @default(cuid()) + email String? @unique + name String + role String + jsonData String? + Profile Profile[] + Post Post[] +} + +model Post { + id String @id @default(cuid()) + createdAt DateTime + updatedAt DateTime + title String + content String? + published Boolean + Category Category[] + User User[] +} + +model Profile { + id String @id @default(cuid()) + bio String? + user String? @unique + User User? @relation(fields: [user], references: [id]) +} + +model Category { + id String @id @default(cuid()) + name String + Post Post[] +} +``` + +While this is already a valid Prisma 2 schema, it lacks a number of _features_ that were part of its Prisma 1 equivalent: + +- no auto-generated date values for the `createdAt` and `updatedAt` fields on `Post` +- no default value for the `role` field on `User` +- no default value for the `published` field on `Post` + +There further are a number of inconsistencies which result in a less idiomatic/ergonomic Prisma Client API: + +- `User` ↔ `Profile` is a 1-n instead of 1-1 relation +- `User` ↔ `Post` is a m-n instead of 1-n relation +- relation fields are uppercased (e.g. `Profile` and `Post` on `User`) +- the `jsonData` field on `User` is of type `String` instead of `Json` +- the `role` field on `User` is of type `String` instead of `Role`, the `enum` definition for role is missing altogether + +While these inconsistencies don't actually impact the "feature set" you'll have available in your Prisma Client API, they make you lose certain constraints/guarantees that were present before. + +For example, Prisma now won't guarantee that a `User` is connected to _at most_ one `Profile` because the relation between the tables was recognized as 1-n during introspection, so one `User` record _could_ now get connected to multiple `Profile` records. + +Another issue is that you can store whatever text for the `jsonData` and `role` fields, regardless of whether it's valid JSON or represents a value of the `Role` enum. + +To learn more about these inconsistencies check out the [Schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) page. + +In the following, we'll go through these incompatibilities and fix them one by one using the Prisma schema upgrade CLI. + +## 5. Use the Prisma schema upgrade CLI to resolve schema incompatibilities + +The [Prisma 1 Upgrade CLI](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#prisma-1-upgrade-cli) is an interactive tool that helps you upgrading your Prisma schema and ironing out most of the inconsistencies listed above. + +The Prisma 1 Upgrade CLI works in two major phases: + +1. Fix the database schema via plain SQL +1. Add missing attributes to the Prisma 2 schema and other schema fixes + +During the first phase, it will generate and print a number of SQL statements that you should run against your database to adjust the database schema. You can either run all of the statements or a subset of them before continuing to the second phase. + +During the second phase, you don't need to do anything manually. The Upgrade CLI will make changes to your Prisma schema by adding certain Prisma-level attributes (like `@default(cuid))` or `@updatedAt`), adjusting the names of relation fields to match the ones from your Prisma 1 datamodel and ensure 1-1-relations that were required on both sides in your Prisma 1 datamodel are also required in the Prisma 2 schema. + +Note that **you can start over at any time during the process** and go back from the second to the first phase. + +In this illustration, the green area shows the first phase, the blue area shows the second phase. Note that you can optionally run `prisma db pull` in between the phases to update your Prisma data model: + +![Fixing the schema incompatibilities](images/fix-schema-incompatibilities.png) + +To use the Upgrade CLI, you can either install it locally in your project, or invoke it once without installation using `npx` as done here: + +```terminal copy +npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma +``` + +The CLI will greet you with the following message: + +```no-lines wrap +◮ Welcome to the interactive Prisma Upgrade CLI that helps with the +upgrade process from Prisma 1 to Prisma 2. + +Please read the docs to learn more about the upgrade process: +https://pris.ly/d/how-to-upgrade + +➤ Goal +The Upgrade CLI helps you resolve the schema incompatibilities +between Prisma 1 and Prisma 2. Learn more in the docs: +https://pris.ly/d/schema-incompatibilities + +➤ How it works +Throughout the process, you'll need to adjust your database schema by sending +SQL statements to it. The SQL statements are provided by the Upgrade CLI. + +Note that the Upgrade CLI never makes changes to your database, +you are in full control over any operations that are executed against it. + +You can stop and re-run the Upgrade CLI at any time. + +These are the different steps of the upgrade process: + + 1. The Upgrade CLI generates SQL commands for you to run on your database. + 2. You run the SQL commands against your database. + 3. You run the `npx prisma db pull` command again. + 4. You run the `npx prisma-upgrade` command again. + 5. The Upgrade CLI adjusts the Prisma 2 schema by adding missing attributes. + +➤ Note +It is recommended that you make a full backup of your existing data before starting +the upgrade process. If possible, the migration should be performed in a staging +environment before executed against a production environment. + +➤ Help +If you have any questions or run into any problems along the way, +please create an issue at: +https://github.com/prisma/upgrade/issues/new + +Are you ready? [Y/n] +``` + +Press the Y button, then confirm by hitting RETURN on your keyboard to continue. + +Once you confirmed, the CLI outputs the SQL statements you should be running against your database: + + + +```no-lines wrap +➤ Adjust your database schema +Run the following SQL statements against your database: + + Fix columns with ENUM data types + https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database + + CREATE TYPE "default$default"."Role" AS ENUM ('ADMIN', 'CUSTOMER'); + ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DATA TYPE "default$default"."Role" using "role"::"default$default"."Role"; + + + Add missing `DEFAULT` constraints to the database + https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database + + ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DEFAULT 'CUSTOMER'; + ALTER TABLE "default$default"."Post" ALTER COLUMN "published" SET DEFAULT false; + + + Fix columns with JSON data types + https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database + + ALTER TABLE "default$default"."User" ALTER COLUMN "jsonData" SET DATA TYPE JSONB USING "jsonData"::TEXT::JSONB; + + + Replicate `@createdAt` behavior in Prisma 2 + https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database + + ALTER TABLE "default$default"."Post" ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP; + + + Fix 1-1 relations by adding `UNIQUE` constraints + https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint + + ALTER TABLE "default$default"."Profile" ADD UNIQUE ("user"); + + + Migrate IDs from varchar(25) to varchar(30) + https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length + + ALTER TABLE "default$default"."Category" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Post" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Profile" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Profile" ALTER COLUMN "user" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."User" ALTER COLUMN "id" SET DATA TYPE character varying(30); + +➤ Breaking changes detected + +In order to fully optimize your database schema, you'll need to run a few SQL +statements that can break your Prisma 1 setup. Note that these changes are optional +and if you are upgrading gradually and running Prisma 1 and Prisma 2 side-by-side, +you should not perform these changes yet. Instead, you can perform them whenever +you are ready to completely remove Prisma 1 from your project. +If you are upgrading all at once, you can safely perform these changes now. + +Learn more in the docs: +https://pris.ly/d/how-to-upgrade' +``` + + + + + +```no-lines wrap +➤ Adjust your database schema +Run the following SQL statements against your database: + + Fix columns with ENUM data types + https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database + + ALTER TABLE `User` CHANGE `role` `role` ENUM('ADMIN', 'CUSTOMER') NOT NULL; + + + Add missing `DEFAULT` constraints to the database + https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database + + ALTER TABLE `User` CHANGE `role` `role` ENUM('ADMIN', 'CUSTOMER') NOT NULL DEFAULT 'CUSTOMER'; + ALTER TABLE `Post` CHANGE `published` `published` TINYINT(1) NOT NULL DEFAULT 0; + + + Fix columns with JSON data types + https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database + + ALTER TABLE `User` CHANGE `jsonData` `jsonData` JSON ; + + + Replicate `@createdAt` behavior in Prisma 2.0 + https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database + + ALTER TABLE `Post` CHANGE `createdAt` `createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; + + + Fix 1-1 relations by adding `UNIQUE` constraints + https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint + + ALTER TABLE `Profile` ADD UNIQUE (`user`); + + + Migrate IDs from varchar(25) to varchar(30) + https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length + + SET FOREIGN_KEY_CHECKS=0; + ALTER TABLE `Category` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; + ALTER TABLE `Post` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; + ALTER TABLE `Profile` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; + ALTER TABLE `Profile` CHANGE `user` `user` char(30) CHARACTER SET utf8 ; + ALTER TABLE `User` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; + SET FOREIGN_KEY_CHECKS=1; + +➤ Breaking changes detected + +In order to fully optimize your database schema, you'll need to run a few SQL +statements that can break your Prisma 1 setup. Note that these changes are optional +and if you are upgrading gradually and running Prisma 1 and Prisma 2 side-by-side, +you should not perform these changes yet. Instead, you can perform them whenever +you are ready to completely remove Prisma 1 from your project. +If you are upgrading all at once, you can safely perform these changes now. + +Learn more in the docs: +https://pris.ly/d/how-to-upgrade' +``` + + + +> **Note**: If you're seeing the note about breaking changes, you can ignore it for now. We'll discuss it later. + +The shown SQL statements are categorized into a number of "buckets", all aiming to resolve a certain [schema incompatibility](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql): + +- Fix columns with ENUM data types +- Add missing `DEFAULT` constraints to the database +- Fix columns with JSON data types +- Replicate `@createdAt` behavior in Prisma 2 +- Fix 1-1 relations by adding `UNIQUE` constraints + +As a next step, you can start sending the SQL statements to your database. Note that all of these changes are non-breaking and you'll be able to continue using Prisma 1 side-by-side with Prisma 2. + +The next sections cover the different kinds of SQL statements to be sent to your database individually. + +### 5.1. Fix the database schema via plain SQL (non-breaking) + +In this section, we'll walk through the printed SQL statements and run them against the database one by one. + +### 5.1.1. Fix columns with ENUM data types + +The first thing the tool does is help you ensure that `enum` definitions in your Prisma 1 datamodel will be represented as actual `ENUM` types in the underlying database, right now they are represented as plain strings (e.g. as `MEDIUMTEXT` in MySQL). + +The CLI currently shows the following output: + + + +```no-lines wrap +Fix columns with ENUM data types +https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database + + CREATE TYPE "default$default"."Role" AS ENUM ('ADMIN', 'CUSTOMER'); + ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DATA TYPE "default$default"."Role" using "role"::"default$default"."Role"; +``` + +> **⚠️ Warning**: If you are running Prisma 1 and Prisma 2 [side-by-side](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#upgrade-strategies), these [SQL statements will break your Prisma 1 setup](https://github.com/prisma/upgrade/issues/74). The docs will be updated to reflect this soon. + + + + + +```no-lines wrap +Fix columns with ENUM data types +https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database + + ALTER TABLE `User` CHANGE `role` `role` ENUM('ADMIN', 'CUSTOMER') NOT NULL; +``` + + + +Go ahead and run these statements against your database now. + +![Altering columns to use ENUM with SQL](images/altering-columns-to-use-enum.png) + +### 5.1.2. Add missing `DEFAULT` constraints to the database + +Next, the Upgrade CLI helps you resolve the issue that [default values aren't represented in the database](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#default-values-arent-represented-in-database) by generating SQL statements that add the respective `DEFAULT` constraints directly to the database. + +In this case, two `DEFAULT` constraints are missing which are suggested by the tool: + + + +```no-lines wrap +Add missing `DEFAULT` constraints to the database +https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database + + ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DEFAULT 'CUSTOMER'; + ALTER TABLE "default$default"."Post" ALTER COLUMN "published" SET DEFAULT false; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like Postico: + +![Adding missing `DEFAULT` constraints to columns](images/add-missing-default-constraints-to-columns.png) + + + + + +```no-lines wrap +Add missing `DEFAULT` constraints to the database +https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database + + ALTER TABLE `User` CHANGE `role` `role` ENUM('ADMIN', 'CUSTOMER') NOT NULL DEFAULT 'CUSTOMER'; + ALTER TABLE `Post` CHANGE `published` `published` TINYINT(1) NOT NULL DEFAULT 0; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like TablePlus: + +![TablePlus GUI](images/TablePlus-GUI.png) + + + +### 5.1.3. Fix columns with JSON data types + +Next, the tool helps you ensure that `Json` fields in your Prisma 1 datamodel will be represented as `JSON` columns in the underlying database, right now they are represented as plain strings (e.g. as `MEDIUMTEXT` in MySQL). + +Changing the column type to `JSON` will ensure that the field is properly recognized as `Json` during Prisma 2 introspection. + +The CLI currently shows the following output: + + + +```no-lines wrap +Fix columns with JSON data types +https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database + + ALTER TABLE "default$default"."User" ALTER COLUMN "jsonData" TYPE JSON USING "jsonData"::json; +``` + +> **⚠️ Warning**: If you are running Prisma 1 and Prisma 2 [side-by-side](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#upgrade-strategies), these [SQL statements will break your Prisma 1 setup](https://github.com/prisma/upgrade/issues/73). The docs will be updated to reflect this soon. + +You can now run these SQL statements against your database either using a command line client or a GUI like Postico: + +![Adding missing `DEFAULT` constraints to columns](images/fix-columns-with-json-data-types.png) + + + + + +```no-lines wrap +Fix columns with JSON data types +https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database + + ALTER TABLE `User` CHANGE `jsonData` `jsonData` JSON ; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like TablePlus: + +![TablePlus GUI](images/fix-columns-with-json-data-types.png) + + + +### 5.1.4. Replicate `@createdAt` behavior in Prisma 2 + +The next thing the tools does is help you resolve the issue that the behavior of [`@createdAt` isn't represented in database](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#default-values-arent-represented-in-database) + +The CLI currently shows the following output: + + + +```no-lines wrap +Replicate `@createdAt` behavior in Prisma 2.0 +https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database + + ALTER TABLE "default$default"."Post" ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like Postico: + +![Running an SQL command to alter a column](images/run-sql-command-to-alter-column.png) + + + + + +```no-lines wrap +Replicate `@createdAt` behavior in Prisma 2.0 +https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database + + ALTER TABLE `Post` CHANGE `createdAt` `createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like TablePlus. + + + +### 5.1.5. Fix 1-1 relations by adding `UNIQUE` constraints + +Now, the tool will help you [turn the current 1-n relation between `User` ↔ `Profile` back into a 1-1 relation](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint) by adding a `UNIQUE` constraint to the foreign key column called `user` (named after the relation field in the Prisma 1 datamodel) in the database. + +The CLI currently shows the following output: + + + +```no-lines wrap +Fix 1-1 relations by adding `UNIQUE` constraints +https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint + + ALTER TABLE "default$default"."Profile" ADD UNIQUE ("user"); +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like Postico: + +![Running an SQL command to alter a column](images/run-sql-command-to-alter-column.png) + + + + + +```no-lines wrap +Fix 1-1 relations by adding `UNIQUE` constraints +https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint + + ALTER TABLE `Profile` ADD UNIQUE (`user`); +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like TablePlus. + + + +### 5.1.6. Fix mismatch of CUID length + +> **Note**: These SQL statements will keep appearing in the Upgrade CLI even after you have changed the column types in the underlying database. This is a currently a limitation in the Upgrade CLI. + +Finally, the tool will help you [turn the current ID columns of type `VARCHAR(25)` into `VARCHAR(30)`](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#mismatching-cuid-length) by adding a `UNIQUE` constraint to the foreign key column called `user` (named after the relation field in the Prisma 1 datamodel) in the database. + +The CLI currently shows the following output: + + + +```no-lines wrap wrap +Migrate IDs from varchar(25) to varchar(30) +https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length + + ALTER TABLE "default$default"."Category" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Post" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Profile" ALTER COLUMN "id" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."Profile" ALTER COLUMN "user" SET DATA TYPE character varying(30); + ALTER TABLE "default$default"."User" ALTER COLUMN "id" SET DATA TYPE character varying(30); +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like Postico: + +![Running an SQL command to alter a column](images/run-sql-command-to-alter-column.png) + + + + + +```no-lines wrap +Migrate IDs from varchar(25) to varchar(30) +https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length + +SET FOREIGN_KEY_CHECKS=0; +ALTER TABLE `Category` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; +ALTER TABLE `Post` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; +ALTER TABLE `Profile` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; +ALTER TABLE `Profile` CHANGE `user` `user` char(30) CHARACTER SET utf8 ; +ALTER TABLE `User` CHANGE `id` `id` char(30) CHARACTER SET utf8 NOT NULL; +SET FOREIGN_KEY_CHECKS=1; +``` + +You can now run these SQL statements against your database either using a command line client or a GUI like TablePlus. + + + +### 5.1.7. Breaking changes detected + +In case the Upgrade CLI has printed a note about breaking changes, your database schema needs some adjustments that will break Prisma 1 compatibility in order to be fully optimized. + +If there are no breaking changes detected, you can [skip forward to section 5.2](#52-re-introspect-your-database-to-update-your-prisma-schema) + +Depending on your [upgrade strategy](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#upgrade-strategies), you can either perform these changes now or skip to the next phase of the Upgrade CLI: + +- If you are following the gradual side-by-side upgrade strategy, do not perform these changes yet since they will break your Prisma 1 setup. In that case, you can continue to the next phase of the Upgrade CLI by typing n and hitting RETURN. +- If you are following the all at once upgrade strategy, you can perform these changes now. In that case, continue by typing Y and hitting RETURN. + +### 5.2. Fix the database schema via plain SQL (breaking) + +In this section, you'll resolve the schema incompatibilities that are breaking your Prisma 1 setup. Do not perform these changes if you are still running Prisma 1 in your project! + +### 5.2.1. Fix incorrect m-n relations + +Now, the Upgrade CLI helps you fix all 1-1 and 1-n relations that Prisma 1 represents with relation tables and that [currently only exist as m-n relations](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#all-non-inline-relations-are-recognized-as-m-n) in your new Prisma 2 schema. Concretely, this is the case for the `User` ↔ `Post` relation which currently is defined as m-n but _should_ really be a 1-n relation. + +To fix this, you'll need to perform the following migration: + +1. Create a new foreign key column on `Post` to link directly to the `User` table. +1. Migrate the foreign key values from the relation table into the new foreign key column on `Post`. +1. Delete the relation table. + +These instructions are now printed by the CLI: + + + +```no-lines wrap +➤ Adjust your database schema +Run the following SQL statements against your database: + + Fix one-to-many table relations + https://pris.ly/d/schema-incompatibilities#all-non-inline-relations-are-recognized-as-m-n + + ALTER TABLE "default$default"."Post" ADD COLUMN "authorId" character varying(25) ; + ALTER TABLE "default$default"."Post" ADD CONSTRAINT "author" FOREIGN KEY ("authorId") REFERENCES "default$default"."User"("id"); + UPDATE "default$default"."Post" SET "authorId" = "default$default"."_PostToUser"."B" FROM "default$default"."_PostToUser" WHERE "default$default"."_PostToUser"."A" = "default$default"."Post"."id"; + DROP TABLE "default$default"."_PostToUser"; + + +➤ Next Steps + +After you executed one or more of the previous SQL statements against your database, +please run the following two commands to refresh your Prisma 2 schema and check +the changes. + + 1. Run `npx prisma db pull` again to refresh your Prisma 2 schema. + 2. Run `npx prisma-upgrade` again. + +If you can't or don't want to execute the remaining SQL statements right now, you can +skip to the last step where the Upgrade CLI adds missing attributes to your Prisma 2 +schema that are not picked up by introspection. + +Skip to the last step? [Y/n]? +``` + +For this fix, you'll need to run three SQL statements: + +1. Create new column `authorId` on the `Post` table. This column should be a _foreign key_ that references the `id` field of the `User` table: + ```sql no-lines + ALTER TABLE `Post` ADD COLUMN `authorId` VARCHAR(25); + ALTER TABLE `Post` ADD FOREIGN KEY (`authorId`) REFERENCES `User` (`id`); + ``` +1. Write a SQL query that reads all the rows from the `_PostToUser` relation table and for each row: + 1. Finds the respective `Post` record by looking up the value from column `A` + 1. Inserts the value from column `B` as the value for `authorId` into that `Post` record + ```sql no-lines + UPDATE Post, _PostToUser + SET Post.authorId = _PostToUser.B + WHERE Post.id = _PostToUser.A + ``` +1. Delete the `_PostToUser` relation table + ```sql no-lines + DROP TABLE `_PostToUser`; + ``` + +![Fixing incorrect m-n relations with SQL](images/fix-incorrect-m-n-relations-sql.png) + + + + + +```no-lines wrap +➤ Adjust your database schema +Run the following SQL statements against your database: + + Fix one-to-many table relations + https://pris.ly/d/schema-incompatibilities#all-non-inline-relations-are-recognized-as-m-n + + ALTER TABLE `Post` ADD COLUMN `authorId` char(25) CHARACTER SET utf8 ; + ALTER TABLE `Post` ADD CONSTRAINT author FOREIGN KEY (`authorId`) REFERENCES `User`(`id`); + UPDATE `Post`, `_PostToUser` SET `Post`.`authorId` = `_PostToUser`.B where `_PostToUser`.A = `Post`.`id`; + DROP TABLE `_PostToUser`; + + +➤ Next Steps + +After you executed one or more of the above SQL statements against your database, +please run the following two commands to refresh your Prisma 2 Schema and check +the changes. + + 1. Run `npx prisma db pull` again to refresh your Prisma 2 schema. + 2. Run `npx prisma-upgrade` again. + +If you can't or don't want to execute the remaining SQL statements right now, you can +skip to the last step where the Upgrade CLI adds missing attributes to your Prisma 2 +schema that are not picked up by introspection. + +Skip to the last step? [Y/n]? +``` + +For this fix, you'll need to run three SQL statements: + +1. Create new column `authorId` on the `Post` table. This column should be a _foreign key_ that references the `id` field of the `User` table: + ```sql no-lines + ALTER TABLE `Post` ADD COLUMN `authorId` char(25) CHARACTER SET utf8 ; + ALTER TABLE `Post` ADD CONSTRAINT author FOREIGN KEY (`authorId`) REFERENCES `User`(`id`); + ``` +1. Write a SQL query that reads all the rows from the `_PostToUser` relation table and for each row: + 1. Finds the respective `Post` record by looking up the value from column `A` + 1. Inserts the value from column `B` as the value for `authorId` into that `Post` record + ```sql no-lines + UPDATE `Post`, `_PostToUser` SET `Post`.`authorId` = `_PostToUser`.B where `_PostToUser`.A = `Post`.`id`; + ``` +1. Delete the `_PostToUser` relation table + ```sql no-lines + DROP TABLE `_PostToUser`; + ``` + +![Fixing incorrect m-n relations with SQL](images/fix-incorrect-m-n-relations-sql.png) + + + +After these commands, the user ID values of the records from column `B` of the relation table are migrated to the new `authorId` column. + +### 5.2. Re-introspect your database to update your Prisma schema + +At this point, you've resolved the schema incompatibilities with the Upgrade CLI. You can now exit the Upgrade CLI for now by typing n and hitting RETURN. + +In this section, you'll update your Prisma schema with another introspection round. This time, the previous flaws of the Prisma schema will be resolved because the database schema has been adjusted: + +```terminal copy +npx prisma db pull +``` + +This time, the resulting Prisma schema looks as follows: + +```prisma file=schema.prisma +model User { + id String @id + name String + email String? @unique + jsonData Json? + role Role @default(CUSTOMER) + Post Post[] + Profile Profile? +} + +model Post { + id String @id + createdAt DateTime @default(now()) + updatedAt DateTime + title String + content String? + published Boolean @default(false) + authorId String? + User User? @relation(fields: [authorId], references: [id]) + Category Category[] @relation(references: [id]) +} + +model Category { + id String @id + name String + Post Post[] @relation(references: [id]) +} + +model Profile { + bio String? + id String @id + user String? @unique + User User? @relation(fields: [user], references: [id]) +} + +enum Role { + ADMIN + CUSTOMER +} +``` + +This schema has most issues resolved, but it still lacks the following: + +### 5.2. Add missing attributes to the Prisma 2 schema and other schema fixes + +The CLI now prints the following: + +```no-lines wrap +➤ What happens next +As a last step, some final adjustments will be made to your Prisma 2 schema +to carry over some Prisma-level attributes that aren't picked up by introspection. + +As a last step, some final adjustments will be made to your Prisma 2.0 +schema to carry over some Prisma-level attributes that aren't picked +up by introspection. + +Warning +Your current Prisma 2.0 schema will be overwritten, so please +make sure you have a backup! + +Are you ready? [Y/n] +``` + +At this point, you either ran all the SQL statement that were printed by the CLI or you skipped some of them. Either way, you can now move on the last step and let the Upgrade CLI add the missing Prisma 2 attributes. Typically these are the following: + +- `@default(cuid())` for your `@id` fields +- `@updatedAt` for any fields that were using this attribute in Prisma 1 +- `@map` and `@@map` as replacements for `@db` and `@@db` from Prisma 1 + +In that step, the Upgrade CLI also fixes other issues that occurred in the transition to Prisma 2: + +- it makes sure that 1-1-relations that were required on both sides in Prisma 1 are also required in your Prisma 2 schema +- it renames relation fields to the same names they had in your Prisma 1 datamodel ([coming soon](https://github.com/prisma/upgrade/issues/25)) + +To apply these changes, you can re-run the Upgrade CLI: + +```terminal copy +npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma +``` + +If you did not resolve all schema incompatibilities, the Upgrade CLI now prints the remaining SQL statements (as well as the ones for migrating IDs). You can just ignore them at this point and continue to the last step by continuously typing Y and hitting RETURN when prompted. + +If you did resolve all schema incompatibilities, no SQL statements will be printed and the Upgrade CLI only outputs the following: + +```no-lines wrap +$ npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma + +➤ Next Steps + +After you executed one or more of the previous SQL statements against your database, +please run the following two commands to refresh your Prisma 2 schema and check +the changes. + + 1. Run `npx prisma db pull` again to refresh your Prisma 2 schema. + 2. Run `npx prisma-upgrade` again. + +If you can't or don't want to execute the remaining SQL statements right now, you can +skip to the last step where the Upgrade CLI adds missing attributes to your Prisma 2 +schema that are not picked up by introspection. + +Skip to the last step? [Y/n]? +``` + +One more time, type Y and hit RETURN to confirm. + +The final prompt of the Upgrade CLI now asks you to confirm the above mentioned changes it will make to your Prisma schema: + +```no-lines wrap +➤ What happens next +As a last step, some final adjustments will be made to your Prisma 2 schema +to carry over some Prisma-level attributes that aren't picked up by introspection. + +As a last step, some final adjustments will be made to your Prisma 2.0 +schema to carry over some Prisma-level attributes that aren't picked +up by introspection. + +Warning +Your current Prisma 2.0 schema will be overwritten, so please +make sure you have a backup! + +Are you ready? [Y/n] +``` + +One last time, type Y and hit RETURN to confirm. + +This is the final output of the Upgrade CLI: + +```no-lines +Updating prisma/schema.prisma... +Done updating prisma/schema.prisma! + +✔ Congratulations, you're all set! + +➤ Note +If you didn't execute all generated SQL commands against your database, +you can re-run the Upgrade CLI at any time. + +Note that the Upgrade CLI doesn't resolve all of the schema incompatibilities +between Prisma 1 and Prisma 2. If you want to resolve the remaining ones, +you can do so manually by following this guide: +https://pris.ly/d/upgrading-the-prisma-layer + +➤ Next steps +Otherwise you can continue your upgrade process by installing Prisma Client 2: +npm install @prisma/client + +You can find guides for different upgrade scenarios in the docs: +https://pris.ly/d/upgrade-from-prisma-1 +``` + +### 5.3. Final result + +The final version of the Prisma schema should look as follows: + +```prisma file=schema.prisma +model User { + id String @id @default(cuid()) + name String + email String? @unique + jsonData Json? + role Role @default(CUSTOMER) + Post Post[] + Profile Profile? +} + +model Post { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String + content String? + published Boolean @default(false) + authorId String? + User User? @relation(fields: [authorId], references: [id]) + Category Category[] @relation(references: [id]) +} + +model Profile { + id String @id @default(cuid()) + bio String? + user String? @unique + User User? @relation(fields: [user], references: [id]) +} + +model Category { + id String @id @default(cuid()) + name String + Post Post[] @relation(references: [id]) +} + +enum Role { + ADMIN + CUSTOMER +} +``` + +### 5.4. Rename relation fields + +One thing you'll notice with this version of the Prisma 2 schema is that all [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) are named after their respective models, e.g: + +```prisma file=schema.prisma +model User { + Post Post[] + Profile Profile? +} + +model Post { + User User? @relation(fields: [authorId], references: [id]) + Category Category[] @relation(references: [id]) +} + +model Profile { + User User? @relation(fields: [user], references: [id]) +} + +model Category { + Post Post[] @relation(references: [id]) +} +``` + +This is not ideal and you can in fact manually rename all of them to their previous versions! + +Because all relation fields are _virtual_, meaning they don't _manifest_ in the database, you can name them whatever you like. In this case, all relation fields are lowercased and sometimes pluralized. + +Here's what they look like after the rename: + +```prisma file=schema.prisma +model User { + posts Post[] + profile Profile? +} + +model Post { + author User? @relation(fields: [authorId], references: [id]) + categories Category[] @relation(references: [id]) +} + +model Profile { + user String? @unique + owner User? @relation(fields: [user], references: [id]) +} + +model Category { + posts Post[] @relation(references: [id]) +} +``` + +> **Note**: For the 1-1-relation between `User` and `Profile` it was not possible to set the old name `user` for the relation field. This is because there'd be a naming conflict with the already existing [relation scalar](/orm/prisma-schema/data-model/relations#annotated-relation-fields) field that holds the foreign key. In that case, you can choose a different name or alternatively rename the foreign key column directly in the database via SQL. + +### 5.5. Resolving remaining schema incompatibilities + +There are a few schema incompatibilities that were not yet resolved by the Upgrade CLI. At this point you still haven't fixed [scalar lists](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#scalar-lists-arrays-are-maintained-with-extra-table) and [cascading deletes](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql#cascading-deletes-are-not-supported-in-prisma-2). You can find the recommended workarounds for these on the [Schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) page. + +## 6. Install and generate Prisma Client + +Now that you have your Prisma 2 schema ready, you can install Prisma Client with the following command: + +```terminal copy +npm install @prisma/client +``` + +## 7. Next steps + +Congratulations, you have now upgraded your Prisma layer to Prisma 2! From here on, you can move on to update your application code using one of the following guides: + +- [Old to new Nexus](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-nexus-prisma-to-nexus): Choose this guide if you're currently running Prisma 1 with GraphQL Nexus. +- [prisma-binding to Nexus](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-prisma-binding-to-nexus): Choose this guide if you're currently running Prisma 1 with `prisma-binding` and want to upgrade to [Nexus](https://www.nexusjs.org/#/) (and TypeScript). +- [prisma-binding to SDL-first](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-prisma-binding-to-sdl-first): Choose this guide if you're currently running Prisma 1 with `prisma-binding` and want to upgrade to an [SDL-first](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3) GraphQL server. +- [REST API](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-a-rest-api): Choose this guide if you're currently running Prisma 1 using Prisma Client 1 and are building a REST API. + +## Bonus: Prisma Client API comparison + +This section contains a high-level and side-by-side comparison of the Prisma Client APIs of Prisma 1 and Prisma 2. For more details about the new Prisma Client API, you can explore the [Prisma Client](/orm/prisma-client) docs. + +### Reading single records + + + + + +```ts +const user = await prisma.user({ id: 1 }) +``` + + + + + +```ts +await prisma.user.findUnique({ + where: { id: 1 }, +}) +``` + + + + + +### Reading lists of records + + + + + +```ts +const user = await prisma.users() +``` + + + + + +```ts +await prisma.user.findMany() +``` + + + + + +### Filtering lists + + + + + +```ts +const users = await prisma.users({ + where: { + name: 'Alice', + }, +}) +``` + + + + + +```ts +await prisma.user.findMany({ + where: { + name: 'Alice', + }, +}) +``` + + + + + +### Paginating lists + + + + + +```ts +const posts = await prisma.posts({ + skip: 5, + first: 10, +}) +``` + + + + + +```ts +await prisma.user.findMany({ + skip: 5, + take: 10, +}) +``` + + + + + +> **Note**: You can learn more about the new pagination API in the respective [release notes](https://github.com/prisma/prisma/releases/tag/2.0.0-beta.7) or the [Pagination](/orm/prisma-client/queries/pagination) page in the docs. + +### Sorting lists + + + + + +```ts +await prisma.posts({ + orderBy: 'title_ASC', +}) +``` + + + + + +```ts +await prisma.posts({ + orderBy: { + title: 'asc', + }, +}) +``` + + + + + +### Creating records + + + + + +```ts +await prisma.createUser({ + name: 'Alice', +}) +``` + + + + + +```ts +await prisma.user.create({ + data: { + name: 'Alice', + }, +}) +``` + + + + + +### Updating records + + + + + +```ts +await prisma.updateUser({ + where: { id: 1 }, + data: { + name: 'James', + email: 'james@prisma.io', + }, +}) +``` + + + + + +```ts +await prisma.user.update({ + where: { id: 1 }, + data: { + name: 'James', + email: 'james@prisma.io', + }, +}) +``` + + + + + +### Deleting records + + + + + +```ts +await prisma.deleteUser({ id: 1 }) +``` + + + + + +```ts +await prisma.user.delete({ + where: { id: 1 }, +}) +``` + + + + + +### Selecting fields & loading relations + +In Prisma 1, the only ways to select specific fields and/or load relations of an object was by using the string-based `$fragment` and `$graphql` functions. With Prisma 2, this is now done in a clean and type-safe manner using [`select`](/orm/prisma-client/queries/select-fields#select-specific-fields) and [`include`](/orm/prisma-client/queries/select-fields#include-relations-and-select-relation-fields). + +Another benefit of this approach is that you can use `select` and `include` on _any_ Prisma Client query, e.g. `findUnique`, `findMany`, `create`, `update`, `delete`, ... + + + + + +```ts +await prisma.user({ id: 1 }).$fragment(` + fragment NameAndEmail on User { id email }` +`) +``` + + + + + +```ts +await prisma.user.findUnique({ + where: { id: 1 }, + select: { + id: true, + email: true, + }, +}) +``` + + + + + +As an example, creating a new record and only retrieving the `id` in the returned object was not possible in Prisma 1. With Prisma 2 you can achieve this as follows: + +```ts +await prisma.user.create({ + data: { + name: 'Alice', + }, + select: { + id: true, + }, +}) +``` diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/04-upgrading-nexus-prisma-to-nexus.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/04-upgrading-nexus-prisma-to-nexus.mdx new file mode 100644 index 0000000000..0393e3d34a --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/04-upgrading-nexus-prisma-to-nexus.mdx @@ -0,0 +1,750 @@ +--- +title: 'Old to new Nexus' +metaTitle: 'Upgrade Prisma 1 with nexus-prisma to @nexus/schema' +metaDescription: 'Learn how to upgrade existing Prisma 1 projects with nexus-prisma to Prisma 2 and Nexus.' +--- + +## Overview + +> **Note**: This guide is not fully up-to-date as it currently uses the [deprecated](https://github.com/graphql-nexus/nexus-plugin-prisma/issues/1039) version of the [`nexus-plugin-prisma`](https://github.com/graphql-nexus/nexus-plugin-prisma). While this is still functional, it is recommended to use the new [`nexus-prisma`](https://github.com/prisma/nexus-prisma/) library or an alternative code-first GraphQL library like [Pothos](https://pothos-graphql.dev/) going forward. If you have any questions, feel free to drop them in the [`#prisma1-community`](https://app.slack.com/client/T0MQBS8JG/C0152UA4DH9) channel in the [Prisma Slack](https://slack.prisma.io). + +This upgrade guide describes how to upgrade a project that's based on [Prisma 1](https://github.com/prisma/prisma1) and uses [`nexus`](https://www.npmjs.com/package/nexus) (< v0.12.0) or [`@nexus/schema`](https://github.com/graphql-nexus/schema) together with [`nexus-prisma`](https://www.npmjs.com/package/nexus-prisma) (< v4.0.0) to implement a GraphQL server. + +The code will be upgraded to the latest version of `@nexus/schema`. Further, the `nexus-prisma` package will be replaced with the new [`nexus-plugin-prisma`](https://github.com/graphql-nexus/nexus-plugin-prisma). + +The guide assumes that you already went through the [guide for upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql). This means you already: + +- installed the Prisma 2 CLI +- created your Prisma 2 schema +- introspected your database and resolved potential [schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) +- installed and generated Prisma Client + +The guide further assumes that you have a file setup that looks similar to this: + +``` +. +├── README.md +├── package.json +├── prisma +│ └── schema.prisma +├── prisma1 +│ ├── datamodel.prisma +│ └── prisma.yml +└── src + ├── generated + │ ├── nexus-prisma + │ ├── nexus.ts + │ ├── prisma-client + │ └── schema.graphql + ├── types.ts + └── index.ts +``` + +The important parts are: + +- A folder called with `prisma` with your Prisma 2 schema +- A folder called `src` with your application code + +If this is not what your project structure looks like, you'll need to adjust the instructions in the guide to match your own setup. + +## 1. Upgrade Nexus dependencies + +To get started, you can remove the old Nexus and Prisma 1 dependencies: + +```copy +npm uninstall nexus nexus-prisma prisma-client-lib prisma1 +``` + +Then, you can install the latest `@nexus/schema` dependency in your project: + +```copy +npm install @nexus/schema +``` + +Next, install the Prisma plugin for Nexus which will allow you to expose Prisma models in your GraphQL API (this is the new equivalent of the former `nexus-prisma` package): + +```copy +npm install nexus-plugin-prisma +``` + +The `nexus-plugin-prisma` dependency bundles all required Prisma dependencies. You should therefore remove the dependencies that you added installed when you upgraded the Prisma layer of your app: + +```copy +npm uninstall @prisma/cli @prisma/client +``` + +Note however that you can still invoke the Prisma 2 CLI with the familiar command: + +```copy +npx prisma -v +``` + +> **Note**: If you see the output of the Prisma 1 CLI when running `npx prisma -v`, be sure to delete your `node_modules` folder and re-run `npm install`. + +## 2. Update the configuration of Nexus and Prisma + +To get started, you can remove the old imports that are not needed any more with your new setup: + +```ts line-number highlight=1-3;delete +import { makePrismaSchema, prismaObjectType } from 'nexus-prisma' +import datamodelInfo from './generated/nexus-prisma' +import { prisma } from './generated/prisma-client' +``` + +Instead, you now import the following into your application: + +```ts line-number highlight=1-3;add +import { nexusSchemaPrisma } from 'nexus-plugin-prisma/schema' +import { objectType, makeSchema, queryType, mutationType } from '@nexus/schema' +import { PrismaClient } from '@prisma/client' +``` + +Next you need to adjust the code where you currently create your `GraphQLSchema`, most likely this is currently happening via the `makePrismaSchema` function in your code. Since this function was imported from the removed `nexus-prisma` package, you'll need to replace it with the `makeSchema` function from the `@nexus/schema` package. The way how the Prisma plugin for Nexus is used also changes in the latest version. + +Here's an example for such a configuration: + +```ts file=./src/index.ts line-number highlight=2,12-14;add|1,8-11;delete + const schema = makePrismaSchema({ + const schema = makeSchema({ + + // Provide all the GraphQL types we've implemented + types: [Query, Mutation, UserUniqueInput, User, Post, Category, Profile], + + // Configure the interface to Prisma + prisma: { + datamodelInfo, + client: prisma, + }, + plugins: [nexusSchemaPrisma({ + experimentalCRUD: true, + })], + + // Specify where Nexus should put the generated files + outputs: { + schema: path.join(__dirname, './generated/schema.graphql'), + typegen: path.join(__dirname, './generated/nexus.ts'), + }, + + // Configure nullability of input arguments: All arguments are non-nullable by default + nonNullDefaults: { + input: false, + output: false, + }, + + // Configure automatic type resolution for the TS representations of the associated types + typegenAutoConfig: { + sources: [ + { + source: path.join(__dirname, './types.ts'), + alias: 'types', + }, + ], + contextType: 'types.Context', + }, +}) +``` + +If you previously typed the GraphQL `context` object that's passed through your resolver chain, you need to adjust the type like so: + +```ts file=./src/types.ts highlight=2,6;add|1,5;delete +import { Prisma } from './generated/prisma-client' +import { PrismaClient } from '@prisma/client' + +export interface Context { + prisma: Prisma + prisma: PrismaClient +} +``` + +## 3. Migrate your GraphQL types + +Here's a quick overview of the main differences between the two approaches of creating GraphQL types with the latest versions of `@nexus/schema` and `nexus-plugin-prisma`. + +- The `prismaObjectType` function is not available any more, all types are created with Nexus' `objectType` function. +- To expose Prisma models via Nexus, you can use the `t.model` property which is added to the `t` argument that's passed into Nexus' `definition` functions. `t.model` gives you access to the properties of a Prisma model and lets you expose them. +- Exposing CRUD operations for Prisma models via Nexus follows a similar approach. These are exposed via `t.crud` in the `definition` functions of your `queryType` and `mutationType` types. + +### 3.1. Migrating the `Post` type + +#### Type definition with the previous `nexus-prisma` package + +In the sample app, the `User` type is defined as follows: + +```ts +const User = prismaObjectType({ + name: 'User', + definition(t) { + t.prismaFields([ + 'id', + 'name', + 'email', + 'jsonData', + 'role' + { + name: 'posts', + args: [], // remove the arguments from the `posts` field of the `User` type in the Prisma schema + }, + ]) + }, +}) +``` + +#### Type definition with the latest version of `@nexus/schema` and the `nexus-plugin-prisma` + +With the latest version of `@nexus/schema`, you can now access the `objectType` function on your main `schema` instance and expose all fields from the Prisma model like so: + +```ts +const User = objectType({ + name: 'User', + definition(t) { + t.model.id() + t.model.name() + t.model.email() + t.model.jsonData() + t.model.role() + t.model.posts({ + pagination: false, + ordering: false, + filtering: false, + }) + t.model.profile() + }, +}) +``` + +Note that `t.model` looks at the `name` attribute in the object that's passed as an argument to the `objectType` function and matches it against the models in your Prisma schema. In this case, it's matched against the `User` model. Therefore, `t.model` exposes functions that are named after the fields of the `User` model. + +At this point, you might see errors on the relation fields `posts` and `profile`, e.g.: + +```bash highlight=1;delete +Missing type Post, did you forget to import a type to the root query? +``` + +This is because you didn't add the `Post` and `Profile` types to the GraphQL schema yet, the errors will go away once these types are part of the GraphQL schema as well! + +### 3.2. Migrating the `Post` type + +#### Type definition with the previous `nexus-prisma` package + +In the sample app, the `Post` type is defined as follows: + +```ts +const Post = prismaObjectType({ + name: 'Post', + definition(t) { + t.prismaFields(['*']) + }, +}) +``` + +The asterisk in `prismaFields` means that _all_ Prisma fields are exposed. + +#### Type definition with the latest version of `@nexus/schema` and the `nexus-plugin-prisma` + +With the latest version of `@nexus/schema`, you need to expose all fields explicitly, there's no option to just expose everything from a Prisma model. + +Therefore, the new definition of `Post` must explicitly list all its fields: + +```ts +const Post = objectType({ + name: 'Post', + definition(t) { + t.model.id() + t.model.title() + t.model.content() + t.model.published() + t.model.author() + t.model.categories() + }, +}) +``` + +Note that `t.model` looks at the `name` attribute and matches it against the models in your Prisma schema. In this case, it's matched against the `Post` model. Therefore, `t.model` exposes functions that are named after the fields of the `Post` model. + +### 3.3. Migrating the `Profile` type + +#### Type definition with the previous `nexus-prisma` package + +In the sample app, the `Profile` type is defined as follows: + +```ts +const Profile = prismaObjectType({ + name: 'Profile', + definition(t) { + t.prismaFields(['*']) + }, +}) +``` + +The asterisk in `prismaFields` means that _all_ Prisma fields are exposed. + +#### Type definition with the latest version of `@nexus/schema` and the `nexus-plugin-prisma` + +With the latest version of `@nexus/schema`, you need to expose all fields explicitly, there's no option to just expose everything from a Prisma model. + +Therefore, the new definition of `Profile` must explicitly list all its fields: + +```ts +const Profile = objectType({ + name: 'Profile', + definition(t) { + t.model.id() + t.model.bio() + t.model.user() + t.model.userId() + }, +}) +``` + +Note that `t.model` looks at the `name` attribute and matches it against the models in your Prisma schema. In this case, it's matched against the `Profile` model. Therefore, `t.model` exposes functions that are named after the fields of the `Profile` model. + +### 3.4. Migrating the `Category` type + +#### Type definition with the previous `nexus-prisma` package + +In the sample app, the `Category` type is defined as follows: + +```ts +const Category = prismaObjectType({ + name: 'Category', + definition(t) { + t.prismaFields(['*']) + }, +}) +``` + +The asterisk in `prismaFields` means that _all_ Prisma fields are exposed. + +#### Type definition with the latest version of `@nexus/schema` and the `nexus-plugin-prisma` + +With the latest version of `@nexus/schema`, you need to expose all fields explicitly, there's no option to just expose everything from a Prisma model. + +Therefore, the new definition of `Category` must explicitly list all its fields: + +```ts +const Category = objectType({ + name: 'Category', + definition(t) { + t.model.id() + t.model.name() + t.model.posts({ + pagination: true, + ordering: true, + filtering: true, + }) + }, +}) +``` + +Note that `t.model` looks at the `name` attribute and matches it against the models in your Prisma schema. In this case, it's matched against the `Category` model. Therefore, `t.model` exposes functions that are named after the fields of the `Category` model. + +## 4. Migrate GraphQL operations + +As a next step, you can start migrating all the GraphQL _queries_ and _mutations_ from the "previous" GraphQL API to the new one. + +For this guide, the following sample GraphQL operations will be used: + +```graphql +input UserUniqueInput { + id: String + email: String +} + +type Query { + posts(searchString: String): [Post!]! + user(userUniqueInput: UserUniqueInput!): User + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! +} + +type Mutation { + createUser(data: UserCreateInput!): User! + createDraft(title: String!, content: String, authorId: ID!): Post + updateBio(userUniqueInput: UserUniqueInput!, bio: String!): User + addPostToCategories(postId: String!, categoryIds: [String!]!): Post +} +``` + +### 4.1. Migrate GraphQL queries + +In this section, you'll migrate all GraphQL _queries_ from the previous version of `nexus` and `nexus-prisma` to the latest version of `@nexus/schema` and the `nexus-plugin-prisma`. + +#### 4.1.1. Migrate the `users` query + +In our sample API, the `users` query from the sample GraphQL schema is implemented as follows. + +```ts +const Query = prismaObjectType({ + name: 'Query', + definition(t) { + t.prismaFields(['users']) + }, +}) +``` + +To get the same behavior with the new Nexus, you need to call the `users` function on `t.crud`: + +```ts +schema.queryType({ + definition(t) { + t.crud.users({ + filtering: true, + ordering: true, + pagination: true, + }) + }, +}) +``` + +Recall that the `crud` property is added to `t` by the `nexus-plugin-prisma` (using the same mechanism as for `t.model`). + +#### 4.1.2. Migrate the `posts(searchString: String): [Post!]!` query + +In the sample API, the `posts` query is implemented as follows: + +```ts +queryType({ + definition(t) { + t.list.field('posts', { + type: 'Post', + args: { + searchString: stringArg({ nullable: true }), + }, + resolve: (parent, { searchString }, context) => { + return context.prisma.posts({ + where: { + OR: [ + { title_contains: searchString }, + { content_contains: searchString }, + ], + }, + }) + }, + }) + }, +}) +``` + +The only thing that needs to be updated for this query is the call to Prisma since the new Prisma Client API looks a bit different from the one used in Prisma 1. + +```ts line-number highlight=6,9,12,13;normal +queryType({ + definition(t) { + t.list.field('posts', { + type: 'Post', + args: { + searchString: stringArg({ nullable: true }), + }, + resolve: (parent, { searchString }, context) => { + return context.prisma.post.findMany({ + where: { + OR: [ + { title: { contains: searchString } }, + { content: { contains: searchString } }, + ], + }, + }) + }, + }) + }, +}) +``` + +Notice that the `db` object is automatically attached to the `context` by the `nexus-plugin-prisma`. It represents an instance of your `PrismaClient` which enables you to send queries to your database inside your resolvers. + +#### 4.1.3. Migrate the `user(uniqueInput: UserUniqueInput): User` query + +In the sample API, the `user` query is implemented as follows: + +```ts +inputObjectType({ + name: 'UserUniqueInput', + definition(t) { + t.string('id') + t.string('email') + }, +}) + +queryType({ + definition(t) { + t.field('user', { + type: 'User', + args: { + userUniqueInput: schema.arg({ + type: 'UserUniqueInput', + nullable: false, + }), + }, + resolve: (_, args, context) => { + return context.prisma.user({ + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email, + }) + }, + }) + }, +}) +``` + +You now need to adjust the call to your `prisma` instance since the new Prisma Client API looks a bit different from the one used in Prisma 1. + +```ts line-number highlight=6,12-17;normal +const Query = queryType({ + definition(t) { + t.field('user', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + }, + resolve: (_, args, context) => { + return context.prisma.user.findUnique({ + where: { + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email, + }, + }) + }, + }) + }, +}) +``` + +### 4.2. Migrate GraphQL mutations + +In this section, you'll migrate the GraphQL mutations from the sample schema to the latest versions of `@nexus/schema` and the `nexus-plugin-prisma`. + +#### 4.2.1. Migrate the `createUser` mutation + +In our sample API, the `createUser` mutation from the sample GraphQL schema is implemented as follows. + +```ts +const Mutation = prismaObjectType({ + name: 'Mutation', + definition(t) { + t.prismaFields(['createUser']) + }, +}) +``` + +To get the same behavior with the latest versions of `@nexus/schema` and the `nexus-plugin-prisma`, you need to call the `createOneUser` function on `t.crud` and pass an `alias` in order to rename the field in your GraphQL schema to `createUser` (otherwise it would be called `createOneUser`, after the function that's used): + +```ts +const Query = queryType({ + definition(t) { + t.crud.createOneUser({ + alias: 'createUser', + }) + }, +}) +``` + +Recall that the `crud` property is added to `t` by the `nexus-plugin-prisma` (using the same mechanism as for `t.model`). + +#### 4.2.2. Migrate the `createDraft(title: String!, content: String, authorId: String!): Post!` query + +In the sample app, the `createDraft` mutation implemented as follows. + +```ts line-number +mutationType({ + definition(t) { + t.field('createDraft', { + type: 'Post', + args: { + title: stringArg({ nullable: false }), + content: stringArg(), + authorId: stringArg({ nullable: false }), + }, + resolve: (_, args, context) => { + return context.prisma.createPost({ + title: args.title, + content: args.content, + author: { + connect: { id: args.authorId }, + }, + }) + }, + }) + }, +}) +``` + +You now need to adjust the call to your `prisma` instance since the new Prisma Client API looks a bit different from the one used in Prisma 1. + +```ts line-number highlight=11-19;normal +const Mutation = mutationType({ + definition(t) { + t.field('createDraft', { + type: 'Post', + args: { + title: stringArg({ nullable: false }), + content: stringArg(), + authorId: stringArg({ nullable: false }), + }, + resolve: (_, args, context) => { + return context.prisma.post.create({ + data: { + title: args.title, + content: args.content, + author: { + connect: { id: args.authorId }, + }, + }, + }) + }, + }) + }, +}) +``` + +#### 4.2.3. Migrate the `updateBio(bio: String, userUniqueInput: UserUniqueInput!): User` mutation + +In the sample API, the `updateBio` mutation is defined and implemented as follows. + +```ts +mutationType({ + definition(t) { + t.field('updateBio', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + bio: stringArg(), + }, + resolve: (_, args, context) => { + return context.prisma.updateUser({ + where: { + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email, + }, + data: { + profile: { + create: { bio: args.bio }, + }, + }, + }) + }, + }) + }, +}) +``` + +You now need to adjust the call to your `prisma` instance since the new Prisma Client API looks a bit different from the one used in Prisma 1. + +```ts highlight=13-23;normal +const Mutation = mutationType({ + definition(t) { + t.field('updateBio', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + bio: stringArg(), + }, + resolve: (_, args, context) => { + return context.prisma.user.update({ + where: { + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email, + }, + data: { + profile: { + create: { bio: args.bio }, + }, + }, + }) + }, + }) + }, +}) +``` + +#### 4.2.4. Migrate the `addPostToCategories(postId: String!, categoryIds: [String!]!): Post` mutation + +In the sample API, the `addPostToCategories` mutation is defined and implemented as follows. + +```ts line-number +mutationType({ + definition(t) { + t.field('addPostToCategories', { + type: 'Post', + args: { + postId: stringArg({ nullable: false }), + categoryIds: stringArg({ + list: true, + nullable: false, + }), + }, + resolve: (_, args, context) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.updatePost({ + where: { + id: args.postId, + }, + data: { + categories: { connect: ids }, + }, + }) + }, + }) + }, +}) +``` + +You now need to adjust the call to your `prisma` instance since the new Prisma Client API looks a bit different from the one used in Prisma 1. + +```ts line-number highlight=14-21;normal +const Mutation = mutationType({ + definition(t) { + t.field('addPostToCategories', { + type: 'Post', + args: { + postId: stringArg({ nullable: false }), + categoryIds: stringArg({ + list: true, + nullable: false, + }), + }, + resolve: (_, args, context) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.post.update({ + where: { + id: args.postId, + }, + data: { + categories: { connect: ids }, + }, + }) + }, + }) + }, +}) +``` + +## 5. Cleaning up + +### 5.1. Clean up npm dependencies + +If you haven't already, you can now uninstall dependencies that were related to the Prisma 1 setup: + +``` +npm uninstall prisma1 prisma-client-lib +``` + +### 5.2. Delete unused files + +Next, delete the files of your Prisma 1 setup: + +``` +rm -rf src/generated +rm -rf prisma1 +``` + +### 5.3. Stop the Prisma server + +Finally, you can stop running your Prisma server. diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/05-upgrading-prisma-binding-to-nexus.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/05-upgrading-prisma-binding-to-nexus.mdx new file mode 100644 index 0000000000..278facb1d3 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/05-upgrading-prisma-binding-to-nexus.mdx @@ -0,0 +1,1290 @@ +--- +title: 'prisma-binding to Nexus' +metaTitle: 'Upgrading from Prisma 1 with prisma-binding to Nexus' +metaDescription: 'Learn how to upgrade existing Prisma 1 projects with prisma-binding to Prisma 2.0 and Nexus.' +--- + +## Overview + +> **Note**: This guide is not fully up-to-date as it currently uses the [deprecated](https://github.com/graphql-nexus/nexus-plugin-prisma/issues/1039) version of the [`nexus-plugin-prisma`](https://github.com/graphql-nexus/nexus-plugin-prisma). While this is still functional, it is recommended to use the new [`nexus-prisma`](https://github.com/prisma/nexus-prisma/) library or an alternative code-first GraphQL library like [Pothos](https://pothos-graphql.dev/) going forward. If you have any questions, feel free to drop them in the [`#prisma1-community`](https://app.slack.com/client/T0MQBS8JG/C0152UA4DH9) channel in the [Prisma Slack](https://slack.prisma.io). + +This upgrade guide describes how to migrate a Node.js project that's based on [Prisma 1](https://github.com/prisma/prisma1) and uses `prisma-binding` to implement a GraphQL server. + +The code will be migrated to [`@nexus/schema`](https://github.com/graphql-nexus/schema) and the [`nexus-plugin-prisma`](https://github.com/graphql-nexus/nexus-plugin-prisma). As opposed to the _SDL-first_ approach that's used with `prisma-binding`, Nexus follows a code-first approach to construct GraphQL schemas. You can learn about the main differences of these two approaches in this [article](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3). If you want to continue using the SDL-first approach, you can follow the [guide](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-prisma-binding-to-sdl-first) to upgrade from `prisma-binding` to an SDL-first setup. + +This guide also explains how to migrate from JavaScript to TypeScript, it therefore basically assumes a **full rewrite** of your existing app. If you want to keep running your application in JavaScript, you can ignore the instructions that relate to the TypeScript setup keep using JavaScript as before. + +The guide assumes that you already went through the [guide for upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql). This means you already: + +- installed the Prisma 2.0 CLI +- created your Prisma 2.0 schema +- introspected your database and resolved potential [schema incompatibilities](/orm/more/upgrade-guides/upgrade-from-prisma-1/schema-incompatibilities-postgresql) +- installed and generated Prisma Client + +The guide further assumes that you have a file setup that looks similar to this: + +``` +. +├── README.md +├── package.json +├── prisma +│ └── schema.prisma +├── prisma1 +│ ├── datamodel.prisma +│ └── prisma.yml +└── src + ├── generated + │ └── prisma.graphql + ├── index.js + └── schema.graphql +``` + +The important parts are: + +- A folder called with `prisma` with your Prisma 2.0 schema +- A folder called `src` with your application code and a schema called `schema.graphql` + +If this is not what your project structure looks like, you'll need to adjust the instructions in the guide to match your own setup. + +## 1. Installing and configuring Nexus + +### 1.1. Install Nexus dependencies + +The first step is to install the Nexus dependency in your project: + +```terminal copy +npm install @nexus/schema +``` + +Next, install the the Prisma plugin for Nexus which will allow you to expose Prisma models in your GraphQL API: + +```terminal copy +npm install nexus-plugin-prisma +``` + +The `nexus-plugin-prisma` dependency bundles all required Prisma dependencies. You should therefore remove the dependencies that you installed when you upgraded the Prisma layer of your app: + +```terminal copy +npm uninstall @prisma/cli @prisma/client +``` + +Note however that you can still invoke the Prisma 2.0 CLI with the familiar command: + +```terminal +npx prisma +``` + +### 1.2. Configure TypeScript + +Since you'll be using TypeScript in this guide, you need to add the required dependencies: + +```terminal copy +npm install typescript ts-node-dev --save-dev +``` + +Create a new file named `tsconfig.json` in the root directory of your project: + +```terminal copy +touch tsconfig.json +``` + +Now add the following contents to the new file: + +```json copy file=tsconfig.json +{ + "compilerOptions": { + "skipLibCheck": true, + "strict": true, + "rootDir": "src", + "noEmit": true + }, + "include": ["src/**/*"] +} +``` + +### 1.3. Create your basic Nexus setup + +Create the root source file of your API called `index.ts` inside the `src` directory: + +```terminal copy +touch src/index.ts +``` + +Note that for this guide, you'll write the entire application inside of `index.ts`. In practice, you probably want to split your GraphQL types across different files as shown in this [example](https://github.com/prisma/prisma-examples/tree/latest/typescript/graphql-auth). + +For some basic setup, add this code to `index.ts`: + +```ts file=index.ts +import { queryType, makeSchema } from '@nexus/schema' +import { nexusSchemaPrisma } from 'nexus-plugin-prisma/schema' +import { GraphQLServer } from 'graphql-yoga' +import { createContext } from './context' + +const Query = queryType({ + definition(t) { + t.string('hello', () => { + return 'Hello Nexus!' + }) + }, +}) + +export const schema = makeSchema({ + types: [Query], + plugins: [nexusSchemaPrisma({ experimentalCRUD: true })], + outputs: { + schema: __dirname + '/../schema.graphql', + typegen: __dirname + '/generated/nexus.ts', + }, + typegenAutoConfig: { + contextType: 'Context.Context', + sources: [ + { + source: '@prisma/client', + alias: 'prisma', + }, + { + source: require.resolve('./context'), + alias: 'Context', + }, + ], + }, +}) + +new GraphQLServer({ schema, context: createContext() }).start(() => + console.log(`Server ready at: http://localhost:4000`) +) +``` + +Note that this setup already contains the configuration of the Prisma plugin for Nexus. This will enable the `t.model` and `t.crud` functionality that you'll get to know later in this guide. + +In the `typegenAutoConfig` setting, you're providing additional types that help your editor to provide your autocompletion as you develop your app. Right now it references a file named `context.ts` that you don't have in your project yet. This file will contain the type of your `context` object that's passed through your GraphQL resolver chain. + +Create the new `context.ts` file inside the `src` directory: + +```terminal copy +touch src/context.ts +``` + +Now add the following code to it: + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export interface Context { + prisma: PrismaClient +} + +export function createContext(): Context { + return { prisma } +} +``` + +Next, adjust the `scripts` section inside your `package.json` to include the following commands: + +```json +{ + "scripts": { + "start": "node dist/server", + "clean": "rm -rf dist", + "build": "npm -s run clean && npm -s run generate && tsc", + "generate": "npm -s run generate:prisma && npm -s run generate:nexus", + "generate:prisma": "prisma generate", + "generate:nexus": "ts-node --transpile-only src/schema", + "dev": "ts-node-dev --no-notify --respawn --transpile-only src" + } +} +``` + +The `dev` script starts a development server that you **always** should have running in the background when developing your app. This is important because of the code generation Nexus performs in the background. + +You can start the development server using the following command: + +```copy +npm run dev +``` + +You should see the following CLI output: + +```terminal +Server ready at: http://localhost:4000 +``` + +Your GraphQL server is now running at [http://localhost:4000](http://localhost:4000). So far it implements a single GraphQL query that you can send as follows: + +```graphql +{ + hello +} +``` + +In the following steps, we'll explain how you can migrate your existing SDL-first GraphQL schema that's implemented with `prisma-binding` to an equivalent setup using Nexus. + +## 2. Create your GraphQL types + +The next step of the upgrade process is to create your _GraphQL types_. In this case, your GraphQL types will mirror the Prisma models (as it likely was the case in your `prisma-binding` setup as well). If a GraphQL type deviates from a Prisma model, you'll be able to easily adjust the exposed GraphQL type accordingly using the Nexus API. + +For the purpose of this guide, you'll keep all the code in a single file. However, you can structure the files to your personal preference and `import` accordingly. + +In Nexus, GraphQL types are defined via the `objectType` function. Import `objectType` and then start with the skeleton for your first GraphQL type. In this case, we're starting by mapping Prisma's `User` model to GraphQL: + +```ts copy +import { objectType } from 'nexus' + +const User = objectType({ + name: 'User', + definition(t) { + // the fields of the type will be defined here + }, +}) +``` + +With this code in place, you can start exposing the _fields_ of the `User` model one by one. You can use your editor's autocompletion to save some typing. Inside the body of the `definition` function, type `t.model.` and then hit CTRL+SPACE. This will bring up the autocompletion and suggest all fields that are defined on the `User` model: + +![Exposing Prisma model fields with t.model](images/expose-prisma-model-fields-with-t-model.png) + +Note that the `model` property on `t` is provided by the `nexus-plugin-prisma`. It leverages the type information from your Prisma schema and lets you expose your Prisma models via GraphQL. + +In that manner, you can start completing your object type definition until you exposed all the fields of the model: + +```ts +const User = objectType({ + name: 'User', + definition(t) { + t.model.id() + t.model.email() + t.model.name() + t.model.jsonData() + t.model.role() + t.model.profile() + t.model.posts() + }, +}) +``` + +At this point, any _relation fields_ might give you TypeScript errors (in this case, that would be `profile` and `posts` which both point to other object types). That's expected, these errors will resolve automatically after you've added the remaining types. + +> **Note**: Be sure to have your Nexus development server that you started with `npm run dev` running all the time. It constantly updates the generated Nexus types that enable the autocompletion in the background as you save a file. + +Note that the `t.model.posts` relation exposes a _list_ of `Post` objects. By default, Nexus exposes only _pagination_ properties for that list – if you want to add _ordering_ and _filtering_ for that relation as well, you'll need to explicitly enable those: + +```ts line-number highlight=10-13;add +const User = objectType({ + name: 'User', + definition(t) { + t.model.id() + t.model.email() + t.model.name() + t.model.jsonData() + t.model.role() + t.model.profile() + t.model.posts({ + filtering: true, + ordering: true, + }) + }, +}) +``` + +After defining a type using the `objectType` function, you also need to manually add it to your GraphQL schema that you're building with Nexus. You can do it by adding it to the `types` which are provided as an option to the `makeSchema` function: + +```ts line-number +export const schema = makeSchema({ + types: [Query, User], + plugins: [nexusSchemaPrisma()], + outputs: { + schema: __dirname + '/../schema.graphql', + typegen: __dirname + '/generated/nexus.ts', + }, + typegenAutoConfig: { + sources: [ + { + source: '@prisma/client', + alias: 'prisma', + }, + ], + }, +}) +``` + +Once you're done with the first type, you can start defining the remaining ones. + +
+ +Expand to view the full version of the sample data model + +To expose all sample Prisma models with Nexus, the following code is needed: + +```ts +const User = objectType({ + name: 'User', + definition(t) { + t.model.id() + t.model.email() + t.model.name() + t.model.jsonData() + t.model.role() + t.model.profile() + t.model.posts({ + filtering: true, + ordering: true, + }) + }, +}) + +const Post = objectType({ + name: 'Post', + definition(t) { + t.model.id() + t.model.createdAt() + t.model.updatedAt() + t.model.title() + t.model.content() + t.model.published() + t.model.author() + t.model.authorId() + t.model.categories({ + filtering: true, + ordering: true, + }) + }, +}) + +const Profile = objectType({ + name: 'Profile', + definition(t) { + t.model.id() + t.model.bio() + t.model.userId() + t.model.user() + }, +}) + +const Category = objectType({ + name: 'Category', + definition(t) { + t.model.id() + t.model.name() + t.model.posts({ + filtering: true, + ordering: true, + }) + }, +}) +``` + +
+ +Be sure to include all newly defined types in the `types` option that's provided to `makeSchema`: + +```ts line-number highlight=2;normal +export const schema = makeSchema({ + types: [Query, User, Post, Profile, Category], + plugins: [nexusSchemaPrisma()], + outputs: { + schema: __dirname + '/../schema.graphql', + typegen: __dirname + '/generated/nexus.ts', + }, + typegenAutoConfig: { + sources: [ + { + source: '@prisma/client', + alias: 'prisma', + }, + ], + }, +}) +``` + +You can view the current version of your GraphQL schema in SDL in the generated GraphQL schema file in `./schema.graphql`. + +## 3. Migrate GraphQL operations + +As a next step, you can start migrating all the GraphQL _queries_ and _mutations_ from the "previous" GraphQL API to the new one that's built with Nexus. + +For this guide, the following sample GraphQL schema will be used: + +```graphql +# import Post from './generated/prisma.graphql' +# import User from './generated/prisma.graphql' +# import Category from './generated/prisma.graphql' + +input UserUniqueInput { + id: String + email: String +} + +type Query { + posts(searchString: String): [Post!]! + user(userUniqueInput: UserUniqueInput!): User + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! +} + +type Mutation { + createUser(data: UserCreateInput!): User! + createDraft(title: String!, content: String, authorId: ID!): Post + updateBio(userUniqueInput: UserUniqueInput!, bio: String!): User + addPostToCategories(postId: String!, categoryIds: [String!]!): Post +} +``` + +### 3.1. Migrate GraphQL queries + +In this section, you'll migrate all GraphQL _queries_ from `prisma-binding` to Nexus. + +#### 3.1.1. Migrate the `users` query (which uses `forwardTo`) + +In our sample API, the `users` query from the sample GraphQL schema is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + # ... other queries +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + users: forwardTo('prisma'), + // ... other resolvers + }, +} +``` + +To mirror the same behaviour with Nexus, you can use the `crud` property on the `t` variable inside the `definition` function. + +Similar to `model`, this property is available because you're using the `nexus-prisma-plugin` which leverages type information from your Prisma models and auto-generates resolvers under the hood. The `crud` property also supports autocompletion, so you can explore all available queries in your editor again: + +![Using t.crud to generate resolvers](images/use-t-crud-to-generate-resolvers.png) + +##### Forwarding the query with the `nexus-prisma-plugin` + +To add the `users` query to your GraphQL API, add the following lines to the query type definition: + +```ts line-number highlight=3-6;add +const Query = queryType({ + definition(t) { + t.crud.users({ + filtering: true, + ordering: true, + }) + }, +}) +``` + +If you have the Nexus development server running, you can save the file and your GraphQL API will be updated to expose the new `users` query. You can also observe this by looking at the `Query` type inside the generated `schema.graphql` file: + +```graphql +type Query { + users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable, skip: Int, where: UserWhereInput): [User!]! +} +``` + +You can now write your first query against the new API, e.g.: + +```graphql +{ + users { + id + name + profile { + id + bio + } + posts { + id + title + categories { + id + name + } + } + } +} +``` + +If your application exposes all CRUD operations from Prisma using `forwardTo`, you can now continue adding all remaining ones using the same approach via `t.crud`. To learn how "custom" queries can be defined and resolved using Nexus, move on to the next sections. + +#### 3.1.2. Migrate the `posts(searchString: String): [Post!]!` query + +The `posts` query is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + posts(searchString: String): [Post!]! + # ... other queries +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + posts: (_, args, context, info) => { + return context.prisma.query.posts( + { + where: { + OR: [ + { title_contains: args.searchString }, + { content_contains: args.searchString }, + ], + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Code-first schema definition with `nexus` + +To get the same behavior with Nexus, you'll need to add a `t.field` definition to the `queryType`: + +```ts line-number highlight=5-9;add +const Query = queryType({ + definition(t) { + // ... previous queries + + t.list.field('posts', { + type: 'Post', + nullable: false, + args: { searchString: stringArg() }, + }) + }, +}) +``` + +Although this code gives probably gives you a type error in your editor, you can already look at the generated SDL version of your GraphQL schema inside `schema.graphql`. You'll notice that this has added the correct _definition_ to your GraphQL schema already: + +```graphql line-number +type Query { +| posts(searchString: String): [Post!]! + users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable, skip: Int, where: UserWhereInput): [User!]! +} +``` + +However, the code is missing the actual resolver logic. This is what you're going to add next. + +##### Resolver implementation with `nexus` + +You can add the resolver with Nexus as follows: + +```ts line-number highlight=9-21;add +const Query = queryType({ + definition(t) { + // ... previous queries + + t.list.field('posts', { + type: 'Post', + nullable: false, + args: { searchString: stringArg() }, + resolve: (_, args, context) => { + return context.prisma.post.findMany({ + where: { + OR: [ + { + title: { contains: args.searchString }, + }, + { + content: { contains: args.searchString }, + }, + ], + }, + }) + }, + }) + }, +}) +``` + +To validate the implementation, you can now e.g. send the following example query to your GraphQL server: + +```graphql +{ + posts { + id + title + author { + id + name + } + } +} +``` + +#### 3.1.2. Migrate the `user(uniqueInput: UserUniqueInput): User` query + +In our sample app, the `user` query is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + user(userUniqueInput: UserUniqueInput): User + # ... other queries +} + +input UserUniqueInput { + id: String + email: String +} +``` + +Note that this is a bit of a contrived example to demonstrate the usage of `input` types with Nexus. + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + user: (_, args, context, info) => { + return context.prisma.query.user( + { + where: args.userUniqueInput, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Code-first schema definition with `nexus` + +To get the same behavior with Nexus, you'll need to add a `t.field` definition to the `queryType` and define an `inputObjectType` that includes the two `@unique` fields of your `User` model: + +```ts line-number highlight=1,3-9,15-23;add +import { inputObjectType, arg } from '@nexus/schema' + +const UserUniqueInput = inputObjectType({ + name: 'UserUniqueInput', + definition(t) { + t.string('id') + t.string('email') + }, +}) + +const Query = queryType({ + definition(t) { + // ... previous queries + + t.field('user', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + }, + }) + }, +}) +``` + +Since `UserUniqueInput` is a new type in your GraphQL schema, you again need to add it to the `types` option that's passed to `makeSchema`: + +```ts line-number highlight=2;normal +export const schema = makeSchema({ + types: [Query, User, Post, Profile, Category, UserUniqueInput], + plugins: [nexusSchemaPrisma()], + outputs: { + schema: __dirname + '/../schema.graphql', + typegen: __dirname + '/generated/nexus.ts', + }, + typegenAutoConfig: { + sources: [ + { + source: '@prisma/client', + alias: 'prisma', + }, + ], + }, +}) +``` + +If you look at the generated SDL version of your GraphQL schema inside `schema.graphql`, you'll notice that this change already added the correct _definition_ to your GraphQL schema: + +```graphql line-number highlight=3,7-10;normal +type Query { + posts(searchString: String): [Post!] + user(userUniqueInput: UserUniqueInput!): User + users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable, skip: Int, where: UserWhereInput): [User!]! +} + +input UserUniqueInput { + email: String + id: String +} +``` + +You can even send the respective query via the GraphQL Playground already: + +```graphql +{ + user(userUniqueInput: { email: "alice@prisma.io" }) { + id + name + } +} +``` + +However, because the resolver is not yet implemented you will not get any data back yet. + +##### Code-first resolver implementation with `nexus` + +That's because you're still missing the _resolver_ implementation for that query. You can add the resolver with Nexus as follows: + +```ts line-number highlight=22-29;add +const UserUniqueInput = inputObjectType({ + name: 'UserUniqueInput', + definition(t) { + t.string('id') + t.string('email') + }, +}) + +const Query = queryType({ + definition(t) { + // ... previous queries + + t.field('user', { + type: 'User', + nullable: true, + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + }, + resolve: (_, args, context) => { + return context.prisma.user.findUnique({ + where: { + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email, + }, + }) + }, + }) + }, +}) +``` + +If you're re-sending the same query from before, you'll find that it now returns actual data. + +### 3.2. Migrate GraphQL mutations + +In this section, you'll migrate the GraphQL mutations from the sample schema to the Nexus. + +#### 3.2.1. Define the `Mutation` type + +The first step to migrate any mutations is to define the `Mutation` type of your GraphQL API. Once that's done, you can gradually add operations to it. Add the following definition to `index.ts`: + +```ts +import { mutationType } from '@nexus/schema' + +const Mutation = mutationType({ + definition(t) { + // your GraphQL mutations + resolvers will be defined here + }, +}) +``` + +In order to make sure that the new `Mutation` type is picked by up Nexus, you need to add it to the `types` that are provided to `makeSchema`: + +```ts line-number highlight=2;normal +export const schema = makeSchema({ + types: [Query, User, Post, Profile, Category, UserUniqueInput, Mutation], + plugins: [nexusSchemaPrisma()], + outputs: { + schema: __dirname + '/../schema.graphql', + typegen: __dirname + '/generated/nexus.ts', + }, + typegenAutoConfig: { + sources: [ + { + source: '@prisma/client', + alias: 'prisma', + }, + ], + }, +}) +``` + +#### 3.2.2. Migrate the `createUser` mutation (which uses `forwardTo`) + +In the sample app, the `createUser` mutation from the sample GraphQL schema is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + createUser(data: UserCreateInput!): User! + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + createUser: forwardTo('prisma'), + // ... other resolvers + }, +} +``` + +Similar to forwarding GraphQL queries, you can use the `crud` property on the `t` variable inside the `definition` function in order to expose full CRUD capabilities for Prisma models. + +Similar to `model`, this property is available because you're using the `nexus-prisma-plugin` which leverages type information from your Prisma models and auto-generates resolvers under the hood. The `crud` property supports autocompletion when defining mutations as well, so you can explore all available operations in your editor again: + +![Generating resolvers with t.crud](images/regenerate-resolvers-with-t-crud.png) + +##### Forwarding the mutation with the `nexus-prisma-plugin` + +To add the `createUser` mutation to your GraphQL API, add the following lines to the query type definition: + +```ts line-number highlight=3-5;add +const Mutation = mutationType({ + definition(t) { + t.crud.createOneUser({ + alias: 'createUser', + }) + }, +}) +``` + +Note that the default name for the mutation in your GraphQL schema is `createOneUser` (named after the function which is exposed by `t.crud`). In order to rename it to `createUser`, you need to provide the `alias` property. + +If you have the Nexus development server running, you can save the file and your GraphQL API will be updated to expose the new `createUser` mutation. You can also observe this by looking at the `Mutation` type inside the generated `schema.graphql` file: + +```graphql +type Mutation { + createUser(data: UserCreateInput!): User! +} +``` + +You can now write your first mutation against the new API, e.g.: + +```graphql +mutation { + createUser(data: { name: "Alice", email: "alice@prisma.io" }) { + id + } +} +``` + +If your application exposes all CRUD operations from Prisma using `forwardTo`, you can now continue adding all remaining ones using the same approach via `t.crud`. To learn how "custom" mutations can be defined and resolved using Nexus, move on to the next sections. + +#### 3.2.3. Migrate the `createDraft(title: String!, content: String, authorId: String!): Post!` query + +In the sample app, the `createDraft` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + createDraft(title: String!, content: String, authorId: String!): Post! + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + createDraft: (_, args, context, info) => { + return context.prisma.mutation.createPost( + { + data: { + title: args.title, + content: args.content, + author: { + connect: { + id: args.authorId, + }, + }, + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Code-first schema definition with `nexus` + +To get the same behavior with Nexus, you'll need to add a `t.field` definition to the `mutationType`: + +```ts line-number highlight=5-12;add +const Mutation = mutationType({ + definition(t) { + // ... previous mutations + + t.field('createDraft', { + type: 'Post', + args: { + title: stringArg({ nullable: false }), + content: stringArg(), + authorId: stringArg({ nullable: false }), + }, + }) + }, +}) +``` + +If you look at the generated SDL version of your GraphQL schema inside `schema.graphql`, you'll notice that this has added the correct _definition_ to your GraphQL schema already: + +```graphql line-number highlight=3;normal +type Mutation { + createUser(data: UserCreateInput!): User! + createDraft(title: String!, content: String, authorId: String!): Post! +} +``` + +You can even send the respective mutation via the GraphQL Playground already: + +```graphql +mutation { + createDraft(title: "Hello World", authorId: "__AUTHOR_ID__") { + id + published + author { + id + name + } + } +} +``` + +However, because the resolver is not yet implemented, no new `Post` record will be created and you will not get any data back in the response. + +##### Resolver implementation with `nexus` + +That's because you're still missing the _resolver_ implementation for that mutation. You can add the resolver with Nexus as follows: + +```ts line-number highlight=12-22;add +const Mutation = mutationType({ + definition(t) { + // ... previous mutations + + t.field('createDraft', { + type: 'Post', + args: { + title: stringArg({ nullable: false }), + content: stringArg(), + authorId: stringArg({ nullable: false }), + }, + resolve: (_, args, context) => { + return context.prisma.post.create({ + data: { + title: args.title, + content: args.content, + author: { + connect: { id: args.authorId }, + }, + }, + }) + }, + }) + }, +}) +``` + +If you're re-sending the same query from before, you'll find that it now create a new `Post` record and return valid data. + +#### 3.2.4. Migrate the `updateBio(bio: String, userUniqueInput: UserUniqueInput!): User` mutation + +In the sample app, the `updateBio` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + updateBio: (_, args, context, info) => { + return context.prisma.mutation.updateUser( + { + data: { + profile: { + update: { bio: args.bio }, + }, + }, + where: { id: args.userId }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Code-first schema definition with `nexus` + +To get the same behavior with Nexus, you'll need to add a `t.field` definition to the `mutationType`: + +```ts line-number highlight=5-14;add +const Mutation = mutationType({ + definition(t) { + // ... previous mutations + + t.field('updateBio', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false, + }), + bio: stringArg({ nullable: false }), + }, + }) + }, +}) +``` + +If you look at the generated SDL version of your GraphQL schema inside `schema.graphql`, you'll notice that this has added the correct _definition_ to your GraphQL schema already: + +```graphql line-number highlight=4;normal +type Mutation { + createUser(data: UserCreateInput!): User! + createDraft(title: String!, content: String, authorId: String!): Post! + updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User +} +``` + +You can even send the respective mutation via the GraphQL Playground already: + +```graphql +mutation { + updateBio( + userUniqueInput: { email: "alice@prisma.io" } + bio: "I like turtles" + ) { + id + name + profile { + id + bio + } + } +} +``` + +However, because the resolver is not yet implemented, nothing will be updated in the database and you will not get any data back in the response. + +##### Resolver implementation with `nexus` + +That's because you're still missing the _resolver_ implementation for that query. You can add the resolver with Nexus as follows: + +```ts line-number highlight=14-26;add +const Mutation = mutationType({ + definition(t) { + // ... previous mutations + + t.field('updateBio', { + type: 'User', + args: { + userUniqueInput: arg({ + type: 'UserUniqueInput', + nullable: false + }), + bio: stringArg() + }, + resolve: (_, args, context) => { + return context.prisma.user.update({ + where: { + id: args.userUniqueInput?.id, + email: args.userUniqueInput?.email + }, + data: { + profile: { + create: { bio: args.bio } + } + } + }) + } + } + } +}) +``` + +If you're re-sending the same query from before, you'll find that it now returns actual data instead of `null`. + +#### 3.2.5. Migrate the `addPostToCategories(postId: String!, categoryIds: [String!]!): Post` mutation + +In our sample app, the `addPostToCategories` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + addPostToCategories(postId: String!, categoryIds: [String!]!): Post + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + addPostToCategories: (_, args, context, info) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.mutation.updatePost( + { + data: { + categories: { + connect: ids, + }, + }, + where: { + id: args.postId, + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Code-first schema definition with `nexus` + +To get the same behavior with Nexus, you'll need to add a `t.field` definition to the `mutationType`: + +```ts line-number highlight=5-14;add +const Mutation = mutationType({ + definition(t) { + // ... mutations from before + + t.field('addPostToCategories', { + type: 'Post', + args: { + postId: stringArg({ nullable: false }), + categoryIds: stringArg({ + list: true, + nullable: false, + }), + }, + }) + }, +}) +``` + +If you look at the generated SDL version of your GraphQL schema inside `schema.graphql`, you'll notice that this has added the correct _definition_ to your GraphQL schema already: + +```graphql line-number highlight=5;normal +type Mutation { + createUser(data: UserCreateInput!): User! + createDraft(title: String!, content: String, authorId: String!): Post! + updateBio(bio: String, userUniqueInput: UserUniqueInput!): User + addPostToCategories(postId: String!, categoryIds: [String!]!): Post +} +``` + +You can even send the respective query via the GraphQL Playground already: + +```graphql +mutation { + addPostToCategories( + postId: "__AUTHOR_ID__" + categoryIds: ["__CATEGORY_ID_1__", "__CATEGORY_ID_2__"] + ) { + id + title + categories { + id + name + } + } +} +``` + +However, because the resolver is not yet implemented, nothing will be updated in the database and you will not get any data back in the response. + +##### Resolver implementation with `nexus` + +That's because you're still missing the _resolver_ implementation for that query. You can add the resolver with Nexus as follows: + +```ts line-number highlight=13-23;add +const Mutation = mutationType({ + definition(t) { + // ... mutations from before + t.field('addPostToCategories', { + type: 'Post', + args: { + postId: stringArg({ nullable: false }), + categoryIds: stringArg({ + list: true, + nullable: false, + }), + }, + resolve: (_, args, context) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.post.update({ + where: { + id: args.postId, + }, + data: { + categories: { connect: ids }, + }, + }) + }, + }) + }, +}) +``` + +If you're re-sending the same query from before, you'll find that it now returns actual data instead of `null`. + +## 4. Cleaning up + +Since the entire app has now been upgrade to Prisma 2.0 and Nexus, you can delete all unnecessary files and remove the no longer needed dependencies. + +### 4.1. Clean up npm dependencies + +You can start by removing npm dependencies that were related to the Prisma 1 setup: + +```copy +npm uninstall graphql-cli prisma-binding prisma1 +``` + +### 4.2. Delete unused files + +Next, delete the files of your Prisma 1 setup: + +```copy +rm prisma1/datamodel.prisma prisma1/prisma.yml +``` + +You can also delete any remaining `.js` files, the old `schema.graphql` and `prisma.graphql` files. + +### 4.3. Stop the Prisma server + +Finally, you can stop running your Prisma server. diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/06-upgrading-prisma-binding-to-sdl-first.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/06-upgrading-prisma-binding-to-sdl-first.mdx new file mode 100644 index 0000000000..63f4fe102f --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/06-upgrading-prisma-binding-to-sdl-first.mdx @@ -0,0 +1,1669 @@ +--- +title: 'prisma-binding to SDL-first' +metaTitle: 'Upgrading from Prisma 1 with prisma-binding to SDL-first' +metaDescription: 'Learn how to upgrade existing Prisma 1 projects with prisma-binding to Prisma 2 (SDL-first).' +--- + +## Overview + +This upgrade guide describes how to migrate a Node.js project that's based on [Prisma 1](https://github.com/prisma/prisma1) and uses `prisma-binding` to implement a GraphQL server. + +The code will keep the [SDL-first approach](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3) for constructing the GraphQL schema. When migrating from `prisma-binding` to Prisma Client, the main difference is that the `info` object can't be used to resolve relations automatically any more, instead you'll need to implement your _type resolvers_ to ensure that relations get resolved properly. + +The guide assumes that you already went through the [guide for upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql). This means you already: + +- installed the Prisma 2 CLI +- created your Prisma 2 schema +- introspected your database and resolved potential schema incompatibilities +- installed and generated Prisma Client + +The guide further assumes that you have a file setup that looks similar to this: + +``` +. +├── README.md +├── package.json +├── prisma +│ └── schema.prisma +├── prisma1 +│ ├── datamodel.prisma +│ └── prisma.yml +└── src + ├── generated + │ └── prisma.graphql + ├── index.js + └── schema.graphql +``` + +The important parts are: + +- A folder called with `prisma` with your Prisma 2 schema +- A folder called `src` with your application code and a schema called `schema.graphql` + +If this is not what your project structure looks like, you'll need to adjust the instructions in the guide to match your own setup. + +## 1. Adjusting your GraphQL schema + +With `prisma-binding`, your approach for defining your GraphQL schema (sometimes called [application schema](https://v1.prisma.io/docs/1.20/data-model-and-migrations/data-model-knul/#a-note-on-the-application-schema)) is based on _importing_ GraphQL types from the generated `prisma.graphql` file (in Prisma 1, this is typically called [Prisma GraphQL schema](https://v1.prisma.io/docs/1.20/data-model-and-migrations/data-model-knul/#the-prisma-graphql-schema)). These types mirror the types from your Prisma 1 datamodel and serve as foundation for your GraphQL API. + +With Prisma 2, there's no `prisma.graphql` file any more that you could import from. Therefore, you have to spell out all the types of your GraphQL schema directly inside your `schema.graphql` file. + +The easiest way to do so is by downloading the full GraphQL schema from the GraphQL Playground. To do so, open the **SCHEMA** tab and click the **DOWNLOAD** button in the top-right corner, then select **SDL**: + +![Downloading the GraphQL schema with GraphQL Playground](images/download-graphql-schema.png) + +Alternatively, you can use the `get-schema` command of the [GraphQL CLI](https://github.com/Urigo/graphql-cli) to download your full schema: + +``` +npx graphql get-schema --endpoint __GRAPHQL_YOGA_ENDPOINT__ --output schema.graphql --no-all +``` + +> **Note**: With the above command, you need to replace the `__GRAPHQL_YOGA_ENDPOINT__` placeholder with the actual endpoint of your GraphQL Yoga server. + +Once you obtained the `schema.graphql` file, replace your current version in `src/schema.graphql` with the new contents. Note that the two schemas are 100% equivalent, except that the new one doesn't use [`graphql-import`](https://github.com/ardatan/graphql-import) for importing types from a different file. Instead, it spells out all types in a single file. + +Here's a comparison of these two versions of the sample GraphQL schema that we'll migrate in this guide (you can use the tabs to switch between the two versions): + + + + +```graphql +# import Post from './generated/prisma.graphql' +# import User from './generated/prisma.graphql' +# import Category from './generated/prisma.graphql' + +type Query { + posts(searchString: String): [Post!]! + user(userUniqueInput: UserUniqueInput!): User + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + allCategories: [Category!]! +} + +input UserUniqueInput { + id: String + email: String +} + +type Mutation { + createDraft(authorId: ID!, title: String!, content: String!): Post + publish(id: ID!): Post + deletePost(id: ID!): Post + signup(name: String!, email: String!): User! + updateBio(userId: String!, bio: String!): User + addPostToCategories(postId: String!, categoryIds: [String!]!): Post +} +``` + + + + +```graphql +type Query { + posts(searchString: String): [Post!]! + user(id: ID!): User + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + allCategories: [Category!]! +} + +type Category implements Node { + id: ID! + name: String! + posts(where: PostWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [Post!] +} + +input CategoryCreateManyWithoutPostsInput { + create: [CategoryCreateWithoutPostsInput!] + connect: [CategoryWhereUniqueInput!] +} + +input CategoryCreateWithoutPostsInput { + id: ID + name: String! +} + +enum CategoryOrderByInput { + id_ASC + id_DESC + name_ASC + name_DESC +} + +input CategoryWhereInput { + """Logical AND on all given filters.""" + AND: [CategoryWhereInput!] + + """Logical OR on all given filters.""" + OR: [CategoryWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [CategoryWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + posts_every: PostWhereInput + posts_some: PostWhereInput + posts_none: PostWhereInput +} + +input CategoryWhereUniqueInput { + id: ID +} + +scalar DateTime + +"""Raw JSON value""" +scalar Json + +"""An object with an ID""" +interface Node { + """The id of the object.""" + id: ID! +} + +type Post implements Node { + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + title: String! + content: String + published: Boolean! + author: User + categories(where: CategoryWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [Category!] +} + +input PostCreateManyWithoutAuthorInput { + create: [PostCreateWithoutAuthorInput!] + connect: [PostWhereUniqueInput!] +} + +input PostCreateWithoutAuthorInput { + id: ID + title: String! + content: String + published: Boolean + categories: CategoryCreateManyWithoutPostsInput +} + +enum PostOrderByInput { + id_ASC + id_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + title_ASC + title_DESC + content_ASC + content_DESC + published_ASC + published_DESC +} + +input PostWhereInput { + """Logical AND on all given filters.""" + AND: [PostWhereInput!] + + """Logical OR on all given filters.""" + OR: [PostWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [PostWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + title: String + + """All values that are not equal to given value.""" + title_not: String + + """All values that are contained in given list.""" + title_in: [String!] + + """All values that are not contained in given list.""" + title_not_in: [String!] + + """All values less than the given value.""" + title_lt: String + + """All values less than or equal the given value.""" + title_lte: String + + """All values greater than the given value.""" + title_gt: String + + """All values greater than or equal the given value.""" + title_gte: String + + """All values containing the given string.""" + title_contains: String + + """All values not containing the given string.""" + title_not_contains: String + + """All values starting with the given string.""" + title_starts_with: String + + """All values not starting with the given string.""" + title_not_starts_with: String + + """All values ending with the given string.""" + title_ends_with: String + + """All values not ending with the given string.""" + title_not_ends_with: String + content: String + + """All values that are not equal to given value.""" + content_not: String + + """All values that are contained in given list.""" + content_in: [String!] + + """All values that are not contained in given list.""" + content_not_in: [String!] + + """All values less than the given value.""" + content_lt: String + + """All values less than or equal the given value.""" + content_lte: String + + """All values greater than the given value.""" + content_gt: String + + """All values greater than or equal the given value.""" + content_gte: String + + """All values containing the given string.""" + content_contains: String + + """All values not containing the given string.""" + content_not_contains: String + + """All values starting with the given string.""" + content_starts_with: String + + """All values not starting with the given string.""" + content_not_starts_with: String + + """All values ending with the given string.""" + content_ends_with: String + + """All values not ending with the given string.""" + content_not_ends_with: String + published: Boolean + + """All values that are not equal to given value.""" + published_not: Boolean + author: UserWhereInput + categories_every: CategoryWhereInput + categories_some: CategoryWhereInput + categories_none: CategoryWhereInput +} + +input PostWhereUniqueInput { + id: ID +} + +type Profile implements Node { + id: ID! + bio: String + user: User! +} + +input ProfileCreateOneWithoutUserInput { + create: ProfileCreateWithoutUserInput + connect: ProfileWhereUniqueInput +} + +input ProfileCreateWithoutUserInput { + id: ID + bio: String +} + +input ProfileWhereInput { + """Logical AND on all given filters.""" + AND: [ProfileWhereInput!] + + """Logical OR on all given filters.""" + OR: [ProfileWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [ProfileWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + bio: String + + """All values that are not equal to given value.""" + bio_not: String + + """All values that are contained in given list.""" + bio_in: [String!] + + """All values that are not contained in given list.""" + bio_not_in: [String!] + + """All values less than the given value.""" + bio_lt: String + + """All values less than or equal the given value.""" + bio_lte: String + + """All values greater than the given value.""" + bio_gt: String + + """All values greater than or equal the given value.""" + bio_gte: String + + """All values containing the given string.""" + bio_contains: String + + """All values not containing the given string.""" + bio_not_contains: String + + """All values starting with the given string.""" + bio_starts_with: String + + """All values not starting with the given string.""" + bio_not_starts_with: String + + """All values ending with the given string.""" + bio_ends_with: String + + """All values not ending with the given string.""" + bio_not_ends_with: String + user: UserWhereInput +} + +input ProfileWhereUniqueInput { + id: ID +} + +enum Role { + ADMIN + CUSTOMER +} + +type User implements Node { + id: ID! + email: String + name: String! + posts(where: PostWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [Post!] + role: Role! + profile: Profile + jsonData: Json +} + +input UserCreateInput { + id: ID + email: String + name: String! + role: Role + jsonData: Json + posts: PostCreateManyWithoutAuthorInput + profile: ProfileCreateOneWithoutUserInput +} + +enum UserOrderByInput { + id_ASC + id_DESC + email_ASC + email_DESC + name_ASC + name_DESC + role_ASC + role_DESC + jsonData_ASC + jsonData_DESC +} + +input UserWhereInput { + """Logical AND on all given filters.""" + AND: [UserWhereInput!] + + """Logical OR on all given filters.""" + OR: [UserWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [UserWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + email: String + + """All values that are not equal to given value.""" + email_not: String + + """All values that are contained in given list.""" + email_in: [String!] + + """All values that are not contained in given list.""" + email_not_in: [String!] + + """All values less than the given value.""" + email_lt: String + + """All values less than or equal the given value.""" + email_lte: String + + """All values greater than the given value.""" + email_gt: String + + """All values greater than or equal the given value.""" + email_gte: String + + """All values containing the given string.""" + email_contains: String + + """All values not containing the given string.""" + email_not_contains: String + + """All values starting with the given string.""" + email_starts_with: String + + """All values not starting with the given string.""" + email_not_starts_with: String + + """All values ending with the given string.""" + email_ends_with: String + + """All values not ending with the given string.""" + email_not_ends_with: String + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + role: Role + + """All values that are not equal to given value.""" + role_not: Role + + """All values that are contained in given list.""" + role_in: [Role!] + + """All values that are not contained in given list.""" + role_not_in: [Role!] + posts_every: PostWhereInput + posts_some: PostWhereInput + posts_none: PostWhereInput + profile: ProfileWhereInput +} +``` + + + + +You'll notice that the new version of your GraphQL schema not only defines the _models_ that were imported directly, but also additional types (e.g. `input` types) that were not present in the schema before. + +## 2. Set up your `PrismaClient` instance + +`PrismaClient` is your new interface to the database in Prisma 2. It lets you invoke various methods which build SQL queries and send them to the database, returning the results as plain JavaScript objects. + +The `PrismaClient` query API is inspired by the initial `prisma-binding` API, so a lot of the queries you send with Prisma Client will feel familiar. + +Similar to the `prisma-binding` instance from Prisma 1, you also want to attach your `PrismaClient` from Prisma 2 to GraphQL's `context` so that in can be accessed inside your resolvers: + +```js line-number highlight=10-13;delete|14;add +const { PrismaClient } = require('@prisma/client') + +// ... + +const server = new GraphQLServer({ + typeDefs: 'src/schema.graphql', + resolvers, + context: (req) => ({ + ...req, + prisma: new Prisma({ + typeDefs: 'src/generated/prisma.graphql', + endpoint: 'http://localhost:4466', + }), + prisma: new PrismaClient(), + }), +}) +``` + +In the code block above, the _red_ lines are the lines to be removed from your current setup, the _green_ lines are the ones that you should add. Of course, it's possible that your previous setup differed from this one (e.g. it's unlikely that your Prisma `endpoint` was `http://localhost:4466` if you're running your API in production), this is just a sample to indicate what it _could_ look like. + +When you're now accessing `context.prisma` inside of a resolver, you now have access to Prisma Client queries. + +## 2. Write your GraphQL type resolvers + +`prisma-binding` was able to _magically_ resolve relations in your GraphQL schema. When not using `prisma-binding` though, you need to explicitly resolve your relations using so-called _type resolvers_. + +> **Note** You can learn more about the concept of type resolvers and why they're necessary in this article: [GraphQL Server Basics: GraphQL Schemas, TypeDefs & Resolvers Explained](https://www.prisma.io/blog/graphql-server-basics-the-schema-ac5e2950214e) + +### 2.1. Implementing the type resolver for the `User` type + +The `User` type in our sample GraphQL schema is defined as follows: + +```graphql +type User implements Node { + id: ID! + email: String + name: String! + posts( + where: PostWhereInput + orderBy: Enumerable + skip: Int + after: String + before: String + first: Int + last: Int + ): [Post!] + role: Role! + profile: Profile + jsonData: Json +} +``` + +This type has two relations: + +- The `posts` field denotes a 1-n relation to `Post` +- The `profile` field denotes a 1-1 relation to `Profile` + +Since you're not using `prisma-binding` any more, you now need to resolve these relations "manually" in type resolvers. + +You can do so by adding a `User` field to your _resolver map_ and implement the resolvers for the `posts` and `profile` relations as follows: + +```js line-number highlight=8-23;add +const resolvers = { + Query: { + // ... your query resolvers + }, + Mutation: { + // ... your mutation resolvers + }, + User: { + posts: (parent, args, context) => { + return context.prisma.user + .findUnique({ + where: { id: parent.id }, + }) + .posts() + }, + profile: (parent, args, context) => { + return context.prisma.user + .findUnique({ + where: { id: parent.id }, + }) + .profile() + }, + }, +} +``` + +Inside of these resolvers, you're using your new `PrismaClient` to perform a query against the database. Inside the `posts` resolver, the database query loads all `Post` records from the specified `author` (whose `id` is carried in the `parent` object). Inside the `profile` resolver, the database query loads the `Profile` record from the specified `user` (whose `id` is carried in the `parent` object). + +Thanks to these extra resolvers, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the `User` type in a query, e.g.: + +```graphql +{ + users { + id + name + posts { + # fetching this relation is enabled by the new type resolver + id + title + } + profile { + # fetching this relation is enabled by the new type resolver + id + bio + } + } +} +``` + +### 2.2. Implementing the type resolver for the `Post` type + +The `Post` type in our sample GraphQL schema is defined as follows: + +```graphql +type Post implements Node { + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + title: String! + content: String + published: Boolean! + author: User + categories( + where: CategoryWhereInput + orderBy: Enumerable + skip: Int + after: String + before: String + first: Int + last: Int + ): [Category!] +} +``` + +This type has two relations: + +- The `author` field denotes a 1-n relation to `User` +- The `categories` field denotes a m-n relation to `Category` + +Since you're not using `prisma-binding` any more, you now need to resolve these relations "manually" in type resolvers. + +You can do so by adding a `Post` field to your _resolver map_ and implement the resolvers for the `author` and `categories` relations as follows: + +```js line-number highlight=11-26;add +const resolvers = { + Query: { + // ... your query resolvers + }, + Mutation: { + // ... your mutation resolvers + }, + User: { + // ... your type resolvers for `User` from before + }, + Post: { + author: (parent, args, context) => { + return context.prisma.post + .findUnique({ + where: { id: parent.id }, + }) + .author() + }, + categories: (parent, args, context) => { + return context.prisma.post + .findUnique({ + where: { id: parent.id }, + }) + .categories() + }, + }, +} +``` + +Inside of these resolvers, you're using your new `PrismaClient` to perform a query against the database. Inside the `author` resolver, the database query loads the `User` record that represents the `author` of the `Post`. Inside the `categories` resolver, the database query loads all `Category` records from the specified `post` (whose `id` is carried in the `parent` object). + +Thanks to these extra resolvers, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the `User` type in a query, e.g.: + +```graphql +{ + posts { + id + title + author { + # fetching this relation is enabled by the new type resolver + id + name + } + categories { + # fetching this relation is enabled by the new type resolver + id + name + } + } +} +``` + +### 2.3. Implementing the type resolver for the `Profile` type + +The `Profile` type in our sample GraphQL schema is defined as follows: + +```graphql +type Profile implements Node { + id: ID! + bio: String + user: User! +} +``` + +This type has one relation: The `user` field denotes a 1-n relation to `User`. + +Since you're not using `prisma-binding` any more, you now need to resolve this relation "manually" in type resolvers. + +You can do so by adding a `Profile` field to your _resolver map_ and implement the resolvers for the `owner` relation as follows: + +```js line-number highlight=14-22;add +const resolvers = { + Query: { + // ... your query resolvers + }, + Mutation: { + // ... your mutation resolvers + }, + User: { + // ... your type resolvers for `User` from before + }, + Post: { + // ... your type resolvers for `Post` from before + }, + Profile: { + user: (parent, args, context) => { + return context.prisma.profile + .findUnique({ + where: { id: parent.id }, + }) + .owner() + }, + }, +} +``` + +Inside of this resolver, you're using your new `PrismaClient` to perform a query against the database. Inside the `user` resolver, the database query loads the `User` records from the specified `profile` (whose `id` is carried in the `parent` object). + +Thanks to this extra resolver, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the `Profile` type in a query. + +### 2.4. Implementing the type resolver for the `Category` type + +The `Category` type in our sample GraphQL schema is defined as follows: + +```graphql +type Category implements Node { + id: ID! + name: String! + posts( + where: PostWhereInput + orderBy: Enumerable + skip: Int + after: String + before: String + first: Int + last: Int + ): [Post!] +} +``` + +This type has one relation: The `posts` field denotes a m-n relation to `Post`. + +Since you're not using `prisma-binding` any more, you now need to resolve this relation "manually" in type resolvers. + +You can do so by adding a `Category` field to your _resolver map_ and implement the resolvers for the `posts` and `profile` relations as follows: + +```js line-number highlight=17-25;add +const resolvers = { + Query: { + // ... your query resolvers + }, + Mutation: { + // ... your mutation resolvers + }, + User: { + // ... your type resolvers for `User` from before + }, + Post: { + // ... your type resolvers for `Post` from before + }, + Profile: { + // ... your type resolvers for `User` from before + }, + Category: { + posts: (parent, args, context) => { + return context.prisma + .findUnique({ + where: { id: parent.id }, + }) + .posts() + }, + }, +} +``` + +Inside of this resolver, you're using your new `PrismaClient` to perform a query against the database. Inside the `posts` resolver, the database query loads all `Post` records from the specified `categories` (whose `id` is carried in the `parent` object). + +Thanks to this extra resolver, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about a `Category` type in a query. + +With all your type resolvers in place, you can start migrating the actual GraphQL API operations. + +## 3. Migrate GraphQL operations + +### 3.1. Migrate GraphQL queries + +In this section, you'll migrate all GraphQL _queries_ from `prisma-binding` to Prisma Client. + +#### 3.1.1. Migrate the `users` query (which uses `forwardTo`) + +In our sample API, the `users` query from the sample GraphQL schema is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + users(where: UserWhereInput, orderBy: Enumerable, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + # ... other queries +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + users: forwardTo('prisma'), + // ... other resolvers + }, +} +``` + +##### Implementing the `users` resolver with Prisma Client + +To re-implement queries that were previously using `forwardTo`, the idea is to pass the incoming filtering, ordering and pagination arguments to `PrismaClient`: + +```js +const resolvers = { + Query: { + users: (_, args, context, info) => { + // this doesn't work yet + const { where, orderBy, skip, first, last, after, before } = args + return context.prisma.user.findMany({ + where, + orderBy, + skip, + first, + last, + after, + before, + }) + }, + // ... other resolvers + }, +} +``` + +Note that this approach does **not** work yet because the _structures_ of the incoming arguments is different from the ones expected by `PrismaClient`. To ensure the structures are compatible, you can use the `@prisma/binding-argument-transform` npm package which ensures compatibility: + +```copy +npm install @prisma/binding-argument-transform +``` + +You can now use this package as follows: + +```js +const { + makeOrderByPrisma2Compatible, + makeWherePrisma2Compatible, +} = require('@prisma/binding-argument-transform') + +const resolvers = { + Query: { + users: (_, args, context, info) => { + // this still doesn't entirely work + const { where, orderBy, skip, first, last, after, before } = args + const prisma2Where = makeWherePrisma2Compatible(where) + const prisma2OrderBy = makeOrderByPrisma2Compatible(orderBy) + return context.prisma.user.findMany({ + where: prisma2Where, + orderBy: prisma2OrderBy, + skip, + first, + last, + after, + before, + }) + }, + // ... other resolvers + }, +} +``` + +The last remaining issue with this are the pagination arguments. Prisma 2 introduces a [new pagination API](https://github.com/prisma/prisma/releases/tag/2.0.0-beta.7): + +- The `first`, `last`, `before` and `after` arguments are removed +- The new `cursor` argument replaces `before` and `after` +- The new `take` argument replaces `first` and `last` + +Here is how you can adjust the call to make it compliant with the new Prisma Client pagination API: + +```js +const { + makeOrderByPrisma2Compatible, + makeWherePrisma2Compatible, +} = require('@prisma/binding-argument-transform') + +const resolvers = { + Query: { + users: (_, args, context) => { + const { where, orderBy, skip, first, last, after, before } = args + const prisma2Where = makeWherePrisma2Compatible(where) + const prisma2OrderBy = makeOrderByPrisma2Compatible(orderBy) + const skipValue = skip || 0 + const prisma2Skip = Boolean(before) ? skipValue + 1 : skipValue + const prisma2Take = Boolean(last) ? -last : first + const prisma2Before = { id: before } + const prisma2After = { id: after } + const prisma2Cursor = + !Boolean(before) && !Boolean(after) + ? undefined + : Boolean(before) + ? prisma2Before + : prisma2After + return context.prisma.user.findMany({ + where: prisma2Where, + orderBy: prisma2OrderBy, + skip: prisma2Skip, + cursor: prisma2Cursor, + take: prisma2Take, + }) + }, + // ... other resolvers + }, +} +``` + +The calculations are needed to ensure the incoming pagination arguments map properly to the ones from the Prisma Client API. + +#### 3.1.2. Migrate the `posts(searchString: String): [Post!]!` query + +The `posts` query is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + posts(searchString: String): [Post!]! + # ... other queries +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + posts: (_, args, context, info) => { + return context.prisma.query.posts( + { + where: { + OR: [ + { title_contains: args.searchString }, + { content_contains: args.searchString }, + ], + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Implementing the `posts` resolver with Prisma Client + +To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-11;normal +const resolvers = { + Query: { + posts: (_, args, context) => { + return context.prisma.post.findMany({ + where: { + OR: [ + { title: { contains: args.searchString } }, + { content: { contains: args.searchString } }, + ], + }, + }) + }, + // ... other resolvers + }, +} +``` + +You can now send the respective query in the GraphQL Playground: + +```graphql +{ + posts { + id + title + author { + id + name + } + } +} +``` + +#### 3.1.3. Migrate the `user(uniqueInput: UserUniqueInput): User` query + +In our sample app, the `user` query is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Query { + user(userUniqueInput: UserUniqueInput): User + # ... other queries +} + +input UserUniqueInput { + id: String + email: String +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Query: { + user: (_, args, context, info) => { + return context.prisma.query.user( + { + where: args.userUniqueInput, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Implementing the `user` resolver with Prisma Client + +To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-7;normal +const resolvers = { + Query: { + user: (_, args, context) => { + return context.prisma.user.findUnique({ + where: args.userUniqueInput, + }) + }, + // ... other resolvers + }, +} +``` + +You can now send the respective query via the GraphQL Playground: + +```graphql +{ + user(userUniqueInput: { email: "alice@prisma.io" }) { + id + name + } +} +``` + +### 3.1. Migrate GraphQL mutations + +In this section, you'll migrate the GraphQL mutations from the sample schema. + +#### 3.1.2. Migrate the `createUser` mutation (which uses `forwardTo`) + +In the sample app, the `createUser` mutation from the sample GraphQL schema is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + createUser(data: UserCreateInput!): User! + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + createUser: forwardTo('prisma'), + // ... other resolvers + }, +} +``` + +##### Implementing the `createUser` resolver with Prisma Client + +To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-7;normal +const resolvers = { + Mutation: { + createUser: (_, args, context, info) => { + return context.prisma.user.create({ + data: args.data, + }) + }, + // ... other resolvers + }, +} +``` + +You can now write your first mutation against the new API, e.g.: + +```graphql +mutation { + createUser(data: { name: "Alice", email: "alice@prisma.io" }) { + id + } +} +``` + +#### 3.1.3. Migrate the `createDraft(title: String!, content: String, authorId: String!): Post!` query + +In the sample app, the `createDraft` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + createDraft(title: String!, content: String, authorId: String!): Post! + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + createDraft: (_, args, context, info) => { + return context.prisma.mutation.createPost( + { + data: { + title: args.title, + content: args.content, + author: { + connect: { + id: args.authorId, + }, + }, + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Implementing the `createDraft` resolver with Prisma Client + +To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-15;normal +const resolvers = { + Mutation: { + createDraft: (_, args, context, info) => { + return context.prisma.post.create({ + data: { + title: args.title, + content: args.content, + author: { + connect: { + id: args.authorId, + }, + }, + }, + }) + }, + // ... other resolvers + }, +} +``` + +You can now send the respective mutation via the GraphQL Playground: + +```graphql +mutation { + createDraft(title: "Hello World", authorId: "__AUTHOR_ID__") { + id + published + author { + id + name + } + } +} +``` + +#### 3.1.4. Migrate the `updateBio(bio: String, userUniqueInput: UserUniqueInput!): User` mutation + +In the sample app, the `updateBio` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + updateBio: (_, args, context, info) => { + return context.prisma.mutation.updateUser( + { + data: { + profile: { + update: { bio: args.bio }, + }, + }, + where: { id: args.userId }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Implementing the `updateBio` resolver with Prisma Client + +To get the same behavior with Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-12;normal +const resolvers = { + Mutation: { + updateBio: (_, args, context, info) => { + return context.prisma.user.update({ + data: { + profile: { + update: { bio: args.bio }, + }, + }, + where: args.userUniqueInput, + }) + }, + // ... other resolvers + }, +} +``` + +You can now send the respective mutation via the GraphQL Playground : + +```graphql +mutation { + updateBio( + userUniqueInput: { email: "alice@prisma.io" } + bio: "I like turtles" + ) { + id + name + profile { + id + bio + } + } +} +``` + +#### 3.1.5. Migrate the `addPostToCategories(postId: String!, categoryIds: [String!]!): Post` mutation + +In our sample app, the `addPostToCategories` mutation is defined and implemented as follows. + +##### SDL schema definition with `prisma-binding` + +```graphql +type Mutation { + addPostToCategories(postId: String!, categoryIds: [String!]!): Post + # ... other mutations +} +``` + +##### Resolver implementation with `prisma-binding` + +```js +const resolvers = { + Mutation: { + addPostToCategories: (_, args, context, info) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.mutation.updatePost( + { + data: { + categories: { + connect: ids, + }, + }, + where: { + id: args.postId, + }, + }, + info + ) + }, + // ... other resolvers + }, +} +``` + +##### Implementing the `addPostToCategories` resolver with Prisma Client + +To get the same behavior with Prisma Client, you'll need to adjust your resolver implementation: + +```js line-number highlight=3-13;normal +const resolvers = { + Mutation: { + addPostToCategories: (_, args, context, info) => { + const ids = args.categoryIds.map((id) => ({ id })) + return context.prisma.post.update({ + where: { + id: args.postId, + }, + data: { + categories: { connect: ids }, + }, + }) + }, + // ... other resolvers + }, +} +``` + +You can now send the respective query via the GraphQL Playground: + +```graphql +mutation { + addPostToCategories( + postId: "__AUTHOR_ID__" + categoryIds: ["__CATEGORY_ID_1__", "__CATEGORY_ID_2__"] + ) { + id + title + categories { + id + name + } + } +} +``` + +## 4. Cleaning up + +Since the entire app has now been upgraded to Prisma 2, you can delete all unnecessary files and remove the no longer needed dependencies. + +### 4.1. Clean up npm dependencies + +You can start by removing npm dependencies that were related to the Prisma 1 setup: + +```copy +npm uninstall graphql-cli prisma-binding prisma1 +``` + +### 4.2. Delete unused files + +Next, delete the files of your Prisma 1 setup: + +```copy +rm prisma1/datamodel.prisma prisma1/prisma.yml +``` + +### 4.3. Stop the Prisma server + +Finally, you can stop running your Prisma server. diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/07-upgrading-a-rest-api.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/07-upgrading-a-rest-api.mdx new file mode 100644 index 0000000000..5e120e3f15 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/07-upgrading-a-rest-api.mdx @@ -0,0 +1,251 @@ +--- +title: 'REST API' +metaTitle: 'Upgrading a REST API from Prisma 1 to Prisma 2' +metaDescription: 'Learn how to upgrade a REST API from Prisma 1 to Prisma 2.' +--- + +## Overview + +This upgrade guide describes how to migrate a Node.js project that's based on [Prisma 1](https://github.com/prisma/prisma1) and uses the [Prisma 1 client](https://v1.prisma.io/docs/1.34/prisma-client/) to implement a REST API. + +The guide assumes that you already went through the [guide for upgrading the Prisma layer](/orm/more/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgresql). This means you already: + +- installed the Prisma 2 CLI +- created your Prisma 2 schema +- introspected your database and resolved potential schema incompatibilities +- installed and generated Prisma Client + +The guide further assumes that you have a file setup that looks similar to this: + +``` +. +├── README.md +├── package-lock.json +├── package.json +├── prisma +│ ├── datamodel.prisma +│ ├── docker-compose-mysql.yml +│ ├── docker-compose.yml +│ ├── prisma.yml +│ └── seed.graphql +├── src +│ ├── generated +│ │ └── prisma-client +│ │ ├── index.ts +│ │ └── prisma-schema.ts +│ └── index.ts +└── tsconfig.json +``` + +The important parts are: + +- A folder called with `prisma` with your Prisma 2 schema +- A folder called `src` with your application code + +If this is not what your project structure looks like, you'll need to adjust the instructions in the guide to match your own setup. + +## 1. Adjust the application to use Prisma Client 2 + +For the purpose of this guide, we'll use the sample API calls from the [`rest-express`](https://github.com/prisma/prisma1-examples/tree/master/typescript/rest-express) example in the [`prisma1-examples`](https://github.com/prisma/prisma1-examples/) repository. + +The application code in our example is located in a single file and looks as follows: + +```ts +import * as express from 'express' +import * as bodyParser from 'body-parser' +import { prisma } from './generated/prisma-client' + +const app = express() + +app.$use(bodyParser.json()) + +app.post(`/user`, async (req, res) => { + const result = await prisma.createUser({ + ...req.body, + }) + res.json(result) +}) + +app.post(`/post`, async (req, res) => { + const { title, content, authorEmail } = req.body + const result = await prisma.createPost({ + title: title, + content: content, + author: { connect: { email: authorEmail } }, + }) + res.json(result) +}) + +app.put('/publish/:id', async (req, res) => { + const { id } = req.params + const post = await prisma.updatePost({ + where: { id }, + data: { published: true }, + }) + res.json(post) +}) + +app.delete(`/post/:id`, async (req, res) => { + const { id } = req.params + const post = await prisma.deletePost({ id }) + res.json(post) +}) + +app.get(`/post/:id`, async (req, res) => { + const { id } = req.params + const post = await prisma.post({ id }) + res.json(post) +}) + +app.get('/feed', async (req, res) => { + const posts = await prisma.post({ where: { published: true } }) + res.json(posts) +}) + +app.get('/filterPosts', async (req, res) => { + const { searchString } = req.query + const draftPosts = await prisma.post({ + where: { + OR: [ + { + title_contains: searchString, + }, + { + content_contains: searchString, + }, + ], + }, + }) + res.json(draftPosts) +}) + +app.listen(3000, () => + console.log('Server is running on http://localhost:3000') +) +``` + +Consider each occurrence of the Prisma Client instance `prisma` and replacing with the respective usage of Prisma Client 2. You can learn more in the [API Reference](/orm/prisma-client). + +### 1.1. Adjusting the import + +Import the generated `@prisma/client` node module as shown: + +```ts +import { PrismaClient } from '@prisma/client' +``` + +Note that this only imports the `PrismaClient` constructor, so you also need to instantiate a Prisma Client 2 instance: + +```ts +const prisma = new PrismaClient() +``` + +### 1.2. Adjusting the `/user` route (`POST`) + +With the Prisma Client 2 API, the `/user` route for `POST` requests has to be changed to: + +```ts +app.post(`/user`, async (req, res) => { + const result = await prisma.user.create({ + data: { + ...req.body, + }, + }) + res.json(result) +}) +``` + +### 1.3. Adjusting the `/post` route (`POST`) + +With the Prisma Client 2 API, the `/post` route for `POST` requests has to be changed to: + +```ts +app.post(`/post`, async (req, res) => { + const { title, content, authorEmail } = req.body + const result = await prisma.post.create({ + data: { + title: title, + content: content, + author: { connect: { email: authorEmail } }, + }, + }) + res.json(result) +}) +``` + +### 1.4. Adjusting the `/publish/:id` route (`PUT`) + +With the Prisma Client 2 API, the `/publish/:id` route for `PUT` requests has to be changed to: + +```ts +app.put('/publish/:id', async (req, res) => { + const { id } = req.params + const post = await prisma.post.update({ + where: { id }, + data: { published: true }, + }) + res.json(post) +}) +``` + +### 1.5. Adjusting the `/post/:id` route (`DELETE`) + +With the Prisma Client 2 API, the `//post/:id` route for `DELETE` requests has to be changed to: + +```ts +app.delete(`/post/:id`, async (req, res) => { + const { id } = req.params + const post = await prisma.post.delete({ + where: { id }, + }) + res.json(post) +}) +``` + +### 1.6. Adjusting the `/post/:id` route (`GET`) + +With the Prisma Client 2 API, the `/post/:id` route for `GET` requests has to be changed to: + +```ts +app.get(`/post/:id`, async (req, res) => { + const { id } = req.params + const post = await prisma.post.findUnique({ + where: { id }, + }) + res.json(post) +}) +``` + +### 1.7. Adjusting the `/feed` route (`GET`) + +With the Prisma Client 2 API, the `/feed` route for `GET` requests has to be changed to: + +```ts +app.get('/feed', async (req, res) => { + const posts = await prisma.post.findMany({ where: { published: true } }) + res.json(posts) +}) +``` + +### 1.8. Adjusting the `/filterPosts` route (`GET`) + +With the Prisma Client 2 API, the `/user` route for `POST` requests has to be changed to: + +```ts +app.get('/filterPosts', async (req, res) => { + const { searchString } = req.query + const filteredPosts = await prisma.post.findMany({ + where: { + OR: [ + { + title: { contains: searchString }, + }, + { + content: { contains: searchString }, + }, + ], + }, + }) + res.json(filteredPosts) +}) +``` diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/08-upgrade-from-mongodb-beta.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/08-upgrade-from-mongodb-beta.mdx new file mode 100644 index 0000000000..55316c7110 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/08-upgrade-from-mongodb-beta.mdx @@ -0,0 +1,232 @@ +--- +title: 'Upgrade from MongoDB Beta' +metaTitle: 'Upgrade from the Prisma 1 MongoDB Beta to Prisma 2 or later' +metaDescription: 'Learn how to upgrade your MongoDB application running Prisma 1 to Prisma 2 or later.' +--- + +## Introduction + +This guide helps you migrate from the Prisma 1 MongoDB Beta to MongoDB on Prisma 2 or later. To learn more about the differences between Prisma 1 and Prisma 2.x and later, refer to [this document](/orm/more/upgrade-guides/upgrade-from-prisma-1/how-to-upgrade#main-differences-between-prisma-1-and-prisma-version-2x-and-later). + +The scope of this guide is to give you the workflow necessary to perform the migration and highlight some of the problems you might encounter. + +We unfortunately can't cover all possible scenarios or changes required, but this guide should help you on your journey. Join [our community Slack](https://slack.prisma.io/) or create an issue [on Github](https://github.com/prisma/prisma1/issues/new/choose) with any questions. + + + Perform this migration on your staging environment before trying this in + production! + + +## Requirements + +- Must be running MongoDB 4.2+ as a replica set (MongoDB Atlas does this for you automatically) +- Node.js: see [system requirements](/orm/reference/system-requirements) +- TypeScript: see [system requirements](/orm/reference/system-requirements) + +## Installing Prisma 3.12.0 or later + +In your project directory run the following commands: + +```bash +$ npm install prisma@latest +$ npx prisma init --datasource-provider=mongodb +``` + +This should create the following files: + +- `prisma/schema.prisma`: An initial Prisma schema +- `.env`: Environment file where you'll store your connection string + + + +If you see the following error: + +``` +ERROR File schema.prisma already exists in your project. +Please try again in a project that is not yet using Prisma. +``` + +You have likely a `prisma/` directory in your project already. Rename that directory to something like `_prisma/` and try again + + + +## Find the Connection String to your MongoDB Database + +Next you'll want to find the connection string to your MongoDB database. You should be able to find it in your `docker-compose.yml` file or on MongoDB Atlas. It's what you'd pass to MongoDB Compass. The connection string should look something like this: + +```bash +mongodb://:@:27017 +``` + +The database that stores application data in Prisma 1 is called `default_default`, so we'll add that to the end of the connection string and update the `DATABASE_URL` key in the `.env` file + +```bash file=.env +DATABASE_URL="mongodb://prisma:prisma@localhost:27017/default_default" +``` + +## Introspect your MongoDB Database + +You're now ready to pull the structure of your database down into your Prisma Schema. + +```bash +$ npx prisma db pull +``` + +And you should see your Prisma schema in `prisma/schema.prisma` populated with your models. + + + +If you see the following error: `Error in connector: SCRAM failure: Authentication failed.`, try adding `?authSource=admin` to the end of your connection string and trying again. + + + +## Touching up your Prisma Schema + +The generated Prisma Client from a freshly introspected Prisma 1 based MongoDB database may not have the best API. You can adjust the model names and fields, just be sure to `@map` and `@@map` the original name to the underlying database collection and field names: + +```diff +- model posts { ++ model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + published Boolean + title String ++ @@map("posts") + } + +- model users { ++ model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + email String @unique(map: "email_U") + name String +- posts String[] @db.ObjectId ++ postIds String[] @db.ObjectId @map("posts") + + @@index([posts], map: "posts_R") ++ @@map("users") + } +``` + +Take caution in doing these renames because you need to make sure the Prisma Schema still maps properly to the underlying database collections and field names. + +Unlike SQL databases, MongoDB doesn't have an explicit understanding of relationships between data. This means that Prisma's introspection is unable to infer those relationships for you. + +We typically recommend adding the relationships by hand with the help of [this documentation](/orm/overview/databases/mongodb#how-to-add-in-missing-relations-after-introspection). However, Prisma 1 stores foreign keys is different than where Prisma 2 and later expects foreign keys, so if you want to take advantage of relationships, you'll need to shift where the foreign keys are on your database before adding the relationships. + + + +💡 Download the Prisma VSCode Extension to provide autocomplete and helpful error messages as you transition your Prisma schema. + + + +## Generating a Prisma Client + +With the Prisma schema populated with the schema of your data, you're now ready to generate a Typescript Client to read and write to your MongoDB database. + +```bash +$ npx prisma generate +``` + +## Testing Reads + +Create a simple `test.ts` script to verify that Prisma Client can read and write to your application. Note that this guide is using the example in the [Prisma 1 examples repository](https://github.com/prisma/prisma1-examples/tree/master/typescript/docker-mongodb), but the code will change depending on your application. + +```ts +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() + +async function main() { + await prisma.$connect() + const posts = await prisma.post.findMany() + console.log(posts) +} + +main() + .catch(console.error) + .finally(() => prisma.$disconnect()) +``` + +Make sure `ts-node` is installed globally and run: + +```bash +ts-node test.ts +``` + +You should see a list of your data: + +```bash +[ + { + comments: [], + id: '62435a83fca136000996ba16', + content: 'https://www.prisma.io/day/', + published: true, + title: 'Join us for Prisma Day 2019 in Berlin', + wasCreated: 2022-03-29T19:14:11.172Z, + wasUpdated: 2022-03-29T19:14:11.172Z + }, + { + comments: [ [Object] ], + id: '62435a83fca136000996ba18', + content: 'https://graphqlweekly.com/', + published: true, + title: 'Subscribe to GraphQL Weekly for community news', + wasCreated: 2022-03-29T19:14:11.369Z, + wasUpdated: 2022-03-29T19:14:11.369Z + }, + { + comments: [], + id: '62435a83fca136000996ba1a', + content: 'https://twitter.com/prisma', + published: false, + title: 'Follow Prisma on Twitter', + wasCreated: 2022-03-29T19:14:11.375Z, + wasUpdated: 2022-03-29T19:14:11.375Z + } +] +``` + +## Testing Writes + +You can then alter your `test.ts` to try writes: + +```ts +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() + +async function main() { + await prisma.$connect() + const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + name: 'Alice', + }, + }) + console.log(user) +} + +main() + .catch(console.error) + .finally(() => prisma.$disconnect()) +``` + +And you should see a user was created. + + + +If you see the following error: + +```no-lines wrap +Prisma needs to perform transactions, which requires your MongoDB server to be run as a replica set. https://pris.ly/d/mongodb-replica-set +``` + +This means that your MongoDB database isn't running as a replica set. Refer to [the link above](https://pris.ly/d/mongodb-replica-set) for steps to resolve this issue. + + + +## Upgrading your Application + +Now that you have a working Prisma Client, you can start replacing Prisma 1 queries with the latest Prisma queries. The [Prisma Client Reference](/orm/reference/prisma-client-reference#filter-conditions-and-operators) is a helpful resource for learning how to use the latest Prisma Client. + +## Conclusion + +I hope this brief guide was helpful in getting you started on the right path. Let us know if you have any questions or issues. We really appreciate your support over the years. diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/TablePlus-GUI.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/TablePlus-GUI.png new file mode 100644 index 0000000000..ce3615ae53 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/TablePlus-GUI.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/add-missing-default-constraints-to-columns.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/add-missing-default-constraints-to-columns.png new file mode 100644 index 0000000000..58b8021662 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/add-missing-default-constraints-to-columns.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/altering-columns-to-use-enum.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/altering-columns-to-use-enum.png new file mode 100644 index 0000000000..2f6088e382 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/altering-columns-to-use-enum.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/download-graphql-schema.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/download-graphql-schema.png new file mode 100644 index 0000000000..cfe963c4bf Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/download-graphql-schema.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/expose-prisma-model-fields-with-t-model.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/expose-prisma-model-fields-with-t-model.png new file mode 100644 index 0000000000..eff2fa57aa Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/expose-prisma-model-fields-with-t-model.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-columns-with-json-data-types.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-columns-with-json-data-types.png new file mode 100644 index 0000000000..6818c41071 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-columns-with-json-data-types.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-incorrect-m-n-relations-sql.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-incorrect-m-n-relations-sql.png new file mode 100644 index 0000000000..1ad8079a24 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-incorrect-m-n-relations-sql.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-schema-incompatibilities.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-schema-incompatibilities.png new file mode 100644 index 0000000000..ec99098339 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/fix-schema-incompatibilities.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/prisma-cli-introspection-flow.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/prisma-cli-introspection-flow.png new file mode 100644 index 0000000000..7b12d1ee24 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/prisma-cli-introspection-flow.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/regenerate-resolvers-with-t-crud.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/regenerate-resolvers-with-t-crud.png new file mode 100644 index 0000000000..05afbde8e1 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/regenerate-resolvers-with-t-crud.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/run-sql-command-to-alter-column.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/run-sql-command-to-alter-column.png new file mode 100644 index 0000000000..456f7a91c7 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/run-sql-command-to-alter-column.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/use-t-crud-to-generate-resolvers.png b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/use-t-crud-to-generate-resolvers.png new file mode 100644 index 0000000000..774c6235d0 Binary files /dev/null and b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/images/use-t-crud-to-generate-resolvers.png differ diff --git a/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/index.mdx b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/index.mdx new file mode 100644 index 0000000000..5c40823d57 --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/800-upgrade-from-prisma-1/index.mdx @@ -0,0 +1,10 @@ +--- +title: 'Upgrade from Prisma 1' +metaTitle: 'Upgrade from Prisma 1 to Prisma 2' +staticLink: false +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/800-more/300-upgrade-guides/index.mdx b/docs/200-orm/800-more/300-upgrade-guides/index.mdx new file mode 100644 index 0000000000..8e2a37d6ef --- /dev/null +++ b/docs/200-orm/800-more/300-upgrade-guides/index.mdx @@ -0,0 +1,10 @@ +--- +title: 'Upgrade guides' +metaTitle: 'Upgrade guides' +metaDescription: 'Learn how to upgrade Prisma versions.' +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/800-more/400-comparisons/01-prisma-and-typeorm.mdx b/docs/200-orm/800-more/400-comparisons/01-prisma-and-typeorm.mdx new file mode 100644 index 0000000000..f4df8d6c06 --- /dev/null +++ b/docs/200-orm/800-more/400-comparisons/01-prisma-and-typeorm.mdx @@ -0,0 +1,1243 @@ +--- +title: 'TypeORM' +metaTitle: 'Prisma vs TypeORM' +metaDescription: 'Learn how Prisma compares to TypeORM.' +--- + + + +This page compares Prisma and [TypeORM](https://typeorm.io/#/). If you want to learn how to migrate from TypeORM to Prisma, check out this [guide](/orm/more/migrating-to-prisma/migrate-from-typeorm). + + + +## TypeORM vs Prisma + +While Prisma and TypeORM solve similar problems, they work in very different ways. + +**TypeORM** is a traditional ORM which maps _tables_ to _model classes_. These model classes can be used to generate SQL migrations. Instances of the model classes then provide an interface for CRUD queries to an application at runtime. + +**Prisma** is a new kind of ORM that mitigates many problems of traditional ORMs, such as bloated model instances, mixing business with storage logic, lack of type-safety or unpredictable queries caused e.g. by lazy loading. + +It uses the [Prisma schema](/orm/prisma-schema) to define application models in a declarative way. Prisma Migrate then allows to generate SQL migrations from the Prisma schema and executes them against the database. CRUD queries are provided by Prisma Client, a lightweight and entirely type-safe database client for Node.js and TypeScript. + +## API design & Level of abstraction + +TypeORM and Prisma operate on different levels of abstraction. TypeORM is closer to mirroring SQL in its API while Prisma Client provides a higher-level abstraction that was carefully designed with the common tasks of application developers in mind. Prisma's API design heavily leans on the idea of [making the right thing easy](https://jason.energy/right-thing-easy-thing/). + +While Prisma Client operates on a higher-level of abstraction, it strives to expose the full power of the underlying database and lets you drop down to [raw SQL](/orm/prisma-client/queries/raw-database-access/raw-queries) at any time if your use case requires it. + +The following sections examine a few examples for how Prisma's and TypeORM's APIs differ in certain scenarios and what the rationale of Prisma's API design is in these cases. + +### Filtering + +TypeORM primarily leans on SQL operators for filtering lists or records, e.g. with the `find` method. Prisma on the other hand, provides a more [generic set of operators](/orm/prisma-client/queries/filtering-and-sorting#filter-conditions-and-operators) that are intuitive to use. It should also be noted that, as explained in the type-safety section [below](#filtering-1), TypeORM loses type-safety in filter queries in many scenarios. + +A good example of how the filtering APIs of both TypeORM and Prisma differ is by looking at `string` filters. While TypeORM primarily provides the filter based on the `ILike` operator which comes directly from SQL, Prisma provides more specific operators that developers can use, e.g.: `contains`, `startsWith` and `endsWith`. + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: 'Hello World', + }, +}) +``` + + + + + +```ts +const posts = await postRepository.find({ + where: { + title: ILike('Hello World'), + }, +}) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { contains: 'Hello World' }, + }, +}) +``` + + + + + +```ts +const posts = await postRepository.find({ + where: { + title: ILike('%Hello World%'), + }, +}) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { startsWith: 'Hello World' }, + }, +}) +``` + + + + + +```ts +const posts = await postRepository.find({ + where: { + title: ILike('Hello World%'), + }, +}) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { endsWith: 'Hello World' }, + }, +}) +``` + + + + + +```ts +const posts = await postRepository.find({ + where: { + title: ILike('%Hello World'), + }, +}) +``` + + + + + +### Pagination + +TypeORM only offers limit-offset pagination while Prisma conveniently provides dedicated APIs for both limit-offset but also cursor-based. You can learn more about both approaches in the [Pagination](/orm/prisma-client/queries/pagination) section of the docs or in the API comparison [below](#pagination-1). + +### Relations + +Working with records that are connected via foreign keys can become very complex in SQL. Prisma's concept of [virtual relation field](/orm/prisma-schema/data-model/relations#relation-fields) enables an intuitive and convenient way for application developers to work with related data. Some benefits of Prisma's approach are: + +- traversing relationships via the fluent API ([docs](/orm/prisma-client/queries/relation-queries#fluent-api)) +- nested writes that enable updating/creating connected records ([docs](/orm/prisma-client/queries/relation-queries#nested-writes)) +- applying filters on related records ([docs](/orm/prisma-client/queries/relation-queries#relation-filters)) +- easy and type-safe querying of nested data without worrying about JOINs ([docs](/orm/prisma-client/queries/relation-queries#nested-reads)) +- creating nested TypeScript typings based on models and their relations ([docs](/orm/prisma-client/type-safety)) +- intuitive modeling of relations in the data model via relation fields ([docs](/orm/prisma-schema/data-model/relations)) +- implicit handling of relation tables (also sometimes called JOIN, link, pivot or junction tables) ([docs](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations)) + +### Data modeling and migrations + +Prisma models are defined in the [Prisma schema](/orm/prisma-schema) while TypeORM uses classes and experimental TypeScript decorators for model definitions. With the Active Record ORM pattern, TypeORM's approach often leads to complex model instances that are becoming hard to maintain as an application grows. + +Prisma on the other hand generates a lightweight database client that exposes a tailored and fully type-safe API to read and write data for the models that are defined in the Prisma schema, following the DataMapper ORM pattern rather than Active Record. + +Prisma's DSL for data modeling is lean, simple and intuitive to use. When modeling data in VS Code, you can further take advantage of Prisma's powerful VS Code extension with features like autocompletion, quick fixes, jump to definition and other benefits that increase developer productivity. + + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) +} +``` + + + + + +```ts +import { + Entity, + PrimaryGeneratedColumn, + Column, + OneToMany, + ManyToOne, +} from 'typeorm' + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ nullable: true }) + name: string + + @Column({ unique: true }) + email: string + + @OneToMany((type) => Post, (post) => post.author) + posts: Post[] +} + +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @Column() + title: string + + @Column({ nullable: true }) + content: string + + @Column({ default: false }) + published: boolean + + @ManyToOne((type) => User, (user) => user.posts) + author: User +} +``` + + + + + +Migrations work in similar fashions in TypeORM and Prisma. Both tools follow the approach of generating SQL files based on the provided model definitions and provide a CLI to execute them against the database. The SQL files can be modified before the migrations are executed so that any custom database operation can be performed with either migration system. + +## Type safety + +TypeORM has been one of the first ORMs in the Node.js ecosystem to fully embrace TypeScript and has done a great job in enabling developers to get a certain level of type safety for their database queries. + +However, there are numerous situations where the type safety guarantees of TypeORM fall short. The following sections describe the scenarios where Prisma can provide stronger guarantees for the types of query results. + +### Selecting fields + +This section explains the differences in type safety when selecting a subset of a model's fields in a query. + +#### TypeORM + +TypeORM provides a `select` option for its [`find`](https://typeorm.io/#/find-options) methods (e.g. `find`, `findByIds`, `findOne`, ...), for example: + + + + + +```ts +const postRepository = getManager().getRepository(Post) +const publishedPosts: Post[] = await postRepository.find({ + where: { published: true }, + select: ['id', 'title'], +}) +``` + + + + + +```ts +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @Column() + title: string + + @Column({ nullable: true }) + content: string + + @Column({ default: false }) + published: boolean + + @ManyToOne((type) => User, (user) => user.posts) + author: User +} +``` + + + + + +While each object in the returned `publishedPosts` array only carries the selected `id` and `title` properties at runtime, the TypeScript compiler doesn't have any knowledge of this. It will allow you to access any other properties defined on the `Post` entity after the query, for example: + +```ts +const post = publishedPosts[0] + +// The TypeScript compiler has no issue with this +if (post.content.length > 0) { + console.log(`This post has some content.`) +} +``` + +This code will result in an error at runtime: + +``` +TypeError: Cannot read property 'length' of undefined +``` + +The TypeScript compiler only sees the `Post` type of the returned objects, but it doesn't know about the fields that these objects _actually_ carry at runtime. It therefore can't protect you from accessing fields that have not been retrieved in the database query, resulting in a runtime error. + +#### Prisma + +Prisma Client can guarantee full type safety in the same situation and protects you from accessing fields that were not retrieved from the database. + +Consider the same example with a Prisma Client query: + + + + + +```ts +const publishedPosts = await prisma.post.findMany({ + where: { published: true }, + select: { + id: true, + title: true, + }, +}) +const post = publishedPosts[0] + +// The TypeScript compiler will not allow this +if (post.content.length > 0) { + console.log(`This post has some content.`) +} +``` + + + + + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) +} +``` + + + + + +In this case, the TypeScript compiler will throw the following error already at compile-time: + +``` +[ERROR] 14:03:39 ⨯ Unable to compile TypeScript: +src/index.ts:36:12 - error TS2339: Property 'content' does not exist on type '{ id: number; title: string; }'. + +42 if (post.content.length > 0) { +``` + +This is because Prisma Client generates the return type for its queries _on the fly_. In this case, `publishedPosts` is typed as follows: + +```ts +const publishedPosts: { + id: number + title: string +}[] +``` + +It therefore is impossible for you to accidentally access a property on a model that has not been retrieved in a query. + +### Loading relations + +This section explains the differences in type safety when loading relations of a model in a query. In traditional ORMs, this is sometimes called _eager loading_. + +#### TypeORM + +TypeORM allows to eagerly load relations from the database via the `relations` option that can be passed to its [`find`](https://typeorm.io/#/find-options) methods. + +Consider this example: + + + + + +```ts +const postRepository = getManager().getRepository(Post) +const publishedPosts: Post[] = await postRepository.find({ + where: { published: true }, + relations: ['author'], +}) +``` + + + + + +```ts +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @Column() + title: string + + @Column({ nullable: true }) + content: string + + @Column({ default: false }) + published: boolean + + @ManyToOne((type) => User, (user) => user.posts) + author: User +} +``` + +```ts +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ nullable: true }) + name: string + + @Column({ unique: true }) + email: string + + @OneToMany((type) => Post, (post) => post.author) + posts: Post[] +} +``` + + + + + +Unlike with `select`, TypeORM does _not_ provide autocompletion, nor any type-safety for the strings that are passed to the `relations` option. This means, the TypeScript compiler is not able to catch any typos that are made when querying these relations. For example, it would allow for the following query: + +```ts +const publishedPosts: Post[] = await postRepository.find({ + where: { published: true }, + // this query would lead to a runtime error because of a typo + relations: ['authors'], +}) +``` + +This subtle typo would now lead to the following runtime error: + +``` +UnhandledPromiseRejectionWarning: Error: Relation "authors" was not found; please check if it is correct and really exists in your entity. +``` + +#### Prisma + +Prisma protects you from mistakes like this and thus eliminates a whole class of errors that can occur in your application at runtime. When using `include` to load a relation in a Prisma Client query, you can not only take advantage of autocompletion to specify the query, but the result of the query will also be properly typed: + + + + + +```ts +const publishedPosts = await prisma.post.findMany({ + where: { published: true }, + include: { author: true }, +}) +``` + + + + + +```ts +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) +} +``` + + + + + +Again, the type of `publishedPosts` is generated on the fly and looks as follows: + +```ts +const publishedPosts: (Post & { + author: User +})[] +``` + +For reference, this is what the `User` and `Post` types look like that Prisma Client generates for your Prisma models: + + + + + +```ts +// Generated by Prisma +export type User = { + id: number + name: string | null + email: string +} +``` + + + + + +```ts +// Generated by Prisma +export type Post = { + id: number + title: string + content: string | null + published: boolean + authorId: number | null +} +``` + + + + + +### Filtering + +This section explains the differences in type safety when filtering a list of records using `where`. + +#### TypeORM + +TypeORM allows to pass a `where` option to its [`find`](https://typeorm.io/#/find-options) methods to filter the list of returned records according to specific criteria. These criteria can be defined with respect to a model's properties. + +##### Loosing type-safety using operators + +Consider this example: + + + + + +```ts +const postRepository = getManager().getRepository(Post) +const publishedPosts: Post[] = await postRepository.find({ + where: { + published: true, + title: ILike('Hello World'), + views: MoreThan(0), + }, +}) +``` + + + + + +```ts +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @Column() + title: string + + @Column({ nullable: true }) + content: string + + @Column({ nullable: true }) + views: number + + @Column({ default: false }) + published: boolean + + @ManyToOne((type) => User, (user) => user.posts) + author: User +} +``` + + + + + +This code runs properly and produces a valid query at runtime. However, the `where` option is not really type-safe in various different scenarios. When using a `FindOperator` like `ILike` or `MoreThan` that only work for specific types (`ILike` works for strings, `MoreThan` for numbers), you're losing the guarantee of providing the correct type for the model's field. + +For example, you can provide a string to the `MoreThan` operator. The TypeScript compiler will not complain and your application will only fail at runtime: + +```ts +const postRepository = getManager().getRepository(Post) +const publishedPosts: Post[] = await postRepository.find({ + where: { + published: true, + title: ILike('Hello World'), + views: MoreThan('test'), + }, +}) +``` + +The code above results in a runtime error that the TypeScript compiler doesn't catch for you: + +``` +error: error: invalid input syntax for type integer: "test" +``` + +##### Specifying non-existing properties + +Also note that the TypeScript compiler allows you to specify properties on the `where` option that don't exist on your models – again resulting in runtime errors: + +```ts +const publishedPosts: Post[] = await postRepository.find({ + where: { + published: true, + title: ILike('Hello World'), + viewCount: 1, + }, +}) +``` + +In this case, your application again fails at runtime with the following error: + +``` +EntityColumnNotFound: No entity column "viewCount" was found. +``` + +#### Prisma + +Both filtering scenarios that are problematic with TypeORM in terms of type-safety are covered by Prisma in a fully type-safe way. + +##### Type-safe usage of operators + +With Prisma, the TypeScript compiler enforces the correct usage of an operator per field: + +```ts +const publishedPosts = await prisma.post.findMany({ + where: { + published: true, + title: { contains: 'Hello World' }, + views: { gt: 0 }, + }, +}) +``` + +It would not be allowed to specify the same problematic query shown above with Prisma Client: + +```ts +const publishedPosts = await prisma.post.findMany({ + where: { + published: true, + title: { contains: 'Hello World' }, + views: { gt: 'test' }, // Caught by the TypeScript compiler + }, +}) +``` + +The TypeScript compiler would catch this and throw the following error to protect you from a runtime failure of the app: + +``` +[ERROR] 16:13:50 ⨯ Unable to compile TypeScript: +src/index.ts:39:5 - error TS2322: Type '{ gt: string; }' is not assignable to type 'number | IntNullableFilter'. + Type '{ gt: string; }' is not assignable to type 'IntNullableFilter'. + Types of property 'gt' are incompatible. + Type 'string' is not assignable to type 'number'. + +42 views: { gt: "test" } +``` + +##### Type-safe definition of filters as model properties + +With TypeORM, you are able to specify a property on the `where` option that doesn't map to a model's field. In the above example, filtering for `viewCount` therefore led to a runtime error because the field actually is called `views`. + +With Prisma, the TypeScript compiler will not allow to reference any properties inside of `where` that don't exist on the model: + +```ts +const publishedPosts = await prisma.post.findMany({ + where: { + published: true, + title: { contains: 'Hello World' }, + viewCount: { gt: 0 }, // Caught by the TypeScript compiler + }, +}) +``` + +Again, the TypeScript compiler complains with the following message to protect you from your own mistakes: + +```ts +[ERROR] 16:16:16 ⨯ Unable to compile TypeScript: +src/index.ts:39:5 - error TS2322: Type '{ published: boolean; title: { contains: string; }; viewCount: { gt: number; }; }' is not assignable to type 'PostWhereInput'. + Object literal may only specify known properties, and 'viewCount' does not exist in type 'PostWhereInput'. + +42 viewCount: { gt: 0 } +``` + +### Creating new records + +This section explains the differences in type safety when creating new records. + +#### TypeORM + +With TypeORM, there are two main ways to create new records in the database: `insert` and `save`. Both methods allow developers to submit data that can lead to runtime errors when _required_ fields are not provided. + +Consider this example: + + + + + +```ts +const userRepository = getManager().getRepository(User) +const newUser = new User() +newUser.name = 'Alice' +userRepository.save(newUser) +``` + + + + + +```ts +const userRepository = getManager().getRepository(User) +userRepository.insert({ + name: 'Alice', +}) +``` + + + + + +```ts +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ nullable: true }) + name: string + + @Column({ unique: true }) + email: string + + @OneToMany((type) => Post, (post) => post.author) + posts: Post[] +} +``` + + + + + +No matter if you're using `save` or `insert` for record creation with TypeORM, you will get the following runtime error if you forget to provide the value for a required field: + +``` +QueryFailedError: null value in column "email" of relation "user" violates not-null constraint +``` + +The `email` field is defined as required on the `User` entity (which is enforced by a `NOT NULL` constraint in the database). + +### Prisma + +Prisma protects you from these kind of mistakes by enforcing that you submit values for _all_ required fields of a model. + +For example, the following attempt to create a new `User` where the required `email` field is missing would be caught by the TypeScript compiler: + + + + + +```ts +const newUser = await prisma.user.create({ + data: { + name: 'Alice', + }, +}) +``` + + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique +} +``` + + + + + +It would lead to the following compile-time error: + +``` +[ERROR] 10:39:07 ⨯ Unable to compile TypeScript: +src/index.ts:39:5 - error TS2741: Property 'email' is missing in type '{ name: string; }' but required in type 'UserCreateInput'. +``` + +## API comparison + +### Fetching single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, +}) +``` + +**TypeORM** + +```ts +const userRepository = getRepository(User) +const user = await userRepository.findOne(id) +``` + +### Fetching selected scalars of single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, + select: { + name: true, + }, +}) +``` + +**TypeORM** + +```ts +const userRepository = getRepository(User) +const user = await userRepository.findOne(id, { + select: ['id', 'email'], +}) +``` + +### Fetching relations + +**Prisma** + + + + +```ts +const posts = await prisma.user.findUnique({ + where: { + id: 2, + }, + include: { + post: true, + }, +}) +``` + + + + +```ts +const posts = await prisma.user + .findUnique({ + where: { + id: 2, + }, + }) + .post() +``` + + + + +> **Note**: `select` return a `user` object that includes a `post` array, whereas the fluent API only returns a `post` array. + +**TypeORM** + + + + +```ts +const userRepository = getRepository(User) +const user = await userRepository.findOne(id, { + relations: ['posts'], +}) +``` + + + + +```ts +const userRepository = getRepository(User) +const user = await userRepository.findOne(id, { + join: { + alias: 'user', + leftJoinAndSelect: { + posts: 'user.posts', + }, + }, +}) +``` + + + + +```ts +const userRepository = getRepository(User) +const user = await userRepository.findOne(id) +``` + + + + +### Filtering for concrete values + +**Prisma** + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + contains: 'Hello', + }, + }, +}) +``` + +**TypeORM** + +```ts +const userRepository = getRepository(User) +const users = await userRepository.find({ + where: { + name: 'Alice', + }, +}) +``` + +### Other filter criteria + +**Prisma** + +Prisma generates many [additional filters](/orm/prisma-client/queries/filtering-and-sorting) that are commonly used in modern application development. + +**TypeORM** + +TypeORM provides [built-in operators](https://typeorm.io/#/find-options/advanced-options) that can be used to create more complex comparisons + +### Relation filters + +**Prisma** + +Prisma lets you filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a _relation_ of that model. + +For example, the following query returns users with one or more posts with "Hello" in the title: + +```ts +const posts = await prisma.user.findMany({ + where: { + Post: { + some: { + title: { + contains: 'Hello', + }, + }, + }, + }, +}) +``` + +**TypeORM** + +TypeORM doesn't offer a dedicated API for relation filters. You can get similar functionality by using the `QueryBuilder` or writing the queries by hand. + +### Pagination + +**Prisma** + +Cursor-style pagination: + +```ts +const page = await prisma.post.findMany({ + before: { + id: 242, + }, + last: 20, +}) +``` + +Offset pagination: + +```ts +const cc = await prisma.post.findMany({ + skip: 200, + first: 20, +}) +``` + +**TypeORM** + +```ts +const postRepository = getRepository(Post) +const posts = await postRepository.find({ + skip: 5, + take: 10, +}) +``` + +### Creating objects + +**Prisma** + +```ts +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + }, +}) +``` + +**TypeORM** + + + + +```ts +const user = new User() +user.name = 'Alice' +user.email = 'alice@prisma.io' +await user.save() +``` + + + + +```ts +const userRepository = getRepository(User) +const user = await userRepository.create({ + name: 'Alice', + email: 'alice@prisma.io', +}) +await user.save() +``` + + + + +```ts +const userRepository = getRepository(User) +await userRepository.insert({ + name: 'Alice', + email: 'alice@prisma.io', +}) +``` + + + + +### Updating objects + +**Prisma** + +```ts +const user = await prisma.user.update({ + data: { + name: 'Alicia', + }, + where: { + id: 2, + }, +}) +``` + +**TypeORM** + +```ts +const userRepository = getRepository(User) +const updatedUser = await userRepository.update(id, { + name: 'James', + email: 'james@prisma.io', +}) +``` + +### Deleting objects + +**Prisma** + +```ts +const deletedUser = await prisma.user.delete({ + where: { + id: 10, + }, +}) +``` + +**TypeORM** + + + + +```ts +const userRepository = getRepository(User) +await userRepository.delete(id) +``` + + + + +```ts +const userRepository = getRepository(User) +const deletedUser = await userRepository.remove(user) +``` + + + + +### Batch updates + +**Prisma** + +```ts +const user = await prisma.user.updateMany({ + data: { + name: 'Published author!', + }, + where: { + Post: { + some: { + published: true, + }, + }, + }, +}) +``` + +**TypeORM** + +You can use the [query builder to update entities in your database](https://typeorm.io/#/update-query-builder). + +### Batch deletes + +**Prisma** + +```ts +const users = await prisma.user.deleteMany({ + where: { + id: { + in: [1, 2, 6, 6, 22, 21, 25], + }, + }, +}) +``` + +**TypeORM** + + + + +```ts +const userRepository = getRepository(User) +await userRepository.delete([id1, id2, id3]) +``` + + + + +```ts +const userRepository = getRepository(User) +const deleteUsers = await userRepository.remove([user1, user2, user3]) +``` + + + + +### Transactions + +**Prisma** + +```ts +const user = await prisma.user.create({ + data: { + email: 'bob.rufus@prisma.io', + name: 'Bob Rufus', + Post: { + create: [ + { title: 'Working at Prisma' }, + { title: 'All about databases' }, + ], + }, + }, +}) +``` + +**TypeORM** + +```ts +await getConnection().$transaction(async (transactionalEntityManager) => { + const user = getRepository(User).create({ + name: 'Bob', + email: 'bob@prisma.io', + }) + const post1 = getRepository(Post).create({ + title: 'Join us for GraphQL Conf in 2019', + }) + const post2 = getRepository(Post).create({ + title: 'Subscribe to GraphQL Weekly for GraphQL news', + }) + user.posts = [post1, post2] + await transactionalEntityManager.save(post1) + await transactionalEntityManager.save(post2) + await transactionalEntityManager.save(user) +}) +``` diff --git a/docs/200-orm/800-more/400-comparisons/02-prisma-and-sequelize.mdx b/docs/200-orm/800-more/400-comparisons/02-prisma-and-sequelize.mdx new file mode 100644 index 0000000000..9d619ded37 --- /dev/null +++ b/docs/200-orm/800-more/400-comparisons/02-prisma-and-sequelize.mdx @@ -0,0 +1,455 @@ +--- +title: 'Sequelize' +metaTitle: 'Prisma vs Sequelize' +metaDescription: 'Learn how Prisma compares to Sequelize.' +--- + + + +This page compares the Prisma and [Sequelize](https://sequelize.org/master/) APIs. + + + +## Sequelize vs Prisma + +While Prisma and Sequelize solve similar problems, they work in very different ways. + +**Sequelize** is a traditional ORM which maps _tables_ to _model classes_. Instances of the model classes then provide an interface for CRUD queries to an application at runtime. + +**Prisma** is a new kind of ORM that mitigates many problems of traditional ORMs, such as bloated model instances, mixing business with storage logic, lack of type-safety or unpredictable queries caused e.g. by lazy loading. + +It uses the [Prisma schema](/orm/prisma-schema) to define application models in a declarative way. Prisma Migrate then allows to generate SQL migrations from the Prisma schema and executes them against the database. CRUD queries are provided by Prisma Client, a lightweight and entirely type-safe database client for Node.js and TypeScript. + +## API comparison + +### Fetching single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, +}) +``` + +**Sequelize** + +```ts +const user = await User.findByPk(id) +``` + +### Fetching selected scalars of single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, + select: { + name: true, + }, +}) +``` + +**Sequelize** + +```ts +const user = await User.findByPk(1, { attributes: ['name'], raw: true }) +``` + +:::tip + +Use the `raw: true` query option to return plain JavaScript objects. + +::: + +### Fetching relations + +**Prisma** + + + + +```ts +const posts = await prisma.user.findUnique({ + where: { + id: 2, + }, + include: { + post: true, + }, +}) +``` + + + + +```ts +const posts = await prisma.user + .findUnique({ + where: { + id: 2, + }, + }) + .post() +``` + + + + +> **Note**: `select` returns a `user` object that includes a `post` array, whereas the fluent API only returns a `post` array. + +**Sequelize** + +```ts +const user = await User.findByPk(id, { + include: [ + { + model: Post, + }, + ], +}) +``` + +:::tip + +Use `model: Post as "Post"` if you used an alias to define the relationship between `User` and `Post` - for example: `User.hasMany(Post, { as: "Post", foreignKey: "authorId" });` + +::: + +### Filtering for concrete values + +**Prisma** + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + contains: 'Hello', + }, + }, +}) +``` + +**Sequelize** + +```ts +const post = await Post.findAll({ + raw: true, + where: { + title: { + [Op.like]: '%Hello%', + }, + }, +}) +``` + +### Other filter criteria + +**Prisma** + +Prisma generates many [additional filters](/orm/prisma-client/queries/filtering-and-sorting) that are commonly used in modern application development. + +**Sequelize** + +Sequelize has an [extensive set of operators](https://sequelize.org/master/manual/querying.html#operators). + +### Relation filters + +**Prisma** + +Prisma lets you filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a _relation_ of that model. + +For example, the following query returns users with one or more posts with "Hello" in the title: + +```ts +const posts = await prisma.user.findMany({ + where: { + Post: { + some: { + title: { + contains: 'Hello', + }, + }, + }, + }, +}) +``` + +**Sequelize** + +Sequelize [doesn't offer a dedicated API for relation filters](https://github.com/sequelize/sequelize/issues/10943). You can get similar functionality by sending a raw SQL query to the database. + +### Pagination + +**Prisma** + +Cursor-style pagination: + +```ts +const page = await prisma.post.findMany({ + before: { + id: 242, + }, + last: 20, +}) +``` + +Offset pagination: + +```ts +const cc = await prisma.post.findMany({ + skip: 200, + first: 20, +}) +``` + +**Sequelize** + +Cursor pagination: + +```ts +const posts = await Post.findAll({ + limit: 20, + where: { + id: { + [Op.gt]: 242, + }, + }, +}) +``` + +> **Note**: Sequelize use the [Sequelize operators](https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#operators) to perform cursor pagination. + +Offset pagination: + +```ts +const posts = await Post.findAll({ + offset: 5, + limit: 10, +}) +``` + +### Creating objects + +**Prisma** + +```ts +const user = await prisma.user.create({ + data: { + email: 'alice@prisma.io', + }, +}) +``` + +**Sequelize** + + + +```ts +const user = User.build({ + name: 'Alice', + email: 'alice@prisma,io', +}) +await user.save() +``` + + +```ts +const user = await User.create({ + name: 'Alice', + email: 'alice@prisma,io', +}) +``` + + + +### Updating objects + +**Prisma** + +```ts +const user = await prisma.user.update({ + data: { + name: 'Alicia', + }, + where: { + id: 2, + }, +}) +``` + +**Sequelize** + + + +```ts +user.name = 'James' +user.email = ' alice@prisma.com' +await user.save() +``` + + +```ts +await User.update({ + name: 'James', + email: 'james@prisma.io', +}) +``` + + + +### Deleting objects + +**Prisma** + +```ts +const user = await prisma.user.delete({ + where: { + id: 10, + }, +}) +``` + +**Sequelize** + +```ts +await user.destroy() +``` + +### Batch updates + +**Prisma** + +```ts +const user = await prisma.user.updateMany({ + data: { + name: 'Published author!', + }, + where: { + email: { + contains: 'prisma.io', + }, + }, +}) +``` + +**Sequelize** + +```ts +const updatedUsers = await User.update({ + { role: "Admin" }, + where: { + email: { + [Op.like]: "%@prisma.io" + } + }, +}) +``` + +### Batch deletes + +**Prisma** + +```ts +const users = await prisma.user.deleteMany({ + where: { + id: { + in: [1, 2, 6, 6, 22, 21, 25], + }, + }, +}) +``` + +**Sequelize** + +```ts +await User.destroy({ + where: { + id: { + [Op.in]: [id1, id2, id3], + }, + }, +}) +``` + +### Transactions + +**Prisma** + +```ts +const user = await prisma.user.create({ + data: { + email: 'bob.rufus@prisma.io', + name: 'Bob Rufus', + Post: { + create: [ + { title: 'Working at Prisma' }, + { title: 'All about databases' }, + ], + }, + }, +}) +``` + +**Sequelize** + + + +```ts +return sequelize.$transaction(async (t) => { + const user = await User.create( + { + name: 'Alice', + email: 'alice@prisma,io', + }, + { + transaction: t, + } + ) + const post1 = await Post.create( + { + title: 'Join us for GraphQL Conf in 2019', + }, + { + transaction: t, + } + ) + const post2 = await Post.create( + { + title: 'Subscribe to GraphQL Weekly for GraphQL news', + }, + { + transaction: t, + } + ) + await user.setPosts([post1, post2]) +}) +``` + + +```ts +return sequelize.$transaction(async (transaction) => { + try { + const user = await User.create({ + name: 'Alice', + email: 'alice@prisma,io', + }) + const post1 = await Post.create({ + title: 'Join us for GraphQL Conf in 2019', + }) + const post2 = await Post.create({ + title: 'Subscribe to GraphQL Weekly for GraphQL news', + }) + await user.setPosts([post1, post2]) + } catch (e) { + return transaction.rollback() + } +}) +``` + + diff --git a/docs/200-orm/800-more/400-comparisons/03-prisma-and-mongoose.mdx b/docs/200-orm/800-more/400-comparisons/03-prisma-and-mongoose.mdx new file mode 100644 index 0000000000..a887bb21e7 --- /dev/null +++ b/docs/200-orm/800-more/400-comparisons/03-prisma-and-mongoose.mdx @@ -0,0 +1,288 @@ +--- +title: 'Mongoose' +metaTitle: 'Prisma vs Mongoose' +metaDescription: 'Learn how Prisma compares to Mongoose.' +--- + + + +This page compares the Prisma and [Mongoose](https://mongoosejs.com/docs/guide.html) APIs. If you want to learn how to migrate from Mongoose to Prisma, check out this [guide](/orm/more/migrating-to-prisma/migrate-from-mongoose). + + + +## Fetching single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, +}) +``` + +**Mongoose** + +```ts +const result = await User.findById(1) +``` + +## Fetching selected scalars of single objects + +**Prisma** + +```ts +const user = await prisma.user.findUnique({ + where: { + id: 1, + }, + select: { + name: true, + }, +}) +``` + +**Mongoose** + +```ts +const user = await User.findById(1).select(['name']) +``` + +## Fetching relations + +**Prisma** + + + +```ts +const userWithPost = await prisma.user.findUnique({ + where: { + id: 2, + }, + include: { + post: true, + }, +}) +``` + + +```ts +const userWithPost = await prisma.user + .findUnique({ + where: { + id: 2, + }, + }) + .post() +``` + + + +**Mongoose** + +```ts +const userWithPost = await User.findById(2).populate('post') +``` + +## Filtering for concrete values + +**Prisma** + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + contains: 'Hello World', + }, + }, +}) +``` + +**Mongoose** + +```ts +const posts = await Post.find({ + title: 'Hello World', +}) +``` + +## Other filter criteria + +**Prisma** + +Prisma generates many [additional filters](/orm/prisma-client/queries/filtering-and-sorting) that are commonly used in modern application development. + +**Mongoose** + +Mongoose exposes the [MongoDB query selectors](https://docs.mongodb.com/manual/reference/operator/query/#query-selectors) as filter criteria. + +## Relation filters + +**Prisma** + +Prisma lets you filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a _relation_ of that model. + +For example, the following query returns users with one or more posts with "Hello" in the title: + +```ts +const posts = await prisma.user.findMany({ + where: { + Post: { + some: { + title: { + contains: 'Hello', + }, + }, + }, + }, +}) +``` + +**Mongoose** + +Mongoose doesn't offer a dedicated API for relation filters. You can get similar functionality by adding an additional step to filter the results returned by the query. + +## Pagination + +**Prisma** + +Cursor-style pagination: + +```ts +const page = prisma.post.findMany({ + before: { + id: 242, + }, + last: 20, +}) +``` + +Offset pagination: + +```ts +const cc = prisma.post.findMany({ + skip: 200, + first: 20, +}) +``` + +**Mongoose** + +```ts +const posts = await Post.find({ + skip: 200, + limit: 20, +}) +``` + +## Creating objects + +**Prisma** + +```ts +const user = await prisma.user.create({ + data: { + name: 'Alice', + email: 'alice@prisma.io', + }, +}) +``` + +**Mongoose** + + + +```ts +const user = await User.create({ + name: 'Alice', + email: 'alice@prisma.io', +}) +``` + + +```ts +const user = new User({ + name: 'Alice', + email: 'alice@prisma.io', +}) +await user.save() +``` + + + +## Updating objects + +**Prisma** + +```ts +const user = await prisma.user.update({ + data: { + name: 'Alicia', + }, + where: { + id: 2, + }, +}) +``` + +**Mongoose** + + + +```ts +const updatedUser = await User.findOneAndUpdate( + { _id: 2 }, + { + $set: { + name: 'Alicia', + }, + } +) +``` + + +```ts +user.name = 'Alicia' +await user.save() +``` + + + +## Deleting objects + +**Prisma** + +```ts +const user = prisma.user.delete({ + where: { + id: 10, + }, +}) +``` + +**Mongoose** + +```ts +await User.deleteOne({ _id: 10 }) +``` + +## Batch deletes + +**Prisma** + +```ts +const users = await prisma.user.deleteMany({ + where: { + id: { + in: [1, 2, 6, 6, 22, 21, 25], + }, + }, +}) +``` + +**Mongoose** + +```ts +await User.deleteMany({ id: { $in: [1, 2, 6, 6, 22, 21, 25] } }) +``` diff --git a/docs/200-orm/800-more/400-comparisons/04-prisma-and-drizzle.mdx b/docs/200-orm/800-more/400-comparisons/04-prisma-and-drizzle.mdx new file mode 100644 index 0000000000..7f906ff804 --- /dev/null +++ b/docs/200-orm/800-more/400-comparisons/04-prisma-and-drizzle.mdx @@ -0,0 +1,472 @@ +--- +title: 'Drizzle' +metaTitle: 'Prisma vs Drizzle' +metaDescription: 'Learn how Prisma compares to Drizzle.' +--- + + + +This page compares Prisma and Drizzle. + + + +## Drizzle vs Prisma + +While Prisma and Drizzle solve similar problems, they work in very different ways and have individual pros and cons. Which one to choose will depend on the needs of your project and the exact tradeoffs that are important for it. + +**Drizzle** is a traditional SQL query builder that lets you compose SQL queries with JavaScript/TypeScript functions. It can be used to query a database or run migrations. Drizzle also offers a Queries API, which offers a higher level abstraction from SQL and can be used to read nested relations. Drizzle schema is defined in TypeScript files, which are used to generate SQL migrations and are then executed against a database. + +**Prisma** ORM mitigates many problems of traditional ORMs, such as bloated model instances, mixing business with storage logic, lack of type-safety or unpredictable queries caused e.g. by lazy loading. It uses the [Prisma schema](/orm/prisma-schema) to define application models in a declarative way. Prisma Migrate then allows the generation of SQL migrations from the Prisma schema and executes them against the database. CRUD queries are provided by Prisma Client, a lightweight and entirely type-safe database client for Node.js and TypeScript. + +## API design & Level of abstraction + +Drizzle and Prisma operate on different levels of abstraction. Drizzle's philosophy is "If you know SQL, you know Drizzle ORM". It mirrors SQL in its API while Prisma Client provides a higher-level abstraction that was designed with the common tasks of application developers in mind. Prisma's API design heavily leans on the idea of [making the right thing easy](https://www.jason.af/right-thing-easy-thing/). + +While Prisma Client operates on a higher level of abstraction, you are able to drop down to [raw SQL](/orm/prisma-client/queries/raw-database-access/raw-queries) at any time. However, full use of Prisma ORM and development of your application does not require SQL knowledge. Prisma's goal is to construct a query syntax focused on developer experience and productivity that feels familiar to developers. You can learn more about this here: [Why Prisma](/orm/overview/introduction/why-prisma#application-developers-should-care-about-data--not-sql). + +The following sections examine a few examples of how Prisma's and Drizzle's APIs differ in certain scenarios and what the rationale of Prisma's API design is in these cases. + +### Data modeling + +Prisma models are defined in the [Prisma schema](/orm/prisma-schema), while Drizzle uses TypeScript functions for table definitions. These functions are then exported and used in queries. + +Prisma generates a lightweight database client that exposes a tailored and fully type-safe API to read and write data for the models that are defined in the Prisma schema, following the DataMapper ORM pattern. + +Prisma's DSL for data modeling is lean, simple and intuitive to use. When modeling data in VS Code, you can further take advantage of Prisma's powerful [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) with features like autocompletion, quick fixes, jump to definition and other benefits that increase developer productivity. On the other hand, Drizzle's use of TypeScript means that you can lean on the power of TypeScript for additional flexibility (via reused code, for example). + + + + + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) +} +``` + + + + + +```ts +import { + boolean, + integer, + pgTable, + serial, + text, + uniqueIndex, + varchar, +} from 'drizzle-orm/pg-core' + +export const users = pgTable('users', { + id: serial('id').primaryKey(), + name: varchar('name', { length: 256 }), + email: varchar('email', { length: 256 }).unique(), +}) + +export const posts = pgTable('posts', { + id: serial('id').primaryKey(), + title: varchar('title', { length: 256 }).notNull(), + content: text('content'), + published: boolean('published'), + authorId: integer('author_id').references(() => users.id), +}) +``` + + + + + +### Migrations + +Migrations work similarly between Drizzle and Prisma. Both tools follow the approach of generating SQL files based on the provided model definitions and provide a CLI to execute them against the database. The SQL files can be modified before the migrations are executed so that any custom database operation can be performed with either migration system. + +### Querying + +Plain queries are natural to construct in both Drizzle and Prisma. Using Drizzle's Queries API, the two approaches are very similar: + + + + + +```ts +// find all users +const allUsers = await prisma.user.findMany() + +// find a single user +const user = await prisma.user.findFirst({ + where: { id: 27 }, +}) + +// find a unique user +const user = await prisma.user.findUnique({ + where: { email: 'nilu@prisma.io' }, +}) +``` + + + + + +```ts +import { eq } from 'drizzle-orm' + +// find all users +const allUsers = await db.query.users.findMany() + +// find a single user +const user = await db.query.users.findFirst({ + where: eq(users.id, 1), +}) + +// find a unique post +const user = await db.query.users.findFirst({ + where: eq(users.email, 'nilu@prisma.io'), +}) +``` + + + + + +When performing a mutation, a `create`, `update`, or `delete`, the Drizzle Queries API is not available. In these cases, you will need to use Drizzle's SQL-like APIs: + + + + + +```ts +// create a user +const user = await prisma.user.create({ + data: { + name: 'Nilu', + email: 'nilu@prisma.io', + }, +}) + +// update a user +const user = await prisma.user.update({ + where: { email: 'nilu@prisma.io' }, + data: { name: 'Another Nilu' }, +}) + +// delete a user +const deletedUser = await prisma.user.delete({ + where: { email: 'nilu@prisma.io' }, +}) +``` + + + + + +```ts +// create a user +const user = await db.insert(users).values({ + name: 'Nilu', + email: 'nilu@prisma.io', +}) + +// update a user +const user = await db + .update(users) + .set({ name: 'Another Nilu' }) + .where(eq(users.email, 'nilu@prisma.io')) + .returning() + +// delete a user +const deletedUser = await db + .delete(users) + .where(eq(users.email, 'nilu@prisma.io')) + .returning() +``` + + + + + +### Relations + +Working with records that are connected via foreign keys can become very complex in SQL. Prisma's concept of [virtual relation field](/orm/prisma-schema/data-model/relations#relation-fields) enables an intuitive and convenient way for application developers to work with related data. Some benefits of Prisma's approach are: + +- traversing relationships via the fluent API ([docs](/orm/prisma-client/queries/relation-queries#fluent-api)) +- nested writes that enable updating/creating connected records ([docs](/orm/prisma-client/queries/relation-queries#nested-writes)) +- applying filters on related records ([docs](/orm/prisma-client/queries/relation-queries#relation-filters)) +- easy and type-safe querying of nested data without worrying about underlying SQL ([docs](/orm/prisma-client/queries/relation-queries#nested-reads)) +- creating nested TypeScript typings based on models and their relations ([docs](/orm/prisma-client/type-safety)) +- intuitive modeling of relations in the data model via relation fields ([docs](/orm/prisma-schema/data-model/relations)) +- implicit handling of relation tables (also sometimes called JOIN, link, pivot or junction tables) ([docs](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations)) + + + + + +```ts +const posts = await prisma.post.findMany({ + include: { + author: true, + }, +}) +``` + + + + + +```ts +const posts = await db.query.posts.findMany({ + with: { + author: true, + }, +}) +``` + + + + + +### Filtering + +Drizzle exposes the underlying filter and conditional operators for a given SQL dialect. Prisma on the other hand, provides a more [generic set of operators](/orm/prisma-client/queries/filtering-and-sorting#filter-conditions-and-operators) that are intuitive to use. + +A good example of how the filtering APIs of both Drizzle and Prisma differ is by looking at `string` filters. While Drizzle provides filters for `like` and `ilike`, Prisma provides more specific operators that developers can use, e.g.: `contains`, `startsWith` and `endsWith`. + + + + + +```ts +// case sensitive filter +const posts = await prisma.post.findMany({ + where: { + title: 'Hello World', + }, +}) + +// case insensitive filter +const posts = await prisma.post.findMany({ + where: { + title: 'Hello World', + mode: 'insensitive', + }, +}) +``` + + + + + +```ts +// case sensitive filter +const posts = await db + .select() + .from(posts) + .where(like(posts.title, 'Hello World')) + +// case insensitive filter +const posts = await db + .select() + .from(posts) + .where(ilike(posts.title, 'Hello World')) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + contains: 'Hello World', + }, + }, +}) +``` + + + + + +```ts +const posts = await db + .select() + .from(posts) + .where(ilike(posts.title, '%Hello World%')) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + startsWith: 'Hello World', + }, + }, +}) +``` + + + + + +```ts +const posts = await db + .select() + .from(posts) + .where(ilike(posts.title, 'Hello World%')) +``` + + + + + + + + + +```ts +const posts = await prisma.post.findMany({ + where: { + title: { + endsWith: 'Hello World', + }, + }, +}) +``` + + + + + +```ts +const posts = await db + .select() + .from(posts) + .where(ilike(posts.title, '%Hello World')) +``` + + + + + +### Pagination + +Drizzle only offers limit-offset pagination while Prisma conveniently provides dedicated APIs for both limit-offset but also cursor-based. You can learn more about both approaches in the [Pagination](/orm/prisma-client/queries/pagination) section of the docs. + + + + + +```ts +// limit-offset pagination +const postPage = await prisma.post.findMany({ + where: { + title: 'Hello World', + }, + skip: 6, + take: 3, +}) + +// cursor-based pagination +const postPage = await prisma.post.findMany({ + where: { + title: 'Hello World', + }, + cursor: { id: 7 }, + take: 3, +}) +``` + + + + + +```ts +// limit-offset pagination (cursor-based not currently possible) +const postPage = await db + .select() + .from(users) + .where(ilike(posts.title, 'Hello World%')) + .limit(3) + .offset(6) +``` + + + + + +### Observability + +Both Drizzle and Prisma have the ability to log queries and the underlying SQL generated. + +Prisma has additional features built into the client that help teams get a better understanding of their data usage. [Metrics](/orm/prisma-client/observability-and-logging/metrics) and [tracing](/orm/prisma-client/observability-and-logging/opentelemetry-tracing) are two features that can be enabled at any time and give you per query information. This information can be integrated with external tools so that you can track performance over time. + +## Additional products + +Both Drizzle and Prisma offer products alongside an ORM. Prisma Studio was released to allow users to interact with their database via a GUI and also allows for limited self-hosting for use within a team. Drizzle Studio was released to accomplish the same tasks. + +In addition to Prisma Studio, Prisma offers commercial products via the Prisma Data Platform: + +- [Prisma Accelerate](https://prisma.io/accelerate): A connection pooler and global cache that integrates with Prisma ORM. Users can take advantage of connection pooling immediately and can control caching at an individual query level. +- [Prisma Pulse](https://prisma.io/pulse): A change data capture (CDC) service where Prisma Client can subscribe to database changes and receive them in real-time with little to no setup. + +These products work hand-in-hand with Prisma ORM to offer comprehensive data tooling, making building data-driven applications easy by following [Data DX](https://datadx.io/) principles. + +## Ecosystem + +Both Drizzle and Prisma have cases where users want to do something not directly supported by the library. Drizzle relies on the expressiveness of SQL to avoid these cases, while Prisma has built [Prisma Client extensions](/orm/prisma-client/client-extensions) to allow any user to add additional behaviors to their instance of Prisma Client. These extensions are also shareable, meaning teams can develop them for use across their projects or even for use by other teams. + +While Drizzle is a relatively new product, Prisma ORM was [released in 2021](https://www.prisma.io/blog/prisma-the-complete-orm-inw24qjeawmb) and is well established in the JavaScript/TypeScript space. It has proven value , many companies trust [Prisma in production](http://prisma.io/showcase). + +Prisma is also included as the data layer tool of choice in many meta-frameworks and development platforms like [Amplication](https://amplication.com/), [Wasp](https://wasp-lang.dev/), [RedwoodJS](https://redwoodjs.com/), [KeystoneJS](https://keystonejs.com/), [Remix](https://remix.run/) and the [t3 stack](https://create.t3.gg/). + +Thanks to its maturity, Prisma's community has developed a [plethora of useful tools](https://www.prisma.io/ecosystem) that helps with various Prisma workflows. Here are a few highlights: + +- [`zenstack`](https://zenstack.dev/): Toolkit that extends Prisma with access control policies in the Prisma schema, auto-generated CRUD APIs and frontend query hooks. +- [`prisma-erd-generator`](https://github.com/keonik/prisma-erd-generator#prisma-entity-relationship-diagram-generator): Visualizes the Prisma schema as an entity-relationship-diagram (ERD). +- [`prisma-zod-generator`](https://github.com/omar-dulaimi/prisma-zod-generator): Generates [Zod](https://github.com/colinhacks/zod) schemas from the Prisma schema. +- [`bridg`](https://github.com/joeroddy/bridg): Let's you access your database from the frontend using Prisma Client. +- [`jest-prisma`](https://github.com/Quramy/jest-prisma): Environment for Prisma integrated testing with [Jest](https://jestjs.io/). +- [`prisma-pothos-types`](https://github.com/hayes/pothos/tree/main/packages/plugin-prisma): Creates GraphQL types based on Prisma models when using [GraphQL Pothos](https://github.com/hayes/pothos/tree/main). +- [`prisma-trpc-generator`](https://github.com/omar-dulaimi/prisma-trpc-generator): Creates [tRPC](https://trpc.io/) routers from your Prisma schema. + +## Database support + +Both Drizzle and Prisma support multiple and different kinds of databases. Drizzle achieves this support through driver implementations created by Drizzle, which integrate with existing third-party database drivers. + +Prisma has begun adding support for [third-party database drivers](https://www.prisma.io/blog/serverless-database-drivers-KML1ehXORxZV), but primarily uses [built-in drivers](/orm/more/under-the-hood/engines#the-query-engine-at-runtime) to communicate with an underlying database. Prisma also defaults connections to TLS, which improves security. + +Additionally, Prisma supports CockroachDB, Microsoft SQL Server, and MongoDB, which Drizzle does not currently support. Prisma also offers the [relation mode](/orm/prisma-schema/data-model/relations/relation-mode) that allows Prisma to emulate foreign key constraints for those database engines that do not support it. Drizzle currently supports Cloudflare D1, `bun:sqlite`, and SQLite via HTTP Proxy, which Prisma does not. + +## Conclusion + +Both Drizzle ORM and Prisma ORM are tools for data access and migrations. Drizzle is focused on being a thin wrapper around a SQL-like syntax while Prisma is focused on a convenient and expressive API. Other important differences include Prisma's support of MSSQL and MongoDB, support for additional features via [Prisma Client extensions](/orm/prisma-client/client-extensions), additional cloud-ready products, and a robust ecosystem. + +For teams that use SQL daily, Drizzle offers a convenient wrapper that will feel familiar and is type-safe. Prisma's `$queryRaw` feature, while available, does not provide the same level of type safety. However, the experience of writing raw SQL in Prisma ORM can be enhanced using the [SafeQL](https://safeql.dev/compatibility/prisma.html) community plugin for Prisma, offering syntax highlighting and type-checking in your editor as well. + +On the other hand, for teams that are a mix of developers (front-end, back-end, and full-stack) that have varying levels of experience with databases, Prisma offers a comprehensive and easy-to-learn approach for data access and managing database schemas. diff --git a/docs/200-orm/800-more/400-comparisons/index.mdx b/docs/200-orm/800-more/400-comparisons/index.mdx new file mode 100644 index 0000000000..115bb88b9f --- /dev/null +++ b/docs/200-orm/800-more/400-comparisons/index.mdx @@ -0,0 +1,17 @@ +--- +title: 'Comparing Prisma' +metaTitle: 'Comparing Prisma to other ORMs and ODMs.' +metaDescription: 'Learn how Prisma compares to other ORMs, ORMs and database libraries, like TypeORM, Sequelize and Mongoose.' +--- + + + +Find out how Prisma compares to ORMs and ODMs in the Node.js and TypeScript ecosystem. + +For a comprehensive overview of the most popular database libraries, read this article: [Top Node.js ORMs, Query Builders & Database Libraries in 2022](https://www.prisma.io/dataguide/database-tools/top-nodejs-orms-query-builders-and-database-libraries). + + + +## In this section + + diff --git a/docs/200-orm/800-more/450-migrating-to-prisma/01-migrate-from-typeorm.mdx b/docs/200-orm/800-more/450-migrating-to-prisma/01-migrate-from-typeorm.mdx new file mode 100644 index 0000000000..f52c3a944f --- /dev/null +++ b/docs/200-orm/800-more/450-migrating-to-prisma/01-migrate-from-typeorm.mdx @@ -0,0 +1,1122 @@ +--- +title: 'Migrate from TypeORM' +metaTitle: 'How to migrate from TypeORM to Prisma' +metaDescription: 'Learn how to migrate from TypeORM to Prisma' +--- + + + +This guide describes how to migrate from TypeORM to Prisma. It uses an extended version of the [TypeORM Express example](https://github.com/typeorm/typescript-express-example/) as a [sample project](https://github.com/prisma/migrate-from-typeorm-to-prisma) to demonstrate the migration steps. You can find the example used for this guide on [GitHub](https://github.com/prisma/migrate-from-typeorm-to-prisma). + +This migration guide uses PostgreSQL as the example database, but it equally applies to any other relational database that's [supported by Prisma](/orm/reference/supported-databases). + +You can learn how Prisma compares to TypeORM on the [Prisma vs TypeORM](/orm/more/comparisons/prisma-and-typeorm) page. + + + +## Overview of the migration process + +Note that the steps for migrating from TypeORM to Prisma are always the same, no matter what kind of application or API layer you're building: + +1. Install the Prisma CLI +1. Introspect your database +1. Create a baseline migration +1. Install Prisma Client +1. Gradually replace your TypeORM queries with Prisma Client + +These steps apply, no matter if you're building a REST API (e.g. with Express, koa or NestJS), a GraphQL API (e.g. with Apollo Server, TypeGraphQL or Nexus) or any other kind of application that uses TypeORM for database access. + +Prisma lends itself really well for **incremental adoption**. This means, you don't have migrate your entire project from TypeORM to Prisma at once, but rather you can _step-by-step_ move your database queries from TypeORM to Prisma. + +## Overview of the sample project + +For this guide, we'll use a REST API built with Express as a [sample project](https://github.com/prisma/migrate-from-typeorm-to-prisma) to migrate to Prisma. It has four models/entities: + + + + + +```ts +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number + + @Column({ nullable: true }) + name: string + + @Column({ unique: true }) + email: string + + @OneToMany((type) => Post, (post) => post.author) + posts: Post[] + + @OneToOne((type) => Profile, (profile) => profile.user, { cascade: true }) + profile: Profile +} +``` + + + + + +```ts +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @Column() + title: string + + @Column({ nullable: true }) + content: string + + @Column({ default: false }) + published: boolean + + @ManyToOne((type) => User, (user) => user.posts) + author: User + + @ManyToMany((type) => Category, (category) => category.posts) + @JoinTable() + categories: Category[] +} +``` + + + + + +```ts +@Entity() +export class Profile { + @PrimaryGeneratedColumn() + id: number + + @Column({ nullable: true }) + bio: string + + @OneToOne((type) => User, (user) => user.profile) + @JoinColumn() + user: User +} +``` + + + + + +```ts +@Entity() +export class Category { + @PrimaryGeneratedColumn() + id: number + + @Column() + name: string + + @ManyToMany((type) => Post, (post) => post.categories) + posts: Post[] +} +``` + + + + + +The models have the following relations: + +- 1-1: `User` ↔ `Profile` +- 1-n: `User` ↔ `Post` +- m-n: `Post` ↔ `Category` + +The corresponding tables have been created using a generated TypeORM migration. + +
+ +Expand to view details of the migration + +The migration has been created using + +```terminal +typeorm migration:generate -n Init +``` + +This created the following migration file: + +```ts file=migrations/1605698662257-Init.ts +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class Init1605698662257 implements MigrationInterface { + name = 'Init1605698662257' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "profile" ("id" SERIAL NOT NULL, "bio" character varying, "userId" integer, CONSTRAINT "REL_a24972ebd73b106250713dcddd" UNIQUE ("userId"), CONSTRAINT "PK_3dd8bfc97e4a77c70971591bdcb" PRIMARY KEY ("id"))` + ) + await queryRunner.query( + `CREATE TABLE "user" ("id" SERIAL NOT NULL, "name" character varying, "email" character varying NOT NULL, CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))` + ) + await queryRunner.query( + `CREATE TABLE "post" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "content" character varying, "published" boolean NOT NULL DEFAULT false, "authorId" integer, CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))` + ) + await queryRunner.query( + `CREATE TABLE "category" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_9c4e4a89e3674fc9f382d733f03" PRIMARY KEY ("id"))` + ) + await queryRunner.query( + `CREATE TABLE "post_categories_category" ("postId" integer NOT NULL, "categoryId" integer NOT NULL, CONSTRAINT "PK_91306c0021c4901c1825ef097ce" PRIMARY KEY ("postId", "categoryId"))` + ) + await queryRunner.query( + `CREATE INDEX "IDX_93b566d522b73cb8bc46f7405b" ON "post_categories_category" ("postId") ` + ) + await queryRunner.query( + `CREATE INDEX "IDX_a5e63f80ca58e7296d5864bd2d" ON "post_categories_category" ("categoryId") ` + ) + await queryRunner.query( + `ALTER TABLE "profile" ADD CONSTRAINT "FK_a24972ebd73b106250713dcddd9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` + ) + await queryRunner.query( + `ALTER TABLE "post" ADD CONSTRAINT "FK_c6fb082a3114f35d0cc27c518e0" FOREIGN KEY ("authorId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` + ) + await queryRunner.query( + `ALTER TABLE "post_categories_category" ADD CONSTRAINT "FK_93b566d522b73cb8bc46f7405bd" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ) + await queryRunner.query( + `ALTER TABLE "post_categories_category" ADD CONSTRAINT "FK_a5e63f80ca58e7296d5864bd2d3" FOREIGN KEY ("categoryId") REFERENCES "category"("id") ON DELETE CASCADE ON UPDATE NO ACTION` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "post_categories_category" DROP CONSTRAINT "FK_a5e63f80ca58e7296d5864bd2d3"` + ) + await queryRunner.query( + `ALTER TABLE "post_categories_category" DROP CONSTRAINT "FK_93b566d522b73cb8bc46f7405bd"` + ) + await queryRunner.query( + `ALTER TABLE "post" DROP CONSTRAINT "FK_c6fb082a3114f35d0cc27c518e0"` + ) + await queryRunner.query( + `ALTER TABLE "profile" DROP CONSTRAINT "FK_a24972ebd73b106250713dcddd9"` + ) + await queryRunner.query(`DROP INDEX "IDX_a5e63f80ca58e7296d5864bd2d"`) + await queryRunner.query(`DROP INDEX "IDX_93b566d522b73cb8bc46f7405b"`) + await queryRunner.query(`DROP TABLE "post_categories_category"`) + await queryRunner.query(`DROP TABLE "category"`) + await queryRunner.query(`DROP TABLE "post"`) + await queryRunner.query(`DROP TABLE "user"`) + await queryRunner.query(`DROP TABLE "profile"`) + } +} +``` + +
+ +As mentioned before, this guide is an extended variation of the TypeORM Express example and uses the same file structure. The route handlers are located in the `src/controller` directory. From there, they are pulled into a central `src/routes.ts` file which is used to set up the required routes in `src/index.ts`: + +``` +└── blog-typeorm + ├── ormconfig.json + ├── package.json + ├── src + │   ├── controllers + │   │   ├── AddPostToCategoryAction.ts + │   │   ├── CreateDraftAction.ts + │   │   ├── CreateUserAction.ts + │   │   ├── FeedAction.ts + │   │   ├── FilterPostsAction.ts + │   │   ├── GetPostByIdAction.ts + │   │   └── SetBioForUserAction.ts + │   ├── entity + │   │   ├── Category.ts + │   │   ├── Post.ts + │   │   ├── Profile.ts + │   │   └── User.ts + │   ├── index.ts + │   ├── migration + │   │   └── 1605698662257-Init.ts + │   └── routes.ts + └── tsconfig.json +``` + +## Step 1. Install the Prisma CLI + +The first step to adopt Prisma is to [install the Prisma CLI](/orm/tools/prisma-cli#installation) in your project: + +```terminal copy +npm install prisma --save-dev +``` + +## Step 2. Introspect your database + +### 2.1. Set up Prisma + +Before you can introspect your database, you need to set up your [Prisma schema](/orm/prisma-schema) and connect Prisma to your database. Run the following command in your terminal to create a basic Prisma schema file: + +```terminal copy +npx prisma init +``` + +This command created a new directory called `prisma` with the following files for you: + +- `schema.prisma`: Your Prisma schema file that specifies your database connection and models +- `.env`: A [`dotenv`](https://github.com/motdotla/dotenv) to configure your database connection URL as an environment variable + +The Prisma schema file currently looks as follows: + +```prisma file=prisma/schema.prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +:::tip + +If you're using VS Code, be sure to install the [Prisma VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) for syntax highlighting, formatting, auto-completion and a lot more cool features. + +::: + +### 2.2. Connect your database + +If you're not using PostgreSQL, you need to adjust the `provider` field on the `datasource` block to the database you currently use: + + + + + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma file=schema.prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma file=schema.prisma +datasource db { + provider = "sqlserver" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma file=schema.prisma +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} +``` + + + + + +Once that's done, you can configure your [database connection URL](/orm/reference/connection-urls) in the `.env` file. Here's how the database connection from TypeORM maps to the connection URL format used by Prisma: + + + + + +Assume you have the following database connection details in `ormconfig.json`: + +```json file=ormconfig.json +{ + "type": "postgres", + "host": "localhost", + "port": 5432, + "username": "alice", + "password": "myPassword42", + "database": "blog-typeorm" +} +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-typeorm" +``` + +Note that you can optionally configure the PostgreSQL [schema](https://www.postgresql.org/docs/9.1/ddl-schemas.html) by appending the `schema` argument to the connection URL: + +```env file=.env +DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-typeorm?schema=myschema" +``` + +If not provided, the default schema called `public` is being used. + + + + + +Assume you have the following database connection details in `ormconfig.json`: + +```json file=ormconfig.json +{ + "type": "mysql", + "host": "localhost", + "port": 3306, + "username": "alice", + "password": "myPassword42", + "database": "blog-typeorm" +} +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="mysql://alice:myPassword42@localhost:3306/blog-typeorm" +``` + + + + + +Assume you have the following database connection details in `ormconfig.json`: + +```json file=ormconfig.json +{ + "type": "mssql", + "host": "localhost", + "port": 1433, + "username": "alice", + "password": "myPassword42", + "database": "blog-typeorm" +} +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="sqlserver://localhost:1433;database=blog-typeorm;user=alice;password=myPassword42;trustServerCertificate=true" +``` + + + + + +Assume you have the following database connection details in `ormconfig.json`: + +```json file=ormconfig.json +{ + "type": "sqlite", + "database": "blog-typeorm" +} +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="file:./blog-typeorm.db" +``` + + + + + +### 2.3. Introspect your database using Prisma + +With your connection URL in place, you can [introspect](/orm/prisma-schema/introspection) your database to generate your Prisma models: + +```terminal copy +npx prisma db pull +``` + +This creates the following Prisma models: + +```prisma file=prisma/schema.prisma +model typeorm_migrations { + id Int @id @default(autoincrement()) + timestamp Int + name String + + @@map("_typeorm_migrations") +} + +model category { + id Int @id @default(autoincrement()) + name String + post_categories_category post_categories_category[] +} + +model post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + user user? @relation(fields: [authorId], references: [id]) + post_categories_category post_categories_category[] +} + +model post_categories_category { + postId Int + categoryId Int + category category @relation(fields: [categoryId], references: [id]) + post post @relation(fields: [postId], references: [id]) + + @@id([postId, categoryId]) + @@index([postId], name: "IDX_93b566d522b73cb8bc46f7405b") + @@index([categoryId], name: "IDX_a5e63f80ca58e7296d5864bd2d") +} + +model profile { + id Int @id @default(autoincrement()) + bio String? + userId Int? @unique + user user? @relation(fields: [userId], references: [id]) +} + +model user { + id Int @id @default(autoincrement()) + name String? + email String @unique + post post[] + profile profile? +} +``` + +The generated Prisma models represent your database tables and are the foundation for your programmatic Prisma Client API which allows you to send queries to your database. + +### 2.4. Create a baseline migration + +To continue using Prisma Migrate to evolve your database schema, you will need to [baseline your database](/orm/prisma-migrate/getting-started). + +First, create a `migrations` directory and add a directory inside with your preferred name for the migration. In this example, we will use `0_init` as the migration name: + +```terminal +mkdir -p prisma/migrations/0_init +``` + +Next, generate the migration file with `prisma migrate diff`. Use the following arguments: + +- `--from-empty`: assumes the data model you're migrating from is empty +- `--to-schema-datamodel`: the current database state using the URL in the `datasource` block +- `--script`: output a SQL script + +```terminal wrap +npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql +``` + +Review the generated migration to ensure everything is correct. + +Next, mark the migration as applied using `prisma migrate resolve` with the `--applied` argument. + +```terminal +npx prisma migrate resolve --applied 0_init +``` + +The command will mark `0_init` as applied by adding it to the `_prisma_migrations` table. + +You now have a baseline for your current database schema. To make further changes to your database schema, you can update your Prisma schema and use `prisma migrate dev` to apply the changes to your database. + +### 2.5. Adjust the Prisma schema (optional) + +The models that were generated via introspection currently _exactly_ map to your database tables. In this section, you'll learn how you can adjust the naming of the Prisma models to adhere to [Prisma's naming conventions](/orm/reference/prisma-schema-reference#naming-conventions). + +All of these adjustment are entirely optional and you are free to skip to the next step already if you don't want to adjust anything for now. You can go back and make the adjustments at any later point. + +As opposed to the current snake_case notation of TypeORM models, Prisma's naming conventions are: + +- PascalCase for model names +- camelCase for field names + +You can adjust the naming by _mapping_ the Prisma model and field names to the existing table and column names in the underlying database using `@@map` and `@map`. + +Also note that you can rename [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) to optimize the Prisma Client API that you'll use later to send queries to your database. For example, the `post` field on the `user` model is a _list_, so a better name for this field would be `posts` to indicate that it's plural. + +You can further completely remove model that represents the TypeORM migrations table (called `_typeorm_migrations` here) from the Prisma schema. + +Here's an adjusted version of the Prisma schema that addresses these points: + +```prisma file=prisma/schema.prisma +model Category { + id Int @id @default(autoincrement()) + name String + postsToCategories PostToCategories[] + + @@map("category") +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) + postsToCategories PostToCategories[] + + @@map("post") +} + +model PostToCategories { + postId Int + categoryId Int + category Category @relation(fields: [categoryId], references: [id]) + post Post @relation(fields: [postId], references: [id]) + + @@id([postId, categoryId]) + @@index([postId], name: "IDX_93b566d522b73cb8bc46f7405b") + @@index([categoryId], name: "IDX_a5e63f80ca58e7296d5864bd2d") + @@map("post_categories_category") +} + +model Profile { + id Int @id @default(autoincrement()) + bio String? + userId Int? @unique + user User? @relation(fields: [userId], references: [id]) + + @@map("profile") +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + posts Post[] + profile Profile? + + @@map("user") +} +``` + +## Step 3. Install Prisma Client + +As a next step, you can install Prisma Client in your project so that you can start replacing the database queries in your project that are currently made with TypeORM: + +```terminal +npm install @prisma/client +``` + +## Step 4. Replace your TypeORM queries with Prisma Client + +In this section, we'll show a few sample queries that are being migrated from TypeORM to Prisma Client based on the example routes from the sample REST API project. For a comprehensive overview of how the Prisma Client API differs from TypeORM, check out the [API comparison](/orm/more/comparisons/prisma-and-typeorm#api-comparison) page. + +First, to set up the `PrismaClient` instance that you'll use to send database queries from the various route handlers. Create a new file named `prisma.ts` in the `src` directory: + +```terminal copy +touch src/prisma.ts +``` + +Now, instantiate `PrismaClient` and export it from the file so you can use it in your route handlers later: + +```ts copy file=src/prisma.ts +import { PrismaClient } from '@prisma/client' + +export const prisma = new PrismaClient() +``` + +### 4.1. Replacing queries in `GET` requests + +The REST API has three routes that accept `GET` requests: + +- `/feed`: Return all published posts +- `/filterPosts?searchString=SEARCH_STRING`: Filter returned posts by `SEARCH_STRING` +- `/post/:postId`: Returns a specific post + +Let's dive into the route handlers that implement these requests. + +#### `/feed` + +The `/feed` handler is currently implemented as follows: + +```ts file=src/controllers/FeedAction.ts +import { getManager } from 'typeorm' +import { Post } from '../entity/Post' + +export async function feedAction(req, res) { + const postRepository = getManager().getRepository(Post) + + const publishedPosts = await postRepository.find({ + where: { published: true }, + relations: ['author'], + }) + + res.send(publishedPosts) +} +``` + +Note that each returned `Post` object includes the relation to the `author` it's associated with. With TypeORM, including the relation is not type-safe. For example, if there was a typo in the relation that is retrieved, your database query would fail only at _runtime_ – the TypeScript compiler does not provide any safety here. + +Here is how the same route is implemented using Prisma Client: + +```ts file=src/controllers/FeedAction.ts +import { prisma } from '../prisma' + +export async function feedAction(req, res) { + const publishedPosts = await prisma.post.findMany({ + where: { published: true }, + include: { author: true }, + }) + + res.send(publishedPosts) +} +``` + +Note that the way how Prisma Client includes the `author` relation is absolutely type-safe. The TypeScript compiler would throw an error if you were trying to include a relation that does not exist on the `Post` model. + +#### `/filterPosts?searchString=SEARCH_STRING` + +The `/filterPosts` handler is currently implemented as follows: + +```ts file=src/controllers/FilterPostsActions.ts +import { getManager, Like } from 'typeorm' +import { Post } from '../entity/Post' + +export async function filterPostsAction(req, res) { + const { searchString } = req.query + const postRepository = getManager().getRepository(Post) + + const filteredPosts = await postRepository.find({ + where: [ + { title: Like(`%${searchString}%`) }, + { content: Like(`%${searchString}%`) }, + ], + }) + + res.send(filteredPosts) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/FilterPostsActions.ts +import { prisma } from '../prisma' + +export async function filterPostsAction(req, res) { + const { searchString } = req.query + + const filteredPosts = prisma.post.findMany({ + where: { + OR: [ + { + title: { contains: searchString }, + }, + { + content: { contains: searchString }, + }, + ], + }, + }) + + res.send(filteredPosts) +} +``` + +Note that TypeORM by default combines several `where` conditions with an implicit `OR` operator. Prisma on the other hand [combines several `where` conditions with an implicit `AND` operator](/orm/reference/prisma-client-reference#get-all-post-records-where-the-content-field-contains-prisma-and-published-is-false-no-and), so in this case the Prisma query needs to make the `OR` explicit. + +#### `/post/:postId` + +The `/post/:postId` handler is currently implemented as follows: + +```ts file=src/controllers/GetPostByIdAction.ts +import { getManager } from 'typeorm' +import { Post } from '../entity/Post' + +export async function getPostByIdAction(req, res) { + const { postId } = req.params + const postRepository = getManager().getRepository(Post) + + const post = await postRepository.findOne(postId) + + res.send(post) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/GetPostByIdAction.ts +import { prisma } from '../prisma' + +export async function getPostByIdAction(req, res) { + const { postId } = req.params + + const post = await prisma.post.findUnique({ + where: { id: postId }, + }) + + res.send(post) +} +``` + +### 4.2. Replacing queries in `POST` requests + +The REST API has three routes that accept `POST` requests: + +- `/user`: Creates a new `User` record +- `/post`: Creates a new `Post` record +- `/user/:userId/profile`: Creates a new `Profile` record for a `User` record with a given ID + +#### `/user` + +The `/user` handler is currently implemented as follows: + +```ts file=src/controllers/CreateUserAction.ts +import { getManager } from 'typeorm' +import { User } from '../entity/User' + +export async function createUserAction(req, res) { + const { name, email } = req.body + + const userRepository = getManager().getRepository(User) + + const newUser = new User() + newUser.name = name + newUser.email = email + userRepository.save(newUser) + + res.send(newUser) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/CreateUserAction.ts +import { prisma } from '../prisma' + +export async function createUserAction(req, res) { + const { name, email } = req.body + + const newUser = await prisma.user.create({ + data: { + name, + email, + }, + }) + + res.send(newUser) +} +``` + +#### `/post` + +The `/post` handler is currently implemented as follows: + +```ts file=src/controllers/CreateDraftAction.ts +import { getManager } from 'typeorm' +import { Post } from '../entity/Post' +import { User } from '../entity/User' + +export async function createDraftAction(req, res) { + const { title, content, authorEmail } = req.body + + const userRepository = getManager().getRepository(User) + const user = await userRepository.findOne({ email: authorEmail }) + + const postRepository = getManager().getRepository(Post) + + const newPost = new Post() + newPost.title = title + newPost.content = content + newPost.author = user + postRepository.save(newPost) + + res.send(newPost) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/CreateDraftAction.ts +import { prisma } from '../prisma' + +export async function createDraftAction(req, res) { + const { title, content, authorEmail } = req.body + + const newPost = await prisma.post.create({ + data: { + title, + content, + author: { + connect: { email: authorEmail }, + }, + }, + }) + + res.send(newPost) +} +``` + +Note that Prisma Client's nested write here save an initial query where first the `User` record needs to be retrieved by its `email`. That's because, with Prisma you can connect records in relations using any unique property. + +#### `/user/:userId/profile` + +The `/user/:userId/profile` handler is currently implemented as follows: + +```ts file=src/controllers/SetBioForUserAction.ts.ts +import { getManager } from 'typeorm' +import { Profile } from '../entity/Profile' +import { User } from '../entity/User' + +export async function setBioForUserAction(req, res) { + const { userId } = req.params + const { bio } = req.body + + const userRepository = getManager().getRepository(User) + const user = await userRepository.findOne(userId, { + relations: ['profile'], + }) + + const profileRepository = getManager().getRepository(Profile) + user.profile.bio = bio + + profileRepository.save(user.profile) + + res.send(user) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/SetBioForUserAction.ts.ts +import { prisma } from '../prisma' + +export async function setBioForUserAction(req, res) { + const { userId } = req.params + const { bio } = req.body + + const user = await prisma.user.update({ + where: { id: userId }, + data: { + profile: { + update: { + bio, + }, + }, + }, + }) + + res.send(user) +} +``` + +### 4.3. Replacing queries in `PUT` requests + +The REST API has one route that accept a `PUT` request: + +- `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID`: Adds the post with `POST_ID` to the category with `CATEGORY_ID` + +Let's dive into the route handlers that implement these requests. + +#### `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID` + +The `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID` handler is currently implemented as follows: + +```ts file=src/controllers/AddPostToCategoryAction.ts +import { getManager } from 'typeorm' +import { Post } from '../entity/Post' +import { Category } from '../entity/Category' + +export async function addPostToCategoryAction(req, res) { + const { postId, categoryId } = req.query + + const postRepository = getManager().getRepository(Post) + const post = await postRepository.findOne(postId, { + relations: ['categories'], + }) + + const categoryRepository = getManager().getRepository(Category) + const category = await categoryRepository.findOne(categoryId) + + post.categories.push(category) + postRepository.save(post) + + res.send(post) +} +``` + +With Prisma, the route is implemented as follows: + +```ts file=src/controllers/AddPostToCategoryAction.ts +import { prisma } from '../prisma' + +export async function addPostToCategoryAction(req, res) { + const { postId, categoryId } = req.query + + const post = await prisma.post.update({ + data: { + postsToCategories: { + create: { + category: { + connect: { id: categoryId }, + }, + }, + }, + }, + where: { + id: postId, + }, + }) + + res.send(post) +} +``` + +Note that this Prisma Client can be made less verbose by modeling the relation as an [implicit many-to-many relation](#implicit-many-to-many-relations) instead. In that case, the query would look as follows: + +```ts file=src/controllers/AddPostToCategoryAction.ts +const post = await prisma.post.update({ + data: { + categories: { + connect: { id: categoryId }, + }, + }, + where: { id: postId }, +}) +``` + +## More + +### Implicit many-to-many relations + +Similar to the `@manyToMany` decorator in TypeORM, Prisma allows you to [model many-to-many relations _implicitly_](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations). That is, a many-to-many relation where you do not have to manage the [relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) (also sometimes called JOIN table) _explicitly_ in your schema. Here is an example with TypeORM: + +```ts +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToMany, + JoinTable, +} from 'typeorm' +import { Category } from './Category' + +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number + + @ManyToMany((type) => Category, (category) => category.posts) + @JoinTable() + categories: Category[] +} +``` + +```ts +import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from 'typeorm' +import { Post } from './Post' + +@Entity() +export class Category { + @PrimaryGeneratedColumn() + id: number + + @ManyToMany((type) => Post, (post) => post.categories) + posts: Post[] +} +``` + +If you generate and run a migration with TypeORM based on these models, TypeORM will automatically create the following relation table for you: + +```sql +-- Table Definition ---------------------------------------------- +CREATE TABLE post_categories_category ( + "postId" integer REFERENCES post(id) ON DELETE CASCADE, + "categoryId" integer REFERENCES category(id) ON DELETE CASCADE, + CONSTRAINT "PK_91306c0021c4901c1825ef097ce" PRIMARY KEY ("postId", "categoryId") +); + +-- Indices ------------------------------------------------------- +CREATE UNIQUE INDEX "PK_91306c0021c4901c1825ef097ce" ON post_categories_category("postId" int4_ops,"categoryId" int4_ops); +CREATE INDEX "IDX_93b566d522b73cb8bc46f7405b" ON post_categories_category("postId" int4_ops); +CREATE INDEX "IDX_a5e63f80ca58e7296d5864bd2d" ON post_categories_category("categoryId" int4_ops); +``` + +If you introspect the database with Prisma, you'll get the following result in the Prisma schema (note that some relation field names have been adjusted to look friendlier compared to the raw version from introspection): + +```prisma file=schema.prisma +model Category { + id Int @id @default(autoincrement()) + name String + postsToCategories PostToCategories[] + + @@map("category") +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) + postsToCategories PostToCategories[] + + @@map("post") +} + +model PostToCategories { + postId Int + categoryId Int + category Category @relation(fields: [categoryId], references: [id]) + post Post @relation(fields: [postId], references: [id]) + + @@id([postId, categoryId]) + @@index([postId], name: "IDX_93b566d522b73cb8bc46f7405b") + @@index([categoryId], name: "IDX_a5e63f80ca58e7296d5864bd2d") + @@map("post_categories_category") +} +``` + +In this Prisma schema, the many-to-many relation is modeled _explicitly_ via the relation table `PostToCategories`. + +By adhering to the conventions for Prisma relation tables, the relation could look as follows: + +```prisma file=schema.prisma +model Category { + id Int @id @default(autoincrement()) + name String + posts Post[] + + @@map("category") +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) + categories Category[] + + @@map("post") +} +``` + +This would also result in a more ergonomic and less verbose Prisma Client API to modify the records in this relation, because you have a direct path from `Post` to `Category` (and the other way around) instead of needing to traverse the `PostToCategories` model first. + + + If your database provider requires tables to have primary keys then you have + to use explicit syntax, and manually create the join model with a primary key. + This is because relation tables (JOIN tables) created by Prisma (expressed via + `@relation`) for many-to-many relations using implicit syntax do not have + primary keys. + diff --git a/docs/200-orm/800-more/450-migrating-to-prisma/02-migrate-from-sequelize.mdx b/docs/200-orm/800-more/450-migrating-to-prisma/02-migrate-from-sequelize.mdx new file mode 100644 index 0000000000..835c9c478b --- /dev/null +++ b/docs/200-orm/800-more/450-migrating-to-prisma/02-migrate-from-sequelize.mdx @@ -0,0 +1,1261 @@ +--- +title: 'Migrate from Sequelize' +metaTitle: 'How to migrate from Sequelize to Prisma' +metaDescription: 'Learn how to migrate from Sequelize to Prisma' +--- + + + +This guide describes how to migrate from Sequelize to Prisma. It uses an extended version of the [Sequelize Express example](https://github.com/sequelize/express-example) as a [sample project](https://github.com/prisma/migrate-from-sequelize-to-prisma) to demonstrate the migration steps. You can find the example used for this guide on [GitHub](https://github.com/prisma/migrate-from-sequelize-to-prisma). + +This migration guide uses PostgreSQL as the example database, but it equally applies to any other relational database that's [supported by Prisma](/orm/reference/supported-databases). + +You can learn how Prisma compares to Sequelize on the [Prisma vs Sequelize](/orm/more/comparisons/prisma-and-sequelize) page. + + + +## Overview of the migration process + +Note that the steps for migrating from Sequelize to Prisma are always the same, no matter what kind of application or API layer you're building: + +1. Install the Prisma CLI +1. Introspect your database +1. Create a baseline migration +1. Install Prisma Client +1. Gradually replace your Sequelize queries with Prisma Client + +These steps apply, no matter if you're building a REST API (e.g. with Express, koa or NestJS), a GraphQL API (e.g. with Apollo Server, TypeGraphQL or Nexus) or any other kind of application that uses Sequelize for database access. + +Prisma lends itself really well for **incremental adoption**. This means, you don't have migrate your entire project from Sequelize to Prisma at once, but rather you can _step-by-step_ move your database queries from Sequelize to Prisma. + +## Overview of the sample project + +For this guide, we'll use a REST API built with Express as a [sample project](https://github.com/prisma/migrate-from-sequelize-to-prisma) to migrate to Prisma. It has four models/entities: + + + + + +```js +module.exports = (sequelize, DataTypes) => { + const User = sequelize.define('User', { + name: { + type: DataTypes.STRING, + }, + email: { + type: DataTypes.STRING, + unique: true, + allowNull: false, + }, + }) + + User.associate = (models) => { + User.hasMany(models.Post, { + foreignKey: 'authorId', + as: 'posts', + }) + User.hasOne(models.Profile, { + onDelete: 'CASCADE', + foreignKey: 'userId', + }) + } + return User +} +``` + + + + + +```js +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define('Post', { + title: { + type: DataTypes.STRING, + allowNull: false, + }, + content: { + type: DataTypes.STRING, + }, + published: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + }) + Post.associate = (models) => { + Post.belongsTo(models.User, { + foreignKey: 'authorId', + as: 'author', + }) + Post.belongsToMany(models.Category, { + through: 'PostCategories', + as: 'categories', + }) + } + return Post +} +``` + + + + + +```js +module.exports = (sequelize, DataTypes) => { + const Profile = sequelize.define('Profile', { + bio: { + type: DataTypes.STRING, + allowNull: false, + }, + }) + Profile.associate = (models) => { + Profile.belongsTo(models.User, { + foreignKey: 'userId', + as: 'user', + }) + } + return Profile +} +``` + + + + + +```js +module.exports = (sequelize, DataTypes) => { + const Category = sequelize.define('Category', { + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }) + Category.associate = (models) => { + Category.belongsToMany(models.Post, { + through: 'PostCategories', + as: 'posts', + }) + } + return Category +} +``` + + + + + +The models have the following relations: + +- 1-1: `User` ↔ `Profile` +- 1-n: `User` ↔ `Post` +- m-n: `Post` ↔ `Category` + +The corresponding tables have been created using a generated Sequelize migration. + +In this guide, the route handlers are located in the `src/controllers` directory. The models are located in the `src/models` directory. From there, they are pulled into a central `src/routes.js` file which is used to set up the required routes in `src/index.js`: + +``` +└── blog-sequelize + ├── package.json + └──src +    ├── controllers +    │   ├── post.js +    │   └── user.js +    ├── models +    │   ├── Category.js +    │   ├── Post.js +    │   ├── Profile.js +    │   └── User.js +    ├── index.js +    └── routes.js +``` + +## Step 1. Install the Prisma CLI + +The first step to adopt Prisma is to [install the Prisma CLI](/orm/tools/prisma-cli#installation) in your project: + +```terminal copy +npm install prisma --save-dev +``` + +## Step 2. Introspect your database + +### 2.1. Set up Prisma + +Before you can introspect your database, you need to set up your [Prisma schema](/orm/prisma-schema) and connect Prisma to your database. Run the following command in your terminal to create a basic Prisma schema file: + +```terminal copy +npx prisma init +``` + +This command created a new directory called `prisma` with the following files for you: + +- `schema.prisma`: Your Prisma schema file that specifies your database connection and models +- `.env`: A [`dotenv`](https://github.com/motdotla/dotenv) to configure your database connection URL as an environment variable + +The Prisma schema file currently looks as follows: + +```prisma file=prisma/schema.prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +:::tip + +If you're using VS Code, be sure to install the [Prisma VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) for syntax highlighting, formatting, auto-completion and a lot more cool features. + +::: + +### 2.2. Connect your database + +If you're not using PostgreSQL, you need to adjust the `provider` field on the `datasource` block to the database you currently use: + + + + + +```prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma +datasource db { + provider = "sqlserver" + url = env("DATABASE_URL") +} +``` + + + + + +```prisma +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} +``` + + + + + +Once that's done, you can configure your [database connection URL](/orm/reference/connection-urls) in the `.env` file. Here's how the database connection from Sequelize maps to the connection URL format used by Prisma: + + + + + +Assume you have the following database connection details in `src/models/index.js`: + +```js file=src/models/index.js +const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', { + host: 'localhost', + dialect: 'postgres', +}) +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-sequelize" +``` + +Note that you can optionally configure the PostgreSQL [schema](https://www.postgresql.org/docs/9.1/ddl-schemas.html) by appending the `schema` argument to the connection URL: + +```env file=.env +DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-sequelize?schema=myschema" +``` + +If not provided, the default schema called `public` is being used. + + + + + +Assume you have the following database connection details in `src/models/index.js`: + +```js file=src/models/index.js +const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', { + host: 'localhost', + dialect: 'postgres', +}) +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="mysql://alice:myPassword42@localhost:3306/blog-sequelize" +``` + + + + + +Assume you have the following database connection details in `src/models/index.js`: + +```js file=src/models/index.js +const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', { + host: 'localhost', + dialect: 'mssql', +}) +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="sqlserver://localhost:1433;database=blog-sequelize;user=alice;password=myPassword42;trustServerCertificate=true" +``` + + + + + +Assume you have the following database connection details in `src/models/index.js`: + +```js file=src/models/index.js +const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: '../../blog-sequelize.sqlite', +}) +``` + +The respective connection URL would look as follows in Prisma: + +```env file=.env +DATABASE_URL="file:./blog-sequelize.db" +``` + + + + + +### 2.3. Introspect your database using Prisma + +With your connection URL in place, you can [introspect](/orm/prisma-schema/introspection) your database to generate your Prisma models: + +```terminal copy +npx prisma db pull +``` + +This creates the following Prisma models: + +```prisma file=prisma/schema.prisma +model Categories { + id Int @id @default(autoincrement()) + name String + createdAt DateTime + updatedAt DateTime + PostCategories PostCategories[] +} + +model PostCategories { + createdAt DateTime + updatedAt DateTime + CategoryId Int + PostId Int + Categories Categories @relation(fields: [CategoryId], references: [id]) + Posts Posts @relation(fields: [PostId], references: [id]) + + @@id([CategoryId, PostId]) +} + +model Posts { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + createdAt DateTime + updatedAt DateTime + authorId Int? + Users Users? @relation(fields: [authorId], references: [id]) + PostCategories PostCategories[] +} + +model Profiles { + id Int @id @default(autoincrement()) + bio String + createdAt DateTime + updatedAt DateTime + userId Int? @unique + Users Users? @relation(fields: [userId], references: [id]) +} + +model SequelizeMeta { + name String @id +} + +model Users { + id Int @id @default(autoincrement()) + name String? + email String @unique + createdAt DateTime + updatedAt DateTime + Posts Posts[] + Profiles Profiles? +} +``` + +### 2.4. Create a baseline migration + +To continue using Prisma Migrate to evolve your database schema, you will need to [baseline your database](/orm/prisma-migrate/getting-started). + +First, create a `migrations` directory and add a directory inside with your preferred name for the migration. In this example, we will use `0_init` as the migration name: + +```terminal +mkdir -p prisma/migrations/0_init +``` + +Next, generate the migration file with `prisma migrate diff`. Use the following arguments: + +- `--from-empty`: assumes the data model you're migrating from is empty +- `--to-schema-datamodel`: the current database state using the URL in the `datasource` block +- `--script`: output a SQL script + +```terminal wrap +npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql +``` + +Review the generated migration to ensure everything is correct. + +Next, mark the migration as applied using `prisma migrate resolve` with the `--applied` argument. + +```terminal +npx prisma migrate resolve --applied 0_init +``` + +The command will mark `0_init` as applied by adding it to the `_prisma_migrations` table. + +You now have a baseline for your current database schema. To make further changes to your database schema, you can update your Prisma schema and use `prisma migrate dev` to apply the changes to your database. + +### 2.5. Adjust `createdAt` and `updatedAt` fields + +The generated Prisma models represent your database tables and are the foundation for your programmatic Prisma Client API which allows you to send queries to your database. +You'll adjust the `createdAt` and `updatedAt` fields in our models. Sequelize doesn't add the `DEFAULT` constraint to `createdAt` when creating the tables in the database. +Therefore, you'll add `@default(now())` and `@updatedAt` attributes to the `createdAt` and `updatedAt` columns respectively. +To learn more how Prisma does this, you can read more [`@default(now())`](/orm/reference/prisma-schema-reference#now) and [`@updatedAt`](/orm/reference/prisma-schema-reference#updatedat) here. +Our updated schema will be as follows: + +```prisma file=prisma/schema.prisma +model Categories { + id Int @id @default(autoincrement()) + name String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + PostCategories PostCategories[] +} + +model PostCategories { + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + CategoryId Int + PostId Int + Categories Categories @relation(fields: [CategoryId], references: [id]) + Posts Posts @relation(fields: [PostId], references: [id]) + + @@id([CategoryId, PostId]) +} + +model Posts { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + authorId Int? + Users Users? @relation(fields: [authorId], references: [id]) + PostCategories PostCategories[] +} + +model Profiles { + id Int @id @default(autoincrement()) + bio String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + userId Int? @unique + Users Users? @relation(fields: [userId], references: [id]) +} + +model SequelizeMeta { + name String @id +} + +model Users { + id Int @id @default(autoincrement()) + name String? + email String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + Posts Posts[] + Profiles Profiles? +} +``` + +### 2.6. Adjust the Prisma schema (optional) + +The models that were generated via introspection currently _exactly_ map to your database tables. In this section, you'll learn how you can adjust the naming of the Prisma models to adhere to [Prisma's naming conventions](/orm/reference/prisma-schema-reference#naming-conventions). + +All of these adjustment are entirely optional and you are free to skip to the next step already if you don't want to adjust anything for now. You can go back and make the adjustments at any later point. + +As opposed to the current snake_case notation of Prisma models, Prisma's naming conventions are: + +- PascalCase for model names +- camelCase for field names + +You can adjust the naming by _mapping_ the Prisma model and field names to the existing table and column names in the underlying database using `@@map` and `@map`. + +Also note that you can rename [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) to optimize the Prisma Client API that you'll use later to send queries to your database. For example, the `post` field on the `user` model is a _list_, so a better name for this field would be `posts` to indicate that it's plural. + +Sequelize generates a `SequelizeMeta` model that is used internally by the library that is not needed. Therefore, you'll manually delete it from the schema. + +Here's an adjusted version of the Prisma schema that addresses these points: + +```prisma file=prisma/schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Category { + id Int @id @default(autoincrement()) + name String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + postCategories PostToCategories[] + + @@map("Categories") +} + +model PostToCategories { + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + categoryId Int + postId Int + category Category @relation(fields: [categoryId], references: [id]) + post Post @relation(fields: [postId], references: [id]) + + @@id([categoryId, postId]) + @@map("PostCategories") +} + +model Post { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + authorId Int? + author User? @relation(fields: [authorId], references: [id]) + postToCategories PostToCategories[] + + @@map("Posts") +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + userId Int? @unique + user User? @relation(fields: [userId], references: [id]) + + @@map("Profiles") +} + +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + posts Post[] + profile Profile? + + @@map("Users") +} +``` + +## Step 3. Install Prisma Client + +As a next step, you can install Prisma Client in your project so that you can start replacing the database queries in your project that are currently made with Sequelize: + +```terminal +npm install @prisma/client +``` + +## Step 4. Replace your Sequelize queries with Prisma Client + +In this section, we'll show a few sample queries that are being migrated from Sequelize to Prisma Client based on the example routes from the sample REST API project. For a comprehensive overview of how the Prisma Client API differs from Sequelize, check out the [API comparison](/orm/more/comparisons/prisma-and-sequelize#api-comparison) page. + +First, to set up the `PrismaClient` instance that you'll use to send database queries from the various route handlers. Create a new file named `prisma.js` in the `src` directory: + +```terminal copy +touch src/prisma.js +``` + +Now, instantiate `PrismaClient` and export it from the file so you can use it in your route handlers later: + +```js copy file=src/prisma.js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +module.exports = prisma +``` + +The imports in our controller files are as follows: + +```js file=src/controllers/post.js +const { Post, User, Category } = require('../models') +const { Op } = require('sequelize') +``` + +```js file=src/controllers/user.js +const { User } = require('../models') +``` + +You'll update the controller imports as you migrate from Sequelize to Prisma: + +```js file=src/controllers/post.js +const prisma = require('../prisma') +``` + +```js file=src/controllers/user.js +const prisma = require('../prisma') +``` + +### 4.1. Replacing queries in `GET` requests + +The REST API has four routes that accept `GET` requests: + +- `/feed`: Return all published posts +- `/filterPosts?searchString=SEARCH_STRING`: Filter returned posts by `SEARCH_STRING` +- `/post/:postId`: Returns a specific post +- `/authors`: Returns a list of authors + +Let's dive into the route handlers that implement these requests. + +#### `/feed` + +The `/feed` handler is currently implemented as follows: + +```js file=src/controllers/post.js +const feed = async (req, res) => { + try { + const feed = await Post.findAll({ + where: { published: true }, + include: ['author', 'categories'], + }) + return res.json(feed) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that each returned `Post` object includes the relation to the `author` and `category` it's associated with. With Sequelize, including the relation is not type-safe. For example, if there was a typo in the relation that is retrieved, your database query would fail only at _runtime_ – the JavaScript compiler does not provide any safety here. + +Here is how the same route is implemented using Prisma Client: + +```js file=src/controllers/post.js +const feed = async (req, res) => { + try { + const feed = await prisma.post.findMany({ + where: { published: true }, + include: { author: true, postToCategories: true }, + }) + return res.json(feed) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that the way how Prisma Client includes the `author` relation is absolutely type-safe. The JavaScript compiler would throw an error if you were trying to include a relation that does not exist on the `Post` model. + +#### `/filterPosts?searchString=SEARCH_STRING` + +The `/filterPosts` handler is currently implemented as follows: + +```js file=src/controllers/post.js +const filterPosts = async (req, res) => { + const { searchString } = req.query + + try { + const filteredPosts = await Post.findAll({ + where: { + [Op.or]: [ + { + title: { + [Op.like]: `%${searchString}%`, + }, + }, + { + content: { + [Op.like]: `%${searchString}%`, + }, + }, + ], + }, + include: 'author', + }) + + res.json(filteredPosts) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/post.js +const filterPosts = async (req, res) => { + const { searchString } = req.query + + try { + const filteredPosts = prisma.post.findMany({ + where: { + OR: [ + { + title: { contains: searchString }, + }, + { + content: { contains: searchString }, + }, + ], + }, + }) + + res.json(filteredPosts) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that Sequelize provides [Operator symbols](https://sequelize.org/master/variable/index.html#static-variable-Op) - `Op` - to be used when querying data. Prisma on the other hand [combines several `where` conditions with an implicit `AND` operator](/orm/reference/prisma-client-reference#get-all-post-records-where-the-content-field-contains-prisma-and-published-is-false-no-and), so in this case the Prisma query needs to make the `OR` explicit. + +#### `/post/:postId` + +The `/post/:postId` handler is currently implemented as follows: + +```js file=src/controllers/post.js +const getPostById = async (req, res) => { + const { postId } = req.params + + try { + const post = await Post.findOne({ + where: { id: postId }, + include: 'author', + }) + + return res.json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/post.js +const getPostById = async (req, res) => { + const { postId } = req.params + + try { + const post = await prisma.post.findUnique({ + where: { id: Number(postId) }, + include: { author: true }, + }) + + return res.json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +### 4.2. Replacing queries in `POST` requests + +The REST API has three routes that accept `POST` requests: + +- `/user`: Creates a new `User` record +- `/post`: Creates a new `User` record +- `/user/:userId/profile`: Creates a new `Profile` record for a `User` record with a given ID + +#### `/user` + +The `/user` handler is currently implemented as follows: + +```js file=src/controllers/user.js +const createUser = async (req, res) => { + const { name, email } = req.body + + try { + const user = await User.create({ + name, + email, + }) + + return res.json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/user.js +const createUser = async (req, res) => { + const { name, email } = req.body + + try { + const user = await prisma.user.create({ + data: { + name, + email, + }, + }) + + return res.json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +#### `/post` + +The `/post` handler is currently implemented as follows: + +```js file=src/controllers/post.js +const createDraft = async (req, res) => { + const { title, content, authorEmail } = req.body + + try { + const user = await User.findOne({ email: authorEmail }) + + const draft = await Post.create({ + title, + content, + authorId: user.id, + }) + + res.json(draft) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/post.js +const createDraft = async (req, res) => { + const { title, content, authorEmail } = req.body + + try { + const draft = await prisma.post.create({ + data: { + title, + content, + author: { + connect: { email: authorEmail }, + }, + }, + }) + + res.json(draft) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that Prisma Client's nested write here save an initial query where first the `User` record needs to be retrieved by its `email`. That's because, with Prisma you can connect records in relations using any unique property. + +#### `/user/:userId/profile` + +The `/user/:userId/profile` handler is currently implemented as follows: + +```js file=src/controllers/user.js +const setUserBio = async (req, res) => { + const { userId } = req.params + const { bio } = req.body + + try { + const user = await User.findOne({ + where: { + id: Number(userId), + }, + }) + + const updatedUser = await user.createProfile({ bio }) + + return res.json(updatedUser) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/user.js +const setUserBio = async (req, res) => { + const { userId } = req.params + const { bio } = req.body + + try { + const user = await prisma.user.update({ + where: { id: Number(userId) }, + data: { + profile: { + create: { bio }, + }, + }, + }) + + return res.json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +### 4.3. Replacing queries in `PUT` requests + +The REST API has one route that accept a `PUT` request: + +- `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID`: Adds the post with `POST_ID` to the category with `CATEGORY_ID` + +Let's dive into the route handlers that implement these requests. + +#### `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID` + +The `/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID` handler is currently implemented as follows: + +```js file=src/controllers/post.js +const addPostToCategory = async (req, res) => { + const { postId, categoryId } = req.query + + try { + const post = await Post.findOne({ + where: { id: postId }, + }) + + const category = await Category.findOne({ + where: { id: categoryId }, + }) + + const updatedPost = await post.addCategory(category) + + return res.json(updatedPost) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route is implemented as follows: + +```js file=src/controllers/post.js +const addPostToCategory = async (req, res) => { + const { postId, categoryId } = req.query + + try { + const post = await prisma.post.update({ + data: { + postToCategories: { + create: { + categories: { + connect: { id: Number(categoryId) }, + }, + }, + }, + }, + where: { + id: Number(postId), + }, + }) + + return res.json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that this Prisma Client can be made less verbose by modeling the relation as an [implicit many-to-many relation](#implicit-many-to-many-relations) instead. In that case, the query would look as follows: + +```js file=src/controllers/posts.js +const post = await prisma.post.update({ + data: { + category: { + connect: { id: categoryId }, + }, + }, + where: { id: postId }, +}) +``` + +## More + +### Primary key column + +By default, Sequelize defines a `primaryKey` and used `id` with the autoby default if not defined. This is optional. +If you would like to set your own primary key, you can use the `primaryKey: true` and define your preferred data type in your field of choice: + +```js +// changing the primary key column +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define('Post', { + postId: { + type: DataTypes.INTEGER, + primaryKey: true, + }, + }) + return Post +} + +// changing the id DataType +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define('Post', { + id: { + type: DataTypes.UUID, // alternative: DataTypes.STRING + primaryKey: true, + }, + }) + return Post +} +``` + +### Table name inference + +Sequelize infers table names from the model name. When the name of a table isn't provided Sequelize automatically pluralizes the model name and uses that as the table name using a library called [inflection](https://www.npmjs.com/package/inflection). +Prisma on the other hand maps the model name to the table name in your database [modelling your data](/orm/prisma-schema/data-model/models). +If you wish to change this default behaviour in Sequelize, you can either enforce the table name to be equal to the model name or provide the table name directly: + +```js +// enforcing table name to be equal to model name +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define( + 'Post', + { + // ... attributes + }, + { + freezeTableName: true, + } + ) + return Post +} +``` + +```js +// providing the table name directly +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define( + 'Post', + { + // ... attributes + }, + { + tableName: 'Post', + } + ) + return Post +} +``` + +### Timestamps + +Sequelize automatically adds the fields `createdAt` and `updatedAt` to every model using the data type `DataTypes.DATE`, by default. You can disable this for a model with the `timestamps: false` option: + +```js +sequelize.define( + 'User', + { + // ... (attributes) + }, + { + timestamps: false, + } +) +``` + +Prisma offers you the flexibility to define these fields in your model. You add the `createdAt` and [`updatedAt`](/orm/reference/prisma-schema-reference#updatedat) fields by defining them explicitly in your model. +To set the `createdAt` field in your model, add the `default(now())` attribute to the column. In order to set the `updatedAt` column, update your model by adding the `@updatedAt` attribute to the column. + +```prisma +model User { + id Int @id @default(autoincrement()) + name String? + email String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} +``` + +### Implicit many-to-many relations + +Similar to the `belongsToMany()` association method in Sequelize, Prisma allows you to [model many-to-many relations _implicitly_](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations). That is, a many-to-many relation where you do not have to manage the [relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) (also sometimes called JOIN table) _explicitly_ in your schema. Here is an example with Sequelize: + +```js +module.exports = (sequelize, DataTypes) => { + const Post = sequelize.define('Post', { + title: { + type: DataTypes.STRING, + allowNull: false, + }, + content: { + type: DataTypes.STRING, + }, + published: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + }) + Post.associate = (models) => { + Post.belongsTo(models.User, { + foreignKey: 'authorId', + as: 'author', + }) + Post.belongsToMany(models.Category, { + through: 'PostCategories', + as: 'categories', + }) + } + return Post +} +``` + +```js +module.exports = (sequelize, DataTypes) => { + const Category = sequelize.define('Category', { + name: { + type: DataTypes.STRING, + allowNull: false, + }, + }) + Category.associate = (models) => { + Category.belongsToMany(models.Post, { + through: 'PostCategories', + as: 'posts', + }) + } + return Category +} +``` + +When you start your application, Sequelize will create the the tables for you - based on these models: + +```sql +Executing (default): CREATE TABLE IF NOT EXISTS "PostCategories" +("createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, +"CategoryId" INTEGER REFERENCES "Categories" ("id") ON DELETE CASCADE ON UPDATE CASCADE, +"PostId" INTEGER REFERENCES "Posts" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("CategoryId","PostId")); +``` + +If you introspect the database with Prisma, you'll get the following result in the Prisma schema (note that some relation field names have been adjusted to look friendlier compared to the raw version from introspection): + +```prisma +model Categories { + id Int @id @default(autoincrement()) + name String + createdAt DateTime + updatedAt DateTime + PostCategories PostCategories[] + + @@map("category") +} + +model PostCategories { + createdAt DateTime + updatedAt DateTime + CategoryId Int + PostId Int + Categories Categories @relation(fields: [CategoryId], references: [id]) + Posts Posts @relation(fields: [PostId], references: [id]) + + @@id([CategoryId, PostId]) + @@map("PostCategories") +} + +model Posts { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean? @default(false) + createdAt DateTime + updatedAt DateTime + authorId Int? + Users Users? @relation(fields: [authorId], references: [id]) + PostCategories PostCategories[] + + @@map("post") +} +``` + +In this Prisma schema, the many-to-many relation is modeled explicitly via the relation table `PostCategories` + +By adhering to the conventions for Prisma relation tables, the relation could look as follows: + +```prisma +model Categories { + id Int @id @default(autoincrement()) + name String + posts Posts[] + + @@map("category") +} + +model Posts { + id Int @id @default(autoincrement()) + title String + content String? + published Boolean @default(false) + authorId Int? + author User? @relation(fields: [authorId], references: [id]) + categories Categories[] + + @@map("post") +} +``` + +This would also result in a more ergonomic and less verbose Prisma Client API to modify the records in this relation, because you have a direct path from `Post` to `Category` (and the other way around) instead of needing to traverse the `PostCategories` model first. diff --git a/docs/200-orm/800-more/450-migrating-to-prisma/03-migrate-from-mongoose.mdx b/docs/200-orm/800-more/450-migrating-to-prisma/03-migrate-from-mongoose.mdx new file mode 100644 index 0000000000..5477b8b957 --- /dev/null +++ b/docs/200-orm/800-more/450-migrating-to-prisma/03-migrate-from-mongoose.mdx @@ -0,0 +1,1069 @@ +--- +title: 'Migrate from Mongoose' +metaTitle: 'How to migrate from Mongoose to Prisma' +metaDescription: 'Learn how to migrate from Mongoose to Prisma' +--- + + + +This guide describes how to migrate from Mongoose to Prisma. It uses an extended version of the [Mongoose Express example](https://github.com/Automattic/mongoose/tree/master/examples/express) as a [sample project](https://github.com/prisma/migrate-from-mongoose-to-prisma) to demonstrate the migration steps. You can find the example used for this guide on [GitHub](https://github.com/prisma/migrate-from-mongoose-to-prisma). + +You can learn how Prisma compares to Mongoose on the [Prisma vs Mongoose](/orm/more/comparisons/prisma-and-mongoose) page. + + + +## Overview of the migration process + +Note that the steps for migrating from Mongoose to Prisma are always the same, no matter what kind of application or API layer you're building: + +1. [Install the Prisma CLI](/orm/tools/prisma-cli#installation) +1. [Introspect your database](/orm/prisma-schema/introspection) +1. [Install and generate Prisma Client](/orm/prisma-client/setup-and-configuration/generating-prisma-client) +1. Gradually replace your Mongoose queries with Prisma Client + +These steps apply whether you're building a REST API (e.g. with Express, koa or NestJS), a GraphQL API (e.g. with Apollo Server, TypeGraphQL or Nexus) or any other kind of application that uses Mongoose for database access. + +Prisma lends itself really well for **incremental adoption**. This means, you don't have migrate your entire project from Mongoose to Prisma at once, but rather you can _step-by-step_ move your database queries from Mongoose to Prisma. + +## Overview of the sample project + +For this guide, we'll use a REST API built with Express as a [sample project](https://github.com/prisma/migrate-from-mongoose-to-prisma) to migrate to Prisma. It has three documents and one sub-document (embedded document): + + + + + +```js +const mongoose = require('mongoose') + +const Schema = mongoose.Schema + +const PostSchema = new Schema({ + title: String, + content: String, + published: { + type: Boolean, + default: false, + }, + author: { + type: Schema.Types.ObjectId, + ref: 'author', + required: true, + }, + categories: [ + { + type: Schema.Types.ObjectId, + ref: 'Category', + }, + ], +}) + +module.exports = mongoose.model('Post', PostSchema) +``` + + + + + +```js +const mongoose = require('mongoose') + +const Schema = mongoose.Schema + +const ProfileSchema = new Schema( + { + bio: String, + }, + { + _id: false, + } +) + +const UserSchema = new Schema({ + name: String, + email: { + type: String, + unique: true, + }, + profile: { + type: ProfileSchema, + default: () => ({}), + }, +}) + +module.exports = mongoose.model('User', UserSchema) +``` + + + + + +```js +const mongoose = require('mongoose') + +const Schema = mongoose.Schema + +const CategorySchema = new Schema({ + name: { + type: String, + required: true, + }, +}) + +module.exports = mongoose.model('Category', CategorySchema) +``` + + + + + +The models/documents have the following types of relationships: + +- 1-n: `User` ↔ `Post` +- m-n: `Post` ↔ `Category` +- Sub-document/ Embedded document: `User` ↔ `Profile` + +In the example used in this guide, the route handlers are located in the `src/controllers` directory. The models are located in the `src/models` directory. From there, the models are pulled into a central `src/routes.js` file, which is used to define the required routes in `src/index.js`: + +```copy=false +└── blog-mongoose + ├── package.json + └──src +    ├── controllers +    │   ├── post.js +    │   └── user.js +    ├── models +    │   ├── category.js +    │   ├── post.js +    │   └── user.js +    ├── index.js +    ├── routes.js +    └── seed.js +``` + +The example repository contains a `seed` script inside the `package.json` file. + +Run `npm run seed` to populate your database with the sample data in the `./src/seed.js` file. + +## Step 1. Install the Prisma CLI + +The first step to adopt Prisma is to [install the Prisma CLI](/orm/tools/prisma-cli#installation) in your project: + +```terminal copy +npm install prisma --save-dev +``` + +## Step 2. Introspect your database + +Introspection is a process of inspecting the structure of a database, used in Prisma to generate a [data model](/orm/prisma-schema/data-model/models) in your [Prisma schema](/orm/prisma-schema). + +### 2.1. Set up Prisma + +Before you can introspect your database, you need to set up your [Prisma schema](/orm/prisma-schema) and connect Prisma to your database. Run the following command in your terminal to create a basic Prisma schema file: + +```terminal copy +npx prisma init --datasource-provider mongodb +``` + +This command creates: + +- A new directory called `prisma` that contains a `schema.prisma` file; your Prisma schema file specifies your database connection and models +- `.env`: A [`dotenv`](https://github.com/motdotla/dotenv) file at the root of your project (if it doesn't already exist), used to configure your database connection URL as an environment variable + +The Prisma schema file currently looks as follows: + +```prisma file=prisma/schema.prisma +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} +``` + +:::tip + +For an optimal development experience when working with Prisma, refer to [editor setup](/orm/more/development-environment/editor-setup) to learn about syntax highlighting, formatting, auto-completion, and many more cool features. + +::: + +### 2.2. Connect your database + +Configure your [database connection URL](/orm/reference/connection-urls#mongodb) in the `.env` file. + +The format of the connection URL that Mongoose uses is similar to the one Prisma uses. + +```bash file=.env +DATABASE_URL="mongodb://alice:myPassword43@localhost:27017/blog-mongoose" +``` + +Refer to the [MongoDB connection URL specification](https://www.mongodb.com/docs/manual/reference/connection-string/#connection-string-options) for further details. + +### 2.3. Run Prisma's introspection + +With your connection URL in place, you can [introspect](/orm/prisma-schema/introspection) your database to generate your Prisma models: + +> **Note**: MongoDB is a _schemaless_ database. To incrementally adopt Prisma in your project, ensure your database is populated with sample data. Prisma introspects a MongoDB schema by sampling data stored and inferring the schema from the data in the database. + +```terminal copy +npx prisma db pull +``` + +This creates the following Prisma models: + +```prisma file=prisma/schema.prisma +type UsersProfile { + bio String +} + +model categories { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + name String +} + +model posts { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + author String @db.ObjectId + categories String[] @db.ObjectId + content String + published Boolean + title String +} + +model users { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + email String @unique(map: "email_1") + name String + profile UsersProfile? +} +``` + +The generated Prisma models represent the MongoDB collections and are the foundation of your programmatic Prisma Client API which allows you to send queries to your database. + +### 2.4. Update the relations + +MongoDB doesn't support relations between different collections. However, you can create references between documents using the [`ObjectId`](/orm/overview/databases/mongodb#using-objectid) field type or from one document to many using an array of `ObjectIds` in the collection. The reference will store id(s) of the related document(s). You can use the `populate()` method that Mongoose provides to populate the reference with the data of the related document. + +Update the 1-n relationship between `Post` \<-\> `User` as follows: + +- Rename the existing `author` reference in the `posts` model to `authorId` and add the `@map("author")` attribute +- Add the `author` relation field in the `posts` model and it's `@relation` attribute specifying the `fields` and `references` +- Add the `posts` relation in the `users` model + + + + + +```prisma +type UsersProfile { + bio String +} + +model categories { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + name String +} + +model posts { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String + published Boolean + v Int @map("__v") + + - author String @db.ObjectId + + author users @relation(fields: [authorId], references: [id]) + + authorId String @map("author") @db.ObjectId + + categories String[] @db.ObjectId +} + +model users { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + email String @unique(map: "email_1") + name String + profile UsersProfile? + + posts posts[] +} +``` + + + + + +```prisma file=schema.prisma +type UsersProfile { + bio String +} + +model categories { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + name String +} + +model posts { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String + published Boolean + v Int @map("__v") + + author users @relation(fields: [authorId], references: [id]) + authorId String @map("author") @db.ObjectId + + categories String[] @db.ObjectId +} + +model users { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + email String @unique(map: "email_1") + name String + profile UsersProfile? + posts posts[] +} +``` + + + + + +Update the m-n between `Post` \<-\> `Category` references as follows: + +- Rename the `categories` field to `categoryIds` and map it using `@map("categories")` in the `posts` model +- Add a new `categories` relation field in the `posts` model +- Add the `postIds` scalar list field in the `categories` model +- Add the `posts` relation in the `categories` model +- Add a [relation scalar](/orm/prisma-schema/data-model/relations#annotated-relation-fields) on both models +- Add the `@relation` attribute specifying the `fields` and `references` arguments on both sides + + + + + +```prisma +type UsersProfile { + bio String +} + +model categories { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + name String + + posts posts[] @relation(fields: [postIds], references: [id]) + + postIds String[] @db.ObjectId +} + +model posts { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String + published Boolean + v Int @map("__v") + + author users @relation(fields: [authorId], references: [id]) + authorId String @map("author") @db.ObjectId + + - categories String[] @db.ObjectId + + categories categories[] @relation(fields: [categoryIds], references: [id]) + + categoryIds String[] @map("categories") @db.ObjectId +} + +model users { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + email String @unique(map: "email_1") + name String + profile UsersProfile? + posts posts[] +} +``` + + + + + +```prisma file=schema.prisma +type UsersProfile { + bio String +} + +model categories { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + v Int @map("__v") + + posts posts[] @relation(fields: [postIds], references: [id]) + postIds String[] @db.ObjectId +} + +model posts { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String + published Boolean + v Int @map("__v") + + author users @relation(fields: [authorId], references: [id]) + authorId String @map("author") @db.ObjectId + + categories categories[] @relation(fields: [categoryIds], references: [id]) + categoryIds String[] @db.ObjectId +} + +model users { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @map("__v") + email String @unique(map: "email_1") + name String + profile UsersProfile? + posts posts[] +} +``` + + + + + +### 2.5 Adjust the Prisma schema (optional) + +The models that were generated via introspection currently _exactly_ map to your database collections. In this section, you'll learn how you can adjust the naming of the Prisma models to adhere to [Prisma's naming conventions](/orm/reference/prisma-schema-reference#naming-conventions). + +Some of these adjustments are entirely optional and you are free to skip to the next step already if you don't want to adjust anything for now. You can go back and make the adjustments at any later point. + +As opposed to the current snake_case notation of Prisma models, Prisma's naming conventions are: + +- PascalCase for model names +- camelCase for field names + +You can adjust the naming by [_mapping_](/orm/overview/databases/mongodb#using-objectid) the Prisma model and field names to the existing table and column names in the underlying database using `@@map` and `@map`, respectively. + +:::tip + +You can use the [rename symbol](https://code.visualstudio.com/docs/editor/refactoring#_rename-symbol) operation to refactor model names by highlighting the model name, pressing F2, and finally typing the desired name. This will rename all instances where it is referenced and add the `@@map()` attribute to the existing model with its former name. + +::: + +If your schema includes a [`versionKey`](https://mongoosejs.com/docs/guide.html#versionKey), update it by adding the `@default(0)` and `@ignore` attributes to the `v` field. This means the field will be excluded from the generated Prisma Client and will have a default value of 0. Prisma does not handle document versioning. + +Also note that you can rename [relation fields](/orm/prisma-schema/data-model/relations#relation-fields) to optimize the Prisma Client API that you'll use later to send queries to your database. For example, the `post` field on the `user` model is a _list_, so a better name for this field would be `posts` to indicate that it's plural. + +Update the `published` field by including the `@default` attribute to define the default value of the field. + +You can also rename the `UserProfile` composite type to `Profile`. + +Here's an adjusted version of the Prisma schema that addresses these points: + +```prisma file=prisma/schema.prisma highlight=10,14,17,22,25,29,30,38,41,43,46,49;normal +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} + +type Profile { + bio String +} + +model Category { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + v Int @default(0) @map("__v") @ignore + + posts Post[] @relation(fields: [post_ids], references: [id]) + post_ids String[] @db.ObjectId + + @@map("categories") +} + +model Post { + id String @id @default(auto()) @map("_id") @db.ObjectId + title String + content String + published Boolean @default(false) + v Int @default(0) @map("__v") @ignore + + author User @relation(fields: [authorId], references: [id]) + authorId String @map("author") @db.ObjectId + + categories Category[] @relation(fields: [categoryIds], references: [id]) + categoryIds String[] @db.ObjectId + + @@map("posts") +} + +model User { + id String @id @default(auto()) @map("_id") @db.ObjectId + v Int @default(0) @map("__v") @ignore + email String @unique(map: "email_1") + name String + profile Profile? + posts Post[] + + @@map("users") +} +``` + +## Step 3. Install Prisma Client + +As a next step, you can install Prisma Client in your project so that you can start replacing the database queries in your project that are currently made with Mongoose: + +```terminal +npm install @prisma/client +``` + +## Step 4. Replace your Mongoose queries with Prisma Client + +In this section, we'll show a few sample queries that are being migrated from Mongoose to Prisma Client, based on the example routes from the sample REST API project. For a comprehensive overview of how the Prisma Client API differs from Mongoose, check out the [Mongoose and Prisma API comparison](/orm/more/comparisons/prisma-and-mongoose) page. + +First, to set up the `PrismaClient` instance that you'll use to send database queries from the various route handlers, create a new file named `prisma.js` in the `src` directory: + +```terminal copy +touch src/prisma.js +``` + +Now, instantiate `PrismaClient` and export it from the file so you can use it in your route handlers later: + +```js copy file=src/prisma.js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +module.exports = prisma +``` + +The imports in our controller files are as follows: + +```js file=src/controllers/post.js +const Post = require('../models/post') +const User = require('../models/user') +const Category = require('../models/category') +``` + +```js file=src/controllers/user.js +const Post = require('../models/post') +const User = require('../models/user') +``` + +You'll update the controller imports as you migrate from Mongoose to Prisma: + +```js file=src/controllers/post.js +const prisma = require('../prisma') +``` + +```js file=src/controllers/user.js +const prisma = require('../prisma') +``` + +### 4.1. Replacing queries in `GET` requests + +The example REST API used in this guide has four routes that accept `GET` requests: + +- `/feed?searchString={searchString}&take={take}&skip={skip}`: Return all published posts + - Query Parameters (optional): + - `searchString`: Filter posts by `title` or `content` + - `take`: Specifies how many objects should be returned in the list + - `skip`: Specifies how many of the returned objects should be skipped +- `/post/:id`: Returns a specific post +- `/authors`: Returns a list of authors + +Let's dive into the route handlers that implement these requests. + +#### `/feed` + +The `/feed` handler is implemented as follows: + +```js file=src/controllers/post.js +const feed = async (req, res) => { + try { + const { searchString, skip, take } = req.query + + const or = + searchString !== undefined + ? { + $or: [ + { title: { $regex: searchString, $options: 'i' } }, + { content: { $regex: searchString, $options: 'i' } }, + ], + } + : {} + + const feed = await Post.find( + { + ...or, + published: true, + }, + null, + { + skip, + batchSize: take, + } + ) + .populate({ path: 'author', model: User }) + .populate('categories') + + return res.status(200).json(feed) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that each returned `Post` object includes the relation to the `author` and `category` with which it is associated. With Mongoose, including the relation is not type-safe. For example, if there was a typo in the relation that is retrieved, your database query would fail only at _runtime_ – the JavaScript compiler does not provide any safety here. + +Here is how the same route handler is implemented using Prisma Client: + +```js file=src/controllers/post.js +const feed = async (req, res) => { + try { + const { searchString, skip, take } = req.query + + const or = searchString + ? { + OR: [ + { title: { contains: searchString } }, + { content: { contains: searchString } }, + ], + } + : {} + + const feed = await prisma.post.findMany({ + where: { + published: true, + ...or, + }, + include: { author: true, categories: true }, + take: Number(take) || undefined, + skip: Number(skip) || undefined, + }) + + return res.status(200).json(feed) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that the way in which Prisma Client includes the `author` relation is absolutely type-safe. The JavaScript compiler would throw an error if you were trying to include a relation that does not exist on the `Post` model. + +#### `/post/:id` + +The `/post/:id` handler is implemented as follows: + +```js file=src/controllers/post.js +const getPostById = async (req, res) => { + const { id } = req.params + + try { + const post = await Post.findById(id) + .populate({ path: 'author', model: User }) + .populate('categories') + + if (!post) return res.status(404).json({ message: 'Post not found' }) + + return res.status(200).json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route handler is implemented as follows: + +```js file=src/controllers/post.js +const getPostById = async (req, res) => { + const { id } = req.params + + try { + const post = await prisma.post.findUnique({ + where: { id }, + include: { + author: true, + category: true, + }, + }) + + if (!post) return res.status(404).json({ message: 'Post not found' }) + + return res.status(200).json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +### 4.2. Replacing queries in `POST` requests + +The REST API has three routes that accept `POST` requests: + +- `/user`: Creates a new `User` record +- `/post`: Creates a new `User` record +- `/user/:id/profile`: Creates a new `Profile` record for a `User` record with a given ID + +#### `/user` + +The `/user` handler is implemented as follows: + +```js file=src/controllers/user.js +const createUser = async (req, res) => { + const { name, email } = req.body + + try { + const user = await User.create({ + name, + email, + }) + + return res.status(201).json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route handler is implemented as follows: + +```js file=src/controllers/user.js +const createUser = async (req, res) => { + const { name, email } = req.body + + try { + const user = await prisma.user.create({ + data: { + name, + email, + }, + }) + + return res.status(201).json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +#### `/post` + +The `/post` handler is implemented as follows: + +```js file=src/controllers/post.js +const createDraft = async (req, res) => { + const { title, content, authorEmail } = req.body + + try { + const author = await User.findOne({ email: authorEmail }) + + if (!author) return res.status(404).json({ message: 'Author not found' }) + + const draft = await Post.create({ + title, + content, + author: author._id, + }) + + res.status(201).json(draft) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route handler is implemented as follows: + +```js file=src/controllers/post.js +const createDraft = async (req, res) => { + const { title, content, authorEmail } = req.body + + try { + const draft = await prisma.post.create({ + data: { + title, + content, + author: { + connect: { + email: authorEmail, + }, + }, + }, + }) + + res.status(201).json(draft) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +Note that Prisma Client's nested write here saves the initial query where the `User` record is first retrieved by its `email`. That's because, with Prisma you can connect records in relations using any unique property. + +#### `/user/:id/profile` + +The `/user/:id/profile` handler is implemented as follows: + +```js file=src/controllers/user.js +const setUserBio = async (req, res) => { + const { id } = req.params + const { bio } = req.body + + try { + const user = await User.findByIdAndUpdate( + id, + { + profile: { + bio, + }, + }, + { new: true } + ) + + if (!user) return res.status(404).json({ message: 'Author not found' }) + + return res.status(200).json(user) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the route handler is implemented as follows: + +```js file=src/controllers/user.js +const setUserBio = async (req, res) => { + const { id } = req.params + const { bio } = req.body + + try { + const user = await prisma.user.update({ + where: { id }, + data: { + profile: { + bio, + }, + }, + }) + + if (!user) return res.status(404).json({ message: 'Author not found' }) + + return res.status(200).json(user) + } catch (error) { + console.log(error) + return res.status(500).json(error) + } +} +``` + +Alternatively, you can use the `set` property to update the value of an embedded document as follows: + +```js file=src/controllers/user.js +const setUserBio = async (req, res) => { + const { id } = req.params + const { bio } = req.body + + try { + const user = await prisma.user.update({ + where: { + id, + }, + data: { + profile: { + set: { bio }, + }, + }, + }) + + return res.status(200).json(user) + } catch (error) { + console.log(error) + return res.status(500).json(error) + } +} +``` + +### 4.3. Replacing queries in `PUT` requests + +The REST API has two routes that accept a `PUT` request: + +- `/post/:id/:categoryId`: Adds the post with `:id` to the category with `:categoryId` +- `/post/:id`: Updates the `published` status of a post to true. + +Let's dive into the route handlers that implement these requests. + +#### `/post/:id/:categoryId` + +The `/post/:id/:categoryId` handler is implemented as follows: + +```js file=src/controllers/post.js +const addPostToCategory = async (req, res) => { + const { id, categoryId } = req.params + + try { + const category = await Category.findById(categoryId) + + if (!category) + return res.status(404).json({ message: 'Category not found' }) + + const post = await Post.findByIdAndUpdate( + { _id: id }, + { + categories: [{ _id: categoryId }], + }, + { new: true } + ) + + if (!post) return res.status(404).json({ message: 'Post not found' }) + return res.status(200).json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the handler is implemented as follows: + +```js file=src/controllers/post.js +const addPostToCategory = async (req, res) => { + const { id, categoryId } = req.query + + try { + const post = await prisma.post.update({ + where: { + id, + }, + data: { + categories: { + connect: { + id: categoryId, + }, + }, + }, + }) + + if (!post) return res.status(404).json({ message: 'Post not found' }) + + return res.status(200).json(post) + } catch (error) { + console.log({ error }) + return res.status(500).json(error) + } +} +``` + +#### `/post/:id` + +The `/post/:id` handler is implemented as follows: + +```js file=src/controllers/post.js +const publishDraft = async (req, res) => { + const { id } = req.params + + try { + const post = await Post.findByIdAndUpdate( + { id }, + { published: true }, + { new: true } + ) + return res.status(200).json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +With Prisma, the handler is implemented as follows: + +```js file=src/controllers/post.js +const publishDraft = async (req, res) => { + const { id } = req.params + + try { + const post = await prisma.post.update({ + where: { id }, + data: { published: true }, + }) + return res.status(200).json(post) + } catch (error) { + return res.status(500).json(error) + } +} +``` + +## More + +### Embedded documents `_id` field + +By default, Mongoose assigns each document and embedded document an `_id` field. If you wish to disable this option for embedded documents, you can set the `_id` option to false. + +```js +const ProfileSchema = new Schema( + { + bio: String, + }, + { + _id: false, + } +) +``` + +### Document version key + +Mongoose assigns each document a version when created. You can disable Mongoose from versioning your documents by setting the `versionKey` option of a model to false. It is [not recommended](http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html) to disable this unless you are an advanced user. + +```js +const ProfileSchema = new Schema( + { + bio: String, + }, + { + versionKey: false, + } +) +``` + +When migrating to Prisma, mark the `versionKey` field as optional ( **?** ) in your Prisma schema and add the `@ignore` attribute to exclude it from Prisma Client. + +### Collection name inference + +Mongoose infers the collection names by automatically converting the model names to lowercase and plural form. + +On the other hand, Prisma maps the model name to the table name in your database [modeling your data](/orm/prisma-schema/data-model/models). + +You can enforce the collection name in Mongoose to have the same name as the model by setting the [](https://mongoosejs.com/docs/guide.html#collection) option while creating your schema + +```js +const PostSchema = new Schema( + { + title: String, + content: String, + // more fields here + }, + { + collection: 'Post', + } +) +``` + +### Modeling relations + +You can model relations in Mongoose between documents by either using [sub-documents](https://mongoosejs.com/docs/subdocs.html) or storing [a reference to other documents](https://mongoosejs.com/docs/queries.html#refs). + +Prisma allows you to model different types of relations between documents when working with MongoDB: + +- [One-to-one relations](/orm/prisma-schema/data-model/relations/one-to-one-relations#mongodb) +- [One-to-many relations](/orm/prisma-schema/data-model/relations/one-to-many-relations#mongodb) +- [Many-to-many relations](/orm/prisma-schema/data-model/relations/many-to-many-relations#mongodb) +- [Self-relations](/orm/prisma-schema/data-model/relations/self-relations#mongodb) +- [Embedded documents](/orm/prisma-schema/data-model/models#defining-composite-types) diff --git a/docs/200-orm/800-more/450-migrating-to-prisma/index.mdx b/docs/200-orm/800-more/450-migrating-to-prisma/index.mdx new file mode 100644 index 0000000000..f949e1bf20 --- /dev/null +++ b/docs/200-orm/800-more/450-migrating-to-prisma/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Migrate to Prisma' +metaTitle: 'Migrate to Prisma from other ORMs' +metaDescription: 'How to migrate to Prisma from other ORMs and query builders.' +--- + +## In this section + + diff --git a/docs/200-orm/800-more/500-development-environment/050-environment-variables/040-env-files.mdx b/docs/200-orm/800-more/500-development-environment/050-environment-variables/040-env-files.mdx new file mode 100644 index 0000000000..b503901566 --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/050-environment-variables/040-env-files.mdx @@ -0,0 +1,93 @@ +--- +title: '.env files' +metaTitle: '.env files' +metaDescription: 'Configure environment variables using .env files in Prisma' +--- + + + +Prisma creates a default `.env` file at your projects root. You can choose to replace this file or create a new one in the `prisma` folder, or if you choose to relocate your `prisma.schema` file, alongside that. + + + +### `.env` file locations + +The Prisma CLI looks for `.env` files, in order, in the following locations: + +1. In the root folder of your project (`./.env`) +1. From the same folder as the schema specified by the `--schema` argument +1. From the same folder as the schema taken from `"prisma": {"schema": "/path/to/schema.prisma"}` in `package.json` +1. From the `./prisma` folder + +If a `.env` file is located in step 1., but additional, clashing `.env` variables are located in steps 2. - 4., the CLI will throw an error. For example, if you specify a `DATABASE_URL` variable in two different `.env` files, you will get the following error: + +``` +Error: There is a conflict between env vars in .env and prisma/.env +Conflicting env vars: + DATABASE_URL + +We suggest to move the contents of prisma/.env to .env to consolidate your env vars. +``` + +The following table describes where the Prisma CLI looks for the `.env` file: + +| **Command** | **Schema file location** | **`.env` file locations checked, in order** | +| :---------------------------------------------- | :--------------------------------------------------------------------------- | :-------------------------------------------------------- | +| `prisma [command]` | `./prisma/schema.prisma` | `./.env`
`./prisma/.env` | +| `prisma [command] --schema=./a/b/schema.prisma` | `./a/b/schema.prisma` | `./.env`
`./a/b/.env`
`./prisma/.env` | +| `prisma [command]` | `"prisma": {"schema": "/path/to/schema.prisma"}` | `.env`
`./path/to/schema/.env`
`./prisma/.env` | +| `prisma [command]` | No schema (for example, when running `prisma db pull` in an empty directory) | `./.env`
`./prisma/.env` | + +Any environment variables defined in that `.env` file will automatically be loaded when running a Prisma CLI command. + + + +**Do not commit your `.env` files into version control**! + + + +Refer to the `dotenv` documentation for information about [what happens if an environment variable is defined in two places](https://www.npmjs.com/package/dotenv#what-happens-to-environment-variables-that-were-already-set). + +### Expanding variables + +Variables stored in `.env` files can be expanded using the format specified by [dotenv-expand](https://github.com/motdotla/dotenv-expand). + +```env file=.env +DATABASE_URL=postgresql://test:test@localhost:5432/test +DATABASE_URL_WITH_SCHEMA=${DATABASE_URL}?schema=public +``` + +This will make the environment variable `DATABASE_URL_WITH_SCHEMA` with value `postgresql://test:test@localhost:5432/test?schema=public` available for Prisma. + +You can also use environment variables in the expansion that are set _outside_ of the `.env` file, for example a database URL that is set on a PaaS like Heroku or similar: + +```terminal +# environment variable already set in the environment of the system +export DATABASE_URL=postgresql://test:test@localhost:5432/test +``` + +```env file=.env +DATABASE_URL_WITH_SCHEMA=${DATABASE_URL}?schema=foo +``` + +This will make the environment variable `DATABASE_URL_WITH_SCHEMA` with value `postgresql://test:test@localhost:5432/test?schema=foo` available for Prisma. + +### Example: Set the `DATABASE_URL` environment variable in an `.env` file + +It is common to load your database connection URL from an environment variable: + +```prisma +// schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +You can set the `DATABASE_URL` in your `.env` file: + +```env file=.env +DATABASE_URL=postgresql://test:test@localhost:5432/test?schema=public +``` + +When you run a command that needs access to the database defined via the `datasource` block (for example, `prisma db pull`), the Prisma CLI automatically loads the `DATABASE_URL` environment variables from the `.env` file and makes it available to the CLI. diff --git a/docs/200-orm/800-more/500-development-environment/050-environment-variables/100-managing-env-files-and-setting-variables.mdx b/docs/200-orm/800-more/500-development-environment/050-environment-variables/100-managing-env-files-and-setting-variables.mdx new file mode 100644 index 0000000000..b9dacf121c --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/050-environment-variables/100-managing-env-files-and-setting-variables.mdx @@ -0,0 +1,150 @@ +--- +title: 'Managing .env files and setting variables' +metaTitle: 'Managing .env files and setting variables' +metaDescription: 'Learn how to manage .env files and set environment variables' +tocDepth: 3 +--- + + + +[Prisma creates an `.env` file for you upon installation](/orm/more/development-environment/environment-variables#how-does-prisma-use-environment-variables). You are not limited to using that file, some other options include: + +- Do not use `.env` files and let Prisma use the system environment variables directly +- Use `.env` files from a location that the Prisma CLI does not check [by default](/orm/more/development-environment/environment-variables/env-files) +- [Use multiple `.env` file](/orm/more/development-environment/environment-variables/using-multiple-env-files) + + + +## Using the system environment directly + +Because Prisma reads from the system's environment when looking for environment variables, it's possible to skip using `.env` completely and create them manually on your local system. + + + +The following examples will use setting the `DATABASE_URL` environment variable which is often used for the database connection URL. + + + +### Manually set an environment variable on a Mac/Linux system + +From a terminal on a Unix machine (Mac/Linux), you export the variable as a key value pair. + +```terminal +export DATABASE_URL=postgresql://test:test@localhost:5432/test?schema=public +``` + +Then check that it has been successfully set using `printenv`: + + + + + +```terminal +printenv DATABASE_URL +``` + + + + + +```code no-copy +postgresql://test:test@localhost:5432/test?schema=public +``` + + + + + +### Manually set an environment variable on a Windows system + +The following examples illustrate how to set the environment variable (for the current user) using both Command Prompt (`cmd.exe`) and PowerShell, depending on your preference. + + + + + +```terminal +set DATABASE_URL="postgresql://test:test@localhost:5432/test?schema=public" +``` + + + + + +```terminal +[Environment]::SetEnvironmentVariable("DATABASE_URL", "postgresql://test:test@localhost:5432/test?schema=public") +``` + + + + + +Then check that it has been successfully set: + + + + + +```terminal +set DATABASE_URL +``` + + + + + +```terminal +Get-ChildItem Env:DATABASE_URL +``` + + + + + +## Manage `.env` files manually + +The [`dotenv-cli`](https://www.npmjs.com/package/dotenv-cli) and [`dotenv`](https://www.npmjs.com/package/dotenv) packages can be used if you want to manage your `.env`files manually. + +They allow you to: + +- [Use multiple `.env` files](/orm/more/development-environment/environment-variables/using-multiple-env-files) +- Use `.env` files from a location that the Prisma CLI does not check [by default](/orm/more/development-environment/environment-variables/env-files) + +### Using `dotenv-cli` via command line + +The following steps show how to use the `dotenv-cli` package to use an alternative file to contain environment variables than the [default](/orm/more/development-environment/environment-variables/env-files) created by Prisma, which is then used to run Introspection. + +1. Install [`dotenv-cli`](https://www.npmjs.com/package/dotenv-cli): + + ``` + npm install -g dotenv-cli + ``` + +2. Create a file - for example, `.env3` - in your project's root folder. + +3. To use the `.env3` file, you can use `dotenv` when you run any Prisma command and specify which `.env` file to use. The following example uses a file named `.env3`: + + ``` + dotenv -e .env3 -- npx prisma db pull + ``` + +> **Note:** dotenv doesn't pass the flags to the Prisma command by default, this is why the command includes two dashes `--` before `prisma`, making it possible to use flags like `--force`, `--schema` or `--preview-feature`. + +### Using `dotenv` via application code + +The following steps show how to use the `dotenv` package to reference an alternative environment file in your project's code. + +1. Add [`dotenv`](https://www.npmjs.com/package/dotenv) to your project: + + ``` + npm install dotenv + ``` + +2. Create a file - for example, `.env3` - in your project's root folder. + +3. To use the `.env3` file, include a reference to `dotenv` at the top of your project's entry file. + + ```ts + import { config } from 'dotenv' + config({ path: '.env3' }) + ``` diff --git a/docs/200-orm/800-more/500-development-environment/050-environment-variables/200-using-multiple-env-files.mdx b/docs/200-orm/800-more/500-development-environment/050-environment-variables/200-using-multiple-env-files.mdx new file mode 100644 index 0000000000..3a92466ff6 --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/050-environment-variables/200-using-multiple-env-files.mdx @@ -0,0 +1,83 @@ +--- +title: 'Using multiple .env files' +metaTitle: 'Using multiple .env files.' +metaDescription: 'Learn how to set up a dedicated testing environment using multiple .env files.' +tocDepth: 3 +--- + + + +There is a risk that your production database could be deleted if you store different connection URLs to each of your environments within a single `.env` file. + +One solution is to have multiple `.env` files which each represent different environments. In practice, this means you create a file for each of your environments: + +- `.env.development` +- `.env.sample` + + + +`.env.production` is omitted from the above list as it is not recommended to store your production credentials locally, even if they are git-ignored. + + + +Then using a package like [`dotenv-cli`](https://www.npmjs.com/package/dotenv-cli), you can load the correct connection URL for the environment you are working in. + + + +## Setup multiple `.env` files + +For the purpose of this guide, it is assumed you have a dedicated development database that you use whilst developing your application. + +1. Rename your `.env` file to `.env.development` + +```env file=.env.development +DATABASE_URL="postgresql://prisma:prisma@localhost:5433/dev" +``` + +2. Create a new `.env.sample` file and change the database name to `sample` (or your preferred name) + +```env file=.env.sample +DATABASE_URL="postgresql://prisma:prisma@localhost:5433/sample" +``` + +3. Install [`dotenv-cli`](https://www.npmjs.com/package/dotenv-cli) + +In order for Prisma and Jest to know which `.env` file to use, alter your package.json scripts to include and call the `dotenv` package and specify which file to use depending on what commands you are running and in which environment you want them to run. + + + +Any top-level script that is running the tests and migrations needs the `dotenv` command before it. This makes sure that the env variables from `.env.sample` are passed to all commands, including Jest. + + + +### Running migrations on different environments + +You can use the [`dotenv-cli`](https://www.npmjs.com/package/dotenv-cli) package to specify which environment file Prisma should use when running a migration. + +The below script uses `dotenv-cli` to pass the `.env.sample` environment file (which holds a `DATABASE_URL` connection string) to the Prisma migration script. + +#### Migration script + +```json file=package.json + "scripts": { + "migrate:postgres": "dotenv -e .env.sample -- npx prisma migrate deploy", + }, +``` + +### Running tests on different environments + +When running tests, we advise you to [mock Prisma Client](/orm/prisma-client/testing/unit-testing#mocking-prisma-client). In doing so, you need to tell Jest which environment it should use when running its tests. + +By default, Prisma Client will use the environment specified in the default `.env` file located at the project's root. + +If you have created a separate `.env.sample` file to specify your testing database, then this environment will need to be passed to Jest. + +The below script uses `dotenv-cli` to pass the `.env.sample` environment file (which holds a `DATABASE_URL` connection string) to Jest. + +#### Test script + +```json file=package.json + "scripts": { + "test": "dotenv -e .env.sample -- jest -i" + }, +``` diff --git a/docs/200-orm/800-more/500-development-environment/050-environment-variables/index.mdx b/docs/200-orm/800-more/500-development-environment/050-environment-variables/index.mdx new file mode 100644 index 0000000000..406ba7e68c --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/050-environment-variables/index.mdx @@ -0,0 +1,47 @@ +--- +title: 'Environment variables' +metaTitle: 'Environment variables' +metaDescription: 'Learn how to use environment variables in your Prisma project' +tocDepth: 3 +--- + + + +An environment variable is a key value pair of string data that is stored on your machine's local environment. Refer to our [Environment variables reference documentation](/orm/reference/environment-variables-reference) for specific details. + +Typically the name of the variable is uppercase, this is then followed by an equals sign then the value of the variable: + +```env +MY_VALUE=prisma +``` + +The environment variable belongs to the environment where a process is running. + +Taking the `TEMP` environment variable as an example, one can query its value to find where to store temporary files. This is a system environment variable and can be queried by any process or application running on the machine. + +Any program can read and create these environment variables. They are a cheap and effective way to store simple information. + + + +## How does Prisma use environment variables? + +Prisma always reads environment variables from the system's environment. + +When you initialize Prisma in your project with `prisma init`, it creates a convenience `.env` file for you to set your [`connection url`](/orm/reference/connection-urls) as an environment variable. When you use Prisma CLI or Prisma Client, the `.env` file content and the variables defined in it are added to the [`process.env` object](https://nodejs.org/api/process.html#processenv), where Prisma can read it and use it. + + + +Looking to use more than one `.env` file? See [Using multiple `.env` files](/orm/more/development-environment/environment-variables/using-multiple-env-files) for information on how to setup and use multiple `.env` files in your application. + + + +### Using environment variables in your code + +If you want environment variables to be evaluated at runtime, you need to load them manually in your application code (for example, by using [`dotenv`](https://github.com/motdotla/dotenv)): + +```ts +import * as dotenv from 'dotenv' + +dotenv.config() // Load the environment variables +console.log(`The connection URL is ${process.env.DATABASE_URL}`) +``` diff --git a/docs/200-orm/800-more/500-development-environment/100-editor-setup.mdx b/docs/200-orm/800-more/500-development-environment/100-editor-setup.mdx new file mode 100644 index 0000000000..3bdadf6ee9 --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/100-editor-setup.mdx @@ -0,0 +1,73 @@ +--- +title: 'Editor setup' +metaTitle: 'Editor and IDE setup' +metaDescription: 'Learn how to configure your editor and IDEs for an optimal developer experience with Prisma.' +tocDepth: 3 +--- + + + +This page describes how you can configure your editor for an optimal developer experience when using Prisma. + +If you don't see your editor here, please [open a feature request](https://github.com/prisma/prisma/issues/new?assignees=&labels=&template=feature_request.md&title=) and ask for dedicated support for your editor (e.g. for syntax highlighting and auto-formatting). + + + +## VS Code + +You can install the official [Prisma VS Code extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma). + +## Community projects + +> **Note**: Community projects are not maintained or officially supported by Prisma and some features may by out of sync. Use at your own discretion. + +### Emacs + +- [emacs-prisma-mode](https://github.com/pimeys/emacs-prisma-mode) provides syntax highlighting of the Prisma Schema Language and uses the Prisma Language Server. + +### Vim + +- [vim-prisma](https://github.com/pantharshit00/vim-prisma) provides file detection and syntax highlighting of the Prisma Schema Language. + +### neovim + +- [coc-prisma](https://github.com/pantharshit00/coc-prisma) implements the Prisma Language Server. + +### JetBrains IDE + +- [Prisma ORM](https://plugins.jetbrains.com/plugin/20686-prisma-orm) Provided by JetBrains. This plugin provides PSL grammar, syntax highlighting, LSP, and more. + +### Sublime Text + +- [Prisma](https://packagecontrol.io/packages/Prisma) - For Sublime Text 3 & 4 - Provides syntax highlighting for the Prisma Schema Language. ([Source Code](https://github.com/Sublime-Instincts/PrismaHighlight/)) +- [LSP-prisma](https://packagecontrol.io/packages/LSP-prisma) - For Sublime Text 4 - Language Server helper package for Prisma schema files that uses Prisma's Language Server to provide linting, error checking, formatting, autocompletion, renaming etc. Note: It requires the Prisma package to be installed. ([Source Code](https://github.com/Sublime-Instincts/LSP-prisma)) + +### nova + +- [nova](https://extensions.panic.com/extensions/robb-j/robb-j.Prisma/) provides syntax highlighting of the Prisma Schema Language and uses the Prisma Language Server. + +### Helix + +- [Helix](https://helix-editor.com/) (from version 22.08) provides syntax highlighting of the Prisma Schema Language and uses the Prisma Language Server. + +### CLI autocomplete + +#### inshellisense + +You can get IDE-style autocompletion for Prisma CLI using [`inshellisense`](https://github.com/microsoft/inshellisense/tree/main). It supports: bash, zsh, fish, pwsh, powershell (Windows Powershell). + +To install, run: + +```shell +npm install -g @microsoft/inshellisense +``` + +#### Fig + +`inshellisense` is built on top of [Fig](https://fig.io/) which you can also use directly. It works in bash, zsh, and fish. + +To install, run: + +```shell +brew install fig +``` diff --git a/docs/200-orm/800-more/500-development-environment/index.mdx b/docs/200-orm/800-more/500-development-environment/index.mdx new file mode 100644 index 0000000000..7dd10bd168 --- /dev/null +++ b/docs/200-orm/800-more/500-development-environment/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'Development environment' +navTitle: Development environment +metaTitle: 'Development environment' +metaDescription: 'Using env vars, editors and workspaces in your development environment' +--- + + + +This section describes using `env` variables, editors, and workspaces in your development environment. + + + +## In this section + + diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/050-creating-bug-reports.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/050-creating-bug-reports.mdx new file mode 100644 index 0000000000..4f077d5196 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/050-creating-bug-reports.mdx @@ -0,0 +1,159 @@ +--- +title: 'Creating bug reports' +metaTitle: 'Creating bug reports for Prisma' +metaDescription: 'This page explains best practices for creating bug reports for Prisma, including sharing additional debugging output and other recommendations.' +--- + +## Overview + +You can help us improve Prisma by creating **bug reports**. When creating a bug report, it's important that you include as much information as possible about your issue. That way, it's easier to reproduce. + +Note that you can also create **feature requests** or ask a **question** via the issue templates on GitHub. + +## Where to open the bug report on GitHub? + +Prisma's tools are spread across different repositories in the [`prisma`](https://github.com/prisma/) organization on GitHub. You can open a new issue in the repo of the respective tool. + +If you're unsure where to open the GitHub issue, you can use the main [`prisma/prisma`](https://github.com/prisma/prisma) repo as a default. Our engineering team is regularly triaging new issues and will move the issue to another repo if necessary. + +## Ideal scenario: Share standalone repository with reproduction + +In an ideal scenario, you're able to reproduce the bug in an isolated environment and put it into a GitHub repository that you can share in your report. That way, we already have a reproduction and the problem can be tackled without further triaging. + +This [StackOverflow guide](https://stackoverflow.com/help/minimal-reproducible-example) has a lot of helpful information for creating minimal, reproducible examples. + +## Best practices for writing a bug report + +If you don't have the time to create a full reproduction of the issue, please include as much information as possible about the problem. The [bug report template](https://pris.ly/prisma-prisma-bug-report) helps you with that. + +### Include logging and debugging output + +Please make sure to include _any_ [logging](/orm/prisma-client/observability-and-logging/logging) and [debugging](/orm/prisma-client/debugging-and-troubleshooting/debugging) output in the issue that may help to identify the problem. + +**Setting the `DEBUG` env var** + +To get additional output from Prisma, you can set `DEBUG` to `*`: + +```terminal +export DEBUG="*" +``` + +**Print logs of Prisma Client** + +You can enable additional logs in Prisma Client by instantiating it with the `log` option: + +```ts +const prisma = new PrismaClient({ log: ['query', 'info', 'warn'] }) +``` + +### Include a bug description, reproduction and expected behavior + +When describing the bug, it's helpful to include the following information: + +- A clear and concise description of what the bug is +- Steps to reproduce the bug +- A clear and concise description of what you expected to happen +- Screenshots (if applicable) + +
+ +Expand for an example for a hypothetical bug report + +**Example** + +**Describe the bug** + +`@unique` attribute on `email` field doesn't work on my model. I can create duplicate records with the same `email`. + +**To reproduce**: + +I have this Prisma schema (removed all unnecessary models and fields): + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique +} +``` + +I then run `prisma generate` to generate Prisma Client. + +I then have a Node.js script that creates two `User` records with the same `email`: + +```ts +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +// A `main` function so that we can use async/await +async function main() { + const user1 = await prisma.create({ + data: { email: 'alice@prisma.io' }, + }) + const user2 = await prisma.create({ + data: { email: 'alice@prisma.io' }, + }) + console.log(user1, user2) +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) +``` + +**Expected behavior** + +I expected an exception when trying to create `user2` with the same `email` as `user1` because this violates the `@unique` constraint defined in the Prisma schema. + +
+ +### Include environment and setup information + +Please include any information about your environment and setup. Specifically it's important to include: + +- Which **operating system** you use (e.g. macOS, Windows, Debian, CentOS, ...) +- Which **database** you use with Prisma (PostgreSQL, MySQL, MariaDB, SQLite or Microsoft SQL Server) +- Which **version of Prisma** you use (run `prisma -v` to see your Prisma version) +- Which **version of Node.js** you use (run `node -v` to see your Node.js version) + +Here's an example of what this could look like in your bug report: + +- OS: macOS Catalina 10.15.7 +- Database: PostgreSQL v11 +- Node.js version: `v14.16.1` +- Prisma version: + +``` +prisma : 2.22.0 +@prisma/client : Not found +Current platform : darwin +Query Engine : query-engine 60cc71d884972ab4e897f0277c4b84383dddaf6c (at ../../../../../.npm/_npx/31227/lib/node_modules/prisma/node_modules/@prisma/engines/query-engine-darwin) +Migration Engine : migration-engine-cli 60cc71d884972ab4e897f0277c4b84383dddaf6c (at ../../../../../.npm/_npx/31227/lib/node_modules/prisma/node_modules/@prisma/engines/migration-engine-darwin) +Format Binary : prisma-fmt 60cc71d884972ab4e897f0277c4b84383dddaf6c (at ../../../../../.npm/_npx/31227/lib/node_modules/prisma/node_modules/@prisma/engines/prisma-fmt-darwin) +Default Engines Hash : 60cc71d884972ab4e897f0277c4b84383dddaf6c +Studio : 0.379.0 +``` + +Additionally, you can use the [`prisma debug`](/orm/reference/prisma-cli-reference#debug) command to retrieve debugging information. The `prisma debug` command provides debugging information that compliments the output of the `prisma -v` command. The information includes [environment variables](/orm/reference/environment-variables-reference) used for Prisma Client, Prisma Migrate, Prisma CLI, and Prisma Studio. + + + +The `prisma debug` command is available from version 5.6.0 and newer. If you're using an older version of Prisma, you can use this command by running: + +```terminal +npx prisma@latest debug +``` + + + +### Include relevant Prisma info (e.g. the Prisma schema, Prisma Client queries, ...) + +To help us reproduce your problem, it is helpful to include your Prisma schema in the bug report. **Please remove any database credentials before sharing your Prisma schema in a bug report**. If you're sure about which parts of the schema is causing the issue, please strip out the irrelevant parts of it and only show the parts that are related to the problem. If you're not sure, please include your entire schema. + +If you have an issue with Prisma Client, please also include which Prisma Client query is causing the issue. diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/100-autocompletion-in-graphql-resolvers-with-js.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/100-autocompletion-in-graphql-resolvers-with-js.mdx new file mode 100644 index 0000000000..0e14c2f877 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/100-autocompletion-in-graphql-resolvers-with-js.mdx @@ -0,0 +1,262 @@ +--- +title: 'Autocompletion in GraphQL resolvers with JavaScript' +metaTitle: 'Autocompletion in GraphQL resolvers with JavaScript' +metaDescription: 'Learn how you can get autocompletion for Prisma Client queries in GraphQL resolvers with plain JavaScript' +--- + +## Problem + +When using GraphQL with TypeScript, you always get autocompletion for the Prisma Client instance in your GraphQL resolvers because then the `context` object can be typed – no matter if folks are using Nexus, TypeGraphQL or SDL first. This immensely helps with autocompletion and preventing unwanted errors. + +Unfortunately, this needs a little more effort when you're working in plain JavaScript. Suppose we have a resolver like this: + +```js +filterPosts: (parent, args, ctx) => { + return ctx.prisma.post.findMany({ + where: { + OR: [ + { title: { contains: args.searchString } }, + { content: { contains: args.searchString } }, + ], + }, + }) +} +``` + +Now whenever you type `ctx.` VS Code will provide unnecessary options in the autocomplete which is undesirable. + +![Unwanted autocomplete values by VSCode](unwanted-autocomplete-values-in-vscode.png) + +VS Code doesn't know the _type_ of the `context` object so it can't provide any intellisense for it, which is why unwanted suggestions are displayed. + +## Solution + +To overcome this, you need to add a [JSDoc](https://jsdoc.app/) comment named `typedef` to "import" the correct type of your `PrismaClient` instance. + +```js +// Add this to the top of the file + +/** + * @typedef { import("@prisma/client").PrismaClient } Prisma + */ +``` + +> **Note**: You can learn more about JSDoc [here](https://devhints.io/jsdoc). + +Finally, you need to type your resolver arguments. For simplicity, ignore the `parent` and `args` parameters. So the resolver should now look like this: + +```js +/** + * @param {any} parent + * @param {{ searchString: string }} args + * @param {{ prisma: Prisma }} ctx + */ +filterPosts: (parent, args, ctx) => { + return ctx.prisma.post.findMany({ + where: { + OR: [ + { title: { contains: args.searchString } }, + { content: { contains: args.searchString } }, + ], + }, + }) +} +``` + +This will tell VS Code that the `context` has a property named `prisma` and the type is `Prisma` which was defined in the `@typedef` above. + +And voilà, autocompletion in plain JavaScript. + +![The correct parameters for context are obtained](prisma-autocompletion-in-js.png) + +The final file should look something like: + +```js +/** + * @typedef { import("@prisma/client").PrismaClient } Prisma + * @typedef { import("@prisma/client").UserCreateArgs } UserCreateArgs + */ + +const { makeExecutableSchema } = require('graphql-tools') + +const typeDefs = ` +type User { + email: String! + id: ID! + name: String + posts: [Post!]! +} + +type Post { + author: User + content: String + id: ID! + published: Boolean! + title: String! +} + + +type Query { + feed: [Post!]! + filterPosts(searchString: String): [Post!]! + post(where: PostWhereUniqueInput!): Post +} + +type Mutation { + createDraft(authorEmail: String, content: String, title: String!): Post! + deleteOnePost(where: PostWhereUniqueInput!): Post + publish(id: ID): Post + signupUser(data: UserCreateInput!): User! +} + +input PostWhereUniqueInput { + id: ID +} + +input UserCreateInput { + email: String! + id: ID + name: String + posts: PostCreateManyWithoutPostsInput +} + +input PostCreateManyWithoutPostsInput { + connect: [PostWhereUniqueInput!] + create: [PostCreateWithoutAuthorInput!] +} + +input PostCreateWithoutAuthorInput { + content: String + id: ID + published: Boolean + title: String! +} +` + +const resolvers = { + Query: { + /** + * @param {any} parent + * @param {any} args + * @param {{ prisma: Prisma }} ctx + */ + feed: (parent, args, ctx) => { + return ctx.prisma.post.findMany({ + where: { published: true }, + }) + }, + /** + * @param {any} parent + * @param {{ searchString: string }} args + * @param {{ prisma: Prisma }} ctx + */ + filterPosts: (parent, args, ctx) => { + return ctx.prisma.post.findMany({ + where: { + OR: [ + { title: { contains: args.searchString } }, + { content: { contains: args.searchString } }, + ], + }, + }) + }, + /** + * @param {any} parent + * @param {{ where: { id: string }}} args + * @param {{ prisma: Prisma }} ctx + */ + post: (parent, args, ctx) => { + return ctx.prisma.post.findUnique({ + where: { id: Number(args.where.id) }, + }) + }, + }, + Mutation: { + /** + * @param {any} parent + * @param {{ title: string, content: string, authorEmail: (string|undefined) }} args + * @param {{ prisma: Prisma }} ctx + */ + createDraft: (parent, args, ctx) => { + return ctx.prisma.post.create({ + data: { + title: args.title, + content: args.content, + published: false, + author: args.authorEmail && { + connect: { email: args.authorEmail }, + }, + }, + }) + }, + /** + * @param {any} parent + * @param {{ where: { id: string }}} args + * @param {{ prisma: Prisma }} ctx + */ + deleteOnePost: (parent, args, ctx) => { + return ctx.prisma.post.delete({ + where: { id: Number(args.where.id) }, + }) + }, + /** + * @param {any} parent + * @param {{ id: string }} args + * @param {{ prisma: Prisma }} ctx + */ + publish: (parent, args, ctx) => { + return ctx.prisma.post.update({ + where: { id: Number(args.id) }, + data: { published: true }, + }) + }, + /** + * @param {any} parent + * @param {UserCreateArgs} args + * @param {{ prisma: Prisma }} ctx + */ + signupUser: (parent, args, ctx) => { + return ctx.prisma.user.create(args) + }, + }, + User: { + /** + * @param {{ id: number }} parent + * @param {any} args + * @param {{ prisma: Prisma }} ctx + */ + posts: (parent, args, ctx) => { + return ctx.prisma.user + .findUnique({ + where: { id: parent.id }, + }) + .posts() + }, + }, + Post: { + /** + * @param {{ id: number }} parent + * @param {any} args + * @param {{ prisma: Prisma }} ctx + */ + author: (parent, args, ctx) => { + return ctx.prisma.post + .findUnique({ + where: { id: parent.id }, + }) + .author() + }, + }, +} + +const schema = makeExecutableSchema({ + resolvers, + typeDefs, +}) + +module.exports = { + schema, +} +``` + +So here's a simple method to get autocompletion for your all Prisma's methods in JavaScript. You can find a practical example of this approach in the [`prisma-examples`](https://github.com/prisma/prisma-examples/) repo [here](https://github.com/prisma/prisma-examples/tree/latest/javascript/graphql-sdl-first). diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/200-working-with-many-to-many-relations.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/200-working-with-many-to-many-relations.mdx new file mode 100644 index 0000000000..9828c2aa4d --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/200-working-with-many-to-many-relations.mdx @@ -0,0 +1,196 @@ +--- +title: 'Modeling and querying many-to-many relations' +metaTitle: 'Modeling and querying many-to-many relations' +metaDescription: 'Learn how you can model and query implicit and explicit many-to-many relations with Prisma' +tocDepth: 3 +--- + +## Problem + +Modeling and querying many-to-many relations in relational databases can be challenging. This article shows two examples how this can be approached with Prisma. The first example uses an [implicit](/orm/prisma-schema/data-model/relations/many-to-many-relations#implicit-many-to-many-relations) and the second one uses an [explicit](/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations) many-to-many relation. + +## Solution + +### Implicit relations + +This is a type of many-to-many relation where Prisma handles the [relation table](/orm/prisma-schema/data-model/relations/many-to-many-relations#relation-tables) internally. A basic example for an implicit many-to-many relation would look like this: + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + tags Tag[] +} + +model Tag { + id Int @id @default(autoincrement()) + name String @unique + posts Post[] +} +``` + +To create a post and its tags, one can write this with Prisma: + +```ts +await prisma.post.create({ + data: { + title: 'Types of relations', + tags: { create: [{ name: 'dev' }, { name: 'prisma' }] }, + }, +}) +``` + +In the above example, we can directly query for posts along with their tags as follows: + +```ts +await prisma.post.findMany({ + include: { tags: true }, +}) +``` + +And the response obtained would be: + +```json +[ + { + "id": 1, + "title": "Types of relations", + "tags": [ + { + "id": 1, + "name": "dev" + }, + { + "id": 2, + "name": "prisma" + } + ] + } +] +``` + +Another use case for this is if you want to add new tags as well as connect to existing tags to a post. An example for this is where a user has created new tags for their post and has also selected existing tags to be added as well. In this case, we can perform this in the following way: + +```ts +await prisma.post.update({ + where: { id: 1 }, + data: { + title: 'Prisma is awesome!', + tags: { set: [{ id: 1 }, { id: 2 }], create: { name: 'typescript' } }, + }, +}) +``` + +### Explicit relations + +Explicit relations mostly need to be created in cases where you need to store extra fields in the relation table or if you're [introspecting](/orm/prisma-schema/introspection) an existing database that already has many-to-many relations setup. This is the same schema used above but with an explicit relation table: + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + tags PostTags[] +} + +model PostTags { + id Int @id @default(autoincrement()) + post Post? @relation(fields: [postId], references: [id]) + tag Tag? @relation(fields: [tagId], references: [id]) + postId Int? + tagId Int? + + @@index([postId, tagId]) +} + +model Tag { + id Int @id @default(autoincrement()) + name String @unique + posts PostTags[] +} +``` + +Adding tags to a post would be a create into the relation table (`PostTags`) as well as into the tags table (`Tag`): + +```ts +await prisma.post.create({ + data: { + title: 'Types of relations', + tags: { + create: [ + { tag: { create: { name: 'dev' } } }, + { tag: { create: { name: 'prisma' } } }, + ], + }, + }, +}) +``` + +Also querying for posts along with their tags would require an extra `include` as follows: + +```ts +await prisma.post.findMany({ + include: { tags: { include: { tag: true } } }, +}) +``` + +This will provide the following output: + +```json +[ + { + "id": 1, + "title": "Types of relations", + "tags": [ + { + "id": 1, + "postId": 1, + "tagId": 1, + "tag": { + "id": 1, + "name": "prisma" + } + }, + { + "id": 2, + "postId": 1, + "tagId": 2, + "tag": { + "id": 2, + "name": "dev" + } + } + ] + } +] +``` + +Sometimes, it's not ideal to show the data for the relation table in your UI. In this case, it's best to map the data after fetching it on the server itself and sending that response to the frontend. + +```ts +const result = posts.map((post) => { + return { ...post, tags: post.tags.map((tag) => tag.tag) } +}) +``` + +This will provide an output similar to the one you received with implicit relations. + +```json +[ + { + "id": 1, + "title": "Types of relations", + "tags": [ + { + "id": 1, + "name": "prisma" + }, + { + "id": 2, + "name": "dev" + } + ] + } +] +``` + +This article showed how you can implement implicit and explicit many-to-many relations and query them using Prisma Client. diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/400-nextjs-prisma-client-dev-practices.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/400-nextjs-prisma-client-dev-practices.mdx new file mode 100644 index 0000000000..bc427f6d89 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/400-nextjs-prisma-client-dev-practices.mdx @@ -0,0 +1,73 @@ +--- +title: 'Best practice for instantiating PrismaClient with Next.js' +metaTitle: 'Best practice for instantiating PrismaClient with Next.js' +metaDescription: 'Best practice for instantiating PrismaClient with Next.js' +--- + +## Problem + +Lots of users have come across this warning while working with [Next.js](https://nextjs.org/) in development: + +``` +warn(prisma-client) There are already 10 instances of Prisma Client actively running. +``` + +There's a related [discussion](https://github.com/prisma/prisma/discussions/4399) and [issue](https://github.com/prisma/prisma/issues/5103) for the same. + +In development, the command `next dev` clears Node.js cache on run. This in turn initializes a new `PrismaClient` instance each time due to hot reloading that creates a connection to the database. This can quickly exhaust the database connections as each `PrismaClient` instance holds its own connection pool. + +## Solution + +The solution in this case is to instantiate a single instance `PrismaClient` and save it on the [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) object. Then we keep a check to only instantiate `PrismaClient` if it's not on the `globalThis` object otherwise use the same instance again if already present to prevent instantiating extra `PrismaClient` instances. + +```ts file=db.ts +import { PrismaClient } from '@prisma/client' + +const prismaClientSingleton = () => { + return new PrismaClient() +} + +declare global { + var prisma: undefined | ReturnType +} + +const prisma = globalThis.prisma ?? prismaClientSingleton() + +export default prisma + +if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma +``` + +You can extend Prisma Client using a Prisma Client extension by appending the `$extends` client method when instantiating Prisma Client as follows: + +```ts +import { PrismaClient } from '@prisma/client' + +const prismaClientSingleton = () => { + return new PrismaClient().$extends({ + result: { + user: { + fullName: { + needs: { firstName: true, lastName: true }, + compute(user) { + return `${user.firstName} ${user.lastName}` + }, + }, + }, + }, + }) +} +``` + +After creating this file, you can now import the extended `PrismaClient` instance anywhere in your Next.js `pages` as follows: + +```ts +// e.g. in `pages/index.tsx` +import prisma from './db' + +export const getServerSideProps = async () => { + const posts = await prisma.post.findMany() + + return { props: { posts } } +} +``` diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/425-nextjs-prisma-client-monorepo.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/425-nextjs-prisma-client-monorepo.mdx new file mode 100644 index 0000000000..a004be99c0 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/425-nextjs-prisma-client-monorepo.mdx @@ -0,0 +1,100 @@ +--- +title: 'Using Prisma Client in a Next.js project in a monorepo setup' +metaTitle: 'Using Prisma Client in a Next.js project in a monorepo setup' +metaDescription: 'Using Prisma Client in a Next.js project in a monorepo setup' +--- + +## Problem + +If you use Prisma Client in a [Next.js](https://nextjs.org/) application within a monorepo, you may run into an error that looks similar to: + +```terminal no-copy wrap +Prisma Client could not locate the Query Engine for runtime "debian-openssl-3.0.x". + +We detected that you are using Next.js, learn how to fix this: https://pris.ly/d/engine-not-found-nextjs. + +This is likely caused by tooling that has not copied "libquery_engine-debian-openssl-3.0.x.so.node" to the deployment folder. +Ensure that you ran \`prisma generate\` and that "libquery_engine-debian-openssl-3.0.x.so.node" has been copied to "generated/client". + +We would appreciate if you could take the time to share some information with us. +Please help us by answering a few questions: https://pris.ly/engine-not-found-tooling-investigation +``` + +or: + +```terminal no-copy wrap +Prisma Client could not locate the Query Engine for runtime "debian-openssl-3.0.x". + +We detected that you are using Next.js, learn how to fix this: https://pris.ly/d/engine-not-found-nextjs. + +This is likely caused by a bundler that has not copied "libquery_engine-debian-openssl-3.0.x.so.node" next to the resulting bundle. +Ensure that "libquery_engine-debian-openssl-3.0.x.so.node" has been copied next to the bundle or in "generated/client". + +We would appreciate if you could take the time to share some information with us. +Please help us by answering a few questions: https://pris.ly/engine-not-found-bundler-investigation +``` + +Assume you have a monorepo with the following structure: + +``` +. +├── packages +│ ├── db +│ │ ├── index.ts +│ │ ├── node_modules +│ │ ├── package.json +│ │ └── prisma +│ │ ├── client // <-- Custom output location for the generated Prisma Client +│ │ │ ├── index.js +│ │ │ ├── libquery_engine-debian-openssl-1.1.x.so.node // engine to be copied +│ │ │ └── schema.prisma // schema to be copied +│ │ └── schema.prisma +│ └── service/ +│ ├── pages/ +│ │ └── api/ +│ │ └── test.js +│ ├── next.config.js +│ └── package.json +├── pnpm-workspace.yaml +├── package.json +└── vercel.json +``` + +The file tree above shows a monorepo contained in a `packages` folder. Inside, there are two packages: + +- `db`: Contains the generated Prisma Client in a custom output location named `client`. `index.ts` at the root of this package exports the instantiated Prisma Client. +- `service`: Contains a Next.js application. The `test.js` API route uses the Prisma Client instance provided by the `db` package. + +The errors mentioned above occur as a result of a bundling problem during Next.js's bundling process. The Query Engine file(s) is expected to be found next to the generated Prisma Client. The bundling process, however, does not copy over those files to the output location of the bundle. + + + +For a more detailed explanation of exactly what is going wrong during the bundling process, please refer to [this issue](https://github.com/vercel/next.js/issues/46070) we opened in the Next.js GitHub repository. + + + +## Solution + +To work around this issue, you can use a custom Webpack plugin we created that correctly copies the files Prisma Client needs to their correct location. + +To use this plugin, first install the package: + +```terminal copy +npm install -D @prisma/nextjs-monorepo-workaround-plugin +``` + +You can then import the plugin into your `next.config.js` file and use it in `config.plugins`. For example: + +```js copy +const { PrismaPlugin } = require('@prisma/nextjs-monorepo-workaround-plugin') + +module.exports = { + webpack: (config, { isServer }) => { + if (isServer) { + config.plugins = [...config.plugins, new PrismaPlugin()] + } + + return config + }, +} +``` diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/450-pkg-issue.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/450-pkg-issue.mdx new file mode 100644 index 0000000000..918c5d4965 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/450-pkg-issue.mdx @@ -0,0 +1,27 @@ +--- +title: 'Solve package error with vercel/pkg' +metaTitle: 'Solve ENOENT package error with vercel/pkg' +metaDescription: 'Solve ENOENT package error with vercel/pkg' +--- + +## Problem + +If you use [vercel/pkg](https://github.com/vercel/pkg) to package your Node.js project, then you might encounter an `ENOENT` error like the following: + +``` +spawn /snapshot/enoent-problem/node_modules/.prisma/client/query-engine-debian-openssl-1.1.x ENOENT +``` + +## Solution + +To avoid this error, add your Prisma query engine binary path to the `pkg/assets` section of your `package.json` file, as follows: + +```json file=package.json copy +{ + "pkg": { + "assets": ["node_modules/.prisma/client/*.node"] + } +} +``` + +See [this Github issue](https://github.com/prisma/prisma/issues/8449) for further discussion. diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/500-comparing-columns-through-raw-queries.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/500-comparing-columns-through-raw-queries.mdx new file mode 100644 index 0000000000..8075e1fae7 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/500-comparing-columns-through-raw-queries.mdx @@ -0,0 +1,211 @@ +--- +title: 'Compare columns of the same table with raw queries' +metaTitle: 'Compare columns of the same table with raw queries' +metaDescription: 'Compare columns with inbuilt raw query methods in Prisma' +--- + +## Problem + +Comparing different columns from the same table is a common scenario that developers encounter. Some examples include comparing two numeric values in the same table or comparing two dates in a same table. There's an existing [GitHub Issue](https://github.com/prisma/prisma/issues/5048) regarding the same. + + + +From version 4.3.0, you do not need to use raw queries to compare columns in the same table. You can use the `.fields` property to compare the columns. [Learn more](/orm/reference/prisma-client-reference#compare-columns-in-the-same-table) + + + +## Workaround + +Comparing values from two columns in the same table can be achieved by using [raw queries](/orm/prisma-client/queries/raw-database-access/raw-queries). + +### Comparing numeric values + + + +From version 4.3.0, you do not need to use raw queries to compare columns in the same table. You can use the `.fields` property to compare the columns. [Learn more](/orm/reference/prisma-client-reference#compare-columns-in-the-same-table) + + + +One use case for comparing values from different columns would be retrieving posts that have more comments than likes; in this case, you need to compare the values of `commentsCount` and `likesCount`. + +```prisma +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + title String + content String? + published Boolean @default(false) + author User @relation(fields: [authorId], references: [id]) + authorId Int + likesCount Int + commentsCount Int +} +``` + +Queries (depending upon which database) could look something like: + +_PostgreSQL / CockroachDB_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateNumbersComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM "public"."Post" WHERE "likesCount" < "commentsCount";` + + console.log(response) +} + +await initiateNumbersComparisonRawQuery() +``` + +_MySQL_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateNumbersComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM \`public\`.\`Post\` WHERE \`likesCount\` < \`commentsCount\`;` + + console.log(response) +} + +await initiateNumbersComparisonRawQuery() +``` + +_Sqlite_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateNumbersComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM "Post" WHERE "likesCount" < "commentsCount";` + + console.log(response) +} + +await initiateNumbersComparisonRawQuery() +``` + +Running the above queries (depending upon the database) would filter posts that has fewer likes compared to comments. + +_Query Response_ + +```js +;[ + { + id: 1, + createdAt: '2022-03-03T12:08:11.421+00:00', + updatedAt: '2022-03-03T12:08:11.422+00:00', + title: 'Hello World', + content: 'This is my first post', + published: false, + authorId: 1, + likesCount: 50, + commentsCount: 100, + }, +] +``` + +### Comparing date values + + + +From version 4.3.0, you do not need to use raw queries to compare columns in the same table. You can use the `.fields` property to compare the columns. [Learn more](/orm/reference/prisma-client-reference#compare-columns-in-the-same-table) + + + +Similarly, if you need to compare dates, you could also achieve the same thing using raw queries. + +For example, a use case could be to get all projects completed after the due date. + +```prisma +model Project { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int + dueDate DateTime + completedDate DateTime + createdAt DateTime @default(now()) +} +``` + +Queries (depending upon the database) could look something like: + +_PostgreSQL / CockroachDB_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateDatesComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM "public"."Project" WHERE "completedDate" > "dueDate";` + + console.log(response) +} + +await initiateDatesComparisonRawQuery() +``` + +_MySQL_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateDatesComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM \`public\`.\`Project\` WHERE \`completedDate\` > \`dueDate\`;` + + console.log(response) +} + +await initiateDatesComparisonRawQuery() +``` + +_Sqlite_ + +```js +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +async function initiateDatesComparisonRawQuery() { + const response = + await prisma.$queryRaw`SELECT * FROM "Project" WHERE "completedDate" > "dueDate";` + + console.log(response) +} + +await initiateDatesComparisonRawQuery() +``` + +Running the above query would fetch projects where `completedDate` is after the `dueDate`. + +_Query Response_ + +```js +;[ + { + id: 1, + title: 'Project 1', + authorId: 1, + dueDate: '2022-03-10T00:00:00+00:00', + completedDate: '2022-03-12T00:00:00+00:00', + createdAt: '2022-03-03T12:08:11.421+00:00', + }, +] +``` diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/600-vercel-caching-issue.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/600-vercel-caching-issue.mdx new file mode 100644 index 0000000000..20aca0f2b3 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/600-vercel-caching-issue.mdx @@ -0,0 +1,99 @@ +--- +title: 'Vercel build dependency caching workaround' +metaTitle: 'Learn to configure your build process on Vercel to avoid caching-related problems' +metaDescription: 'Learn to configure your build process on Vercel to avoid caching-related problems' +--- + +## Problem + +If you deploy an application using Prisma to [Vercel](https://vercel.com/), you may run into the following error message on deployment: + +``` +Prisma has detected that this project was built on Vercel, which caches dependencies. +This leads to an outdated Prisma Client because Prisma's auto-generation isn't triggered. +To fix this, make sure to run the `prisma generate` command during the build process. + +Learn how: https://pris.ly/d/vercel-build +``` + +This occurs because Vercel caches the dependencies of your project until one of those dependencies changes. It does this to allow faster builds, and while this is typically a good thing, it causes some problems for Prisma Client. + +Prisma uses a `postinstall` hook to generate Prisma Client when dependencies are installed. Because Vercel uses cached modules, this `postinstall` hook never gets run in subsequent deployments after the initial deployment. This results in Prisma Client becoming out of sync with your database schema. + +This error message prevents this situation from happening and directs you here to learn how to fix the root issue. + +
+ +Prisma Client versions below 4.13.0 + +On Prisma Client versions lower than 4.13.0, you may encounter error messages that look like the following: + +``` +// 1: When adding a field: +Unknown arg `name` in data.name for type UserCreateInput. Did you mean `nick`? + +// 2: When removing a field: +Invalid `prisma.user.create()` invocation: The column `User.name` does not exist in the current database. + +// 3: When a model was removed/renamed +Invalid `prisma.user.deleteMany()` invocation: The table `public.User` does not exist in the current database. + +// 4: When a model was added +Cannot read properties of undefined (reading 'create') +``` + +The solutions described in this guide are meant to solve these problems. + +
+ +## Solution + +This issue can be solved by explicitly generating Prisma Client on every deployment. Running `prisma generate` before each deployment will ensure Prisma Client is up-to-date. + +You can configure the deployment to run this command in multiple different ways: + +### A custom `postinstall` script + + + +This is the preferred method as it is a universal solution. + + + +Within the `scripts` section of your project's `package.json` file, if there is not already a script named `postinstall`, add one and add `prisma generate` to that script: + +```json highlight=4;add +{ + ... + "scripts" { + "postinstall": "prisma generate" + } + ... +} +``` + +### The application's `build` script in `package.json` + +Within the `scripts` section of your project's `package.json` file, within the `build` script, prepend `prisma generate` to the default `vercel build` command: + +```json highlight=4;add +{ + ... + "scripts" { + "build": "prisma generate && " + } + ... +} +``` + +### Vercel UI's build script field + +Another way to configure `prisma generate` to be run on every deployment is to add the command to the build settings via Vercel's UI. + +Within your project's dashboard, go to the **Settings** tab and find the **General** section. In that section you will find a box labeled **Build & Development Settings** that contains an input field named **Build Command**: + +![Vercel project dashboard's Build Command setting](./vercel-ui-build-command.png) + +Within that field, prepend `prisma generate` to the existing script: + +![Vercel project dashboard's Build Command setting filled](./vercel-ui-build-command-filled.png) diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/700-netlify-caching-issue.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/700-netlify-caching-issue.mdx new file mode 100644 index 0000000000..e71d57880b --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/700-netlify-caching-issue.mdx @@ -0,0 +1,101 @@ +--- +title: 'Netlify build dependency caching workaround' +metaTitle: 'Learn to configure your build process on Netlify to avoid caching-related problems' +metaDescription: 'Learn to configure your build process on Netlify to avoid caching-related problems' +--- + +## Problem + +If you deploy an application using Prisma to [Netlify](https://netlify.com/), you may run into the following error message on deployment: + +``` +Prisma has detected that this project was built on Netlify, which caches dependencies. +This leads to an outdated Prisma Client because Prisma's auto-generation isn't triggered. +To fix this, make sure to run the \`prisma generate\` command during the build process. + +Learn how: https://pris.ly/d/netlify-build +``` + +This occurs because Netlify caches the dependencies of your project until one of those dependencies changes. It does this to allow faster builds, and while this is typically a good thing, it causes some problems for Prisma Client. + +Prisma uses a `postinstall` hook to generate Prisma Client when dependencies are installed. Because Netlify uses cached modules, this `postinstall` hook never gets run in subsequent deployments after the initial deployment. This results in Prisma Client becoming out of sync with your database schema. + +This error message prevents this situation from happening and directs you here to learn how to fix the root issue. + +
+ +Prisma Client versions below 4.13.0 + +On Prisma Client versions lower than 4.13.0, you may encounter error messages that look like the following: + +``` +// 1: When adding a field: +Unknown arg `name` in data.name for type UserCreateInput. Did you mean `nick`? + +// 2: When removing a field: +Invalid `prisma.user.create()` invocation: The column `User.name` does not exist in the current database. + +// 3: When a model was removed/renamed +Invalid `prisma.user.deleteMany()` invocation: The table `public.User` does not exist in the current database. + +// 4: When a model was added +Cannot read properties of undefined (reading 'create') +``` + +The solutions described in this guide are meant to solve these problems. + +
+ +## Solution + +This issue can be solved by explicitly generating Prisma Client on every deployment. Running `prisma generate` before each deployment will ensure Prisma Client is up-to-date. + +You can configure the deployment to run this command in multiple different ways: + +### A custom `postinstall` script + + + +This is the preferred method as it is a universal solution. + + + +Within the `scripts` section of your project's `package.json` file, if there is not already a script named `postinstall`, add one and add prisma generate` in that script: + +```json highlight=4;add +{ + ... + "scripts" { + "postinstall": "prisma generate" + } + ... +} +``` + +### The application's `build` script in `package.json` + +Within the `scripts` section of your project's `package.json` file, within the `build` script, prepend `prisma generate` to the existing build command: + +```json highlight=4;add +{ + ... + "scripts" { + "build": "prisma generate && " + } + ... +} +``` + +### Netlify UI's build script field + +Another way to configure `prisma generate` to be run on every deployment is to add the command to the build settings via Netlify's UI. + +Within your project's dashboard, go to the **Site Settings** tab and find the **Build & deploy** section. In that section, enter the **Continuous deployment** subsection. + +Find the box in that section labeled **Build settings** and click the **Edit settings** button: + +![Netlify project dashboard's Build settings button](./netlify-edit-settings.png) + +Clicking that button will open a form with various fields. Find the **Build command** field and prepend `prisma generate` to the existing script: + +![Netlify project dashboard's Build command setting filled](./netlify-build-command-filled.png) diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/800-check-constraints.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/800-check-constraints.mdx new file mode 100644 index 0000000000..217d6ad77d --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/800-check-constraints.mdx @@ -0,0 +1,453 @@ +--- +title: 'Data validation with CHECK constraints (PostgreSQL)' +metaTitle: 'Data validation with CHECK constraints (PostgreSQL)' +metaDescription: 'Learn how to configure CHECK constraints for data validation with Prisma and PostgreSQL by following the step-by-step instructions in this practical guide.' +--- + +## Overview + +This page explains how to configure [check constraints](https://www.postgresql.org/docs/9.4/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS) in a PostgreSQL database. A check constraint is a condition that must be satisfied before a value can be saved to a table - for example, the discounted price of a product must always be less than the original price. + +Check constraints can be added when you create the table (using `CREATE TABLE`) or to a table that already exists (using `ALTER TABLE`). This guide covers all four combinations. + +At the end of the guide, you'll introspect your database, generate a Prisma Client, and write a simple Node.js script to validate the constraints. + +## Prerequisites + +In order to follow this guide, you need: + +- a [PostgreSQL](https://www.postgresql.org/) database server running +- the [`createdb`](https://www.postgresql.org/docs/9.1/app-createdb.html) command line utility +- the [`psql`](https://www.postgresql.org/docs/13/app-psql.html) command line client for PostgreSQL +- [Node.js](https://nodejs.org/) installed on your machine + +## 1. Create a new database and project directory + +Start by creating a project directory for the files that you'll create throughout this guide. Open terminal or command line and run the following commands: + +``` +mkdir check-demo +cd check-demo +``` + +Next, make sure that your PostgreSQL database server is running. Authenticate the default `postgres` user: + +Unix (bash): + +``` +sudo -u postgres +``` + +Windows (command line): + +``` +psql -U postgres +``` + +Then execute the following command in your terminal to create a new database called `CheckDemo`: + +Unix (bash): + +``` +createdb CheckDemo +``` + +Windows (command line): + +``` +create database CheckDemo; +\connect CheckDemo +``` + +> _Tip_: Remember the trailing `;`! `postgres=#` `postgres-#` + +You can validate that the database was created by running the `\dt` command which lists all tables (_relations_) in your database (right now there are none): + +Unix (bash): + +``` +psql -d CheckDemo -c "\dt" +``` + +Windows (command line): + +``` +-d CheckDemo -c \dt +``` + +## 2. Adding a table with a single check constraint on a single column + +In this section, you'll **create a new table with a single check constraint on a single column** in the `CheckDemo` database. + +Create a new file named `single-column-check-constraint.sql` and add the following code to it: + +```sql +CREATE TABLE "public"."product" ( + price NUMERIC CONSTRAINT price_value_check CHECK (price > 0.01 AND price <> 1240.00) +); +ALTER TABLE "public"."product" + ADD COLUMN "productid" serial, + ADD PRIMARY KEY ("productid"); +``` + +Now run the SQL statement against your database to create a new table called `product`: + +Unix (bash): + +``` +psql CheckDemo < single-column-check-constraint.sql +``` + +Windows (command line): + +``` +\i 'c:/checkdemo/single-column-check-constraint.sql' +``` + +Congratulations, you just created a table called `product` in the database. The table has one column called `price`, which has a single check constraint that ensures price of a product is: + +- Never less than 0.01 +- Never equal to 1240.00 + +Run the following command to see the a list of check constraints that apply to the `product` table: + +``` +\d+ product +``` + +You will see the following output, which includes a list of all check constraints: + +``` +Table "public.product" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + price | numeric | | | | main | | +Check constraints: + "price_value_check" CHECK (price > 0.01 AND price <> 1240.00) +``` + +Note that PostgreSQL will auto-generate a constraint name if you do not provide one. For example, the constraint created by `price NUMERIC CHECK (price > 0.01 AND price <> 1240.00)` would be `price_check`. + +## 3. Adding a table with a multi-column check constraint + +Next, you'll **create a table with a multi-column check constraint** that compares the values of two columns. + +Create a new file named `multi-column-check-constraint.sql` and add the following code to it: + +```sql +CREATE TABLE "public"."anotherproduct" ( + reducedprice NUMERIC CONSTRAINT reduced_price_check CHECK (price > reducedprice), + price NUMERIC +); +ALTER TABLE "public"."anotherproduct" + ADD COLUMN "productid" serial, + ADD PRIMARY KEY ("productid"); +``` + +Now run the SQL statement against your database to create a new table called `anotherproduct`: + +Unix (bash): + +``` +psql CheckDemo < multi-column-check-constraint.sql +``` + +Windows (command line): + +``` +\i 'c:/checkdemo/multi-column-check-constraint.sql' +``` + +Congratulations, you just created a table called `anotherproduct` in the database. The table has two columns called `reducedprice` and `price`. The `reducedprice` column has a check constraint that ensures that the value of `reducedprice` is always less than the value of `price`. + +## 4. Adding a table with multiple check constraints + +Next, you'll **create a table with multiple check constraint** on different columns. + +Create a new file named `multiple-check-constraints.sql` and add the following code to it: + +```sql + CREATE TABLE "public"."secondtolastproduct" ( + reducedprice NUMERIC CONSTRAINT reduced_price_check CHECK (price > reducedprice), + price NUMERIC, + tags TEXT[] CONSTRAINT tags_contains_product CHECK ('product' = ANY(tags)) + ); +ALTER TABLE "public"."secondtolastproduct" + ADD COLUMN "productid" serial, + ADD PRIMARY KEY ("productid"); +``` + +Now run the SQL statement against your database to create a new table called `secondtolastproduct`: + +Unix (bash): + +``` +psql CheckDemo < multiple-check-constraints.sql +``` + +Windows (command line): + +``` +\i 'c:/checkdemo/multiple-check-constraints.sql' +``` + +Congratulations, you just created a table called `lastproduct` in the database. The table has three columns named `reducedprice`, `price` and `tags`, and the following check constraints: + +- The `tags` column (which is an array) must contain a tag named `product` +- The value of `reducedprice` must be less than the value of `price` + +## 5. Adding a check constraint to an existing table + +In this section, you'll **add a check constraint to a table that already exists in your database**. To do so, you first need to create a new table and then alter the table to add the constraint. + +Create a new file named `add-single-check-constraint-later.sql` and add the following code: + +```sql +CREATE TABLE "public"."lastproduct" ( + category TEXT +); + +ALTER TABLE "public"."lastproduct" + ADD CONSTRAINT "category_not_clothing" CHECK (category <> 'clothing'); +``` + +This code contains two SQL statements: + +1. Create a new table called `lastproduct` +2. Alter the table to add a check constraint named `price_not_zero_constraint` + +Now run the SQL statements against your database to create a new table called `lastproduct`: + +Unix (bash): + +``` +psql CheckDemo < add-single-check-constraint-later.sql +``` + +Windows (command line): + +``` +\i 'c:/checkdemo/add-single-check-constraint-later.sql' +``` + +Congratulations, you just created a table called `lastproduct` in the database with a single column called `price`. You added constraint named `price_not_zero_constraint` to with a second SQL command, which ensures that the price of a product is never less than 0.01. + +## 6. Introspect your database with Prisma + +In the previous sections, you created four tables with different check constraints: + +- The `product` table has a check constraint that ensures that the value of `price` is never less than `0.01` and never exactly `1240.00`. +- The `anotherproduct` table has a check constraint that ensures that the value of `reducedprice` is never greater than the value of `price`. +- The `secondtolastproduct` table has two check constraints - one that ensures that the value of `reducedprice` is never greater than the value of `price`, and one that ensures that the `tags` array always contains the value `product`. +- The `lastproduct` table has a check constraint that ensures that the value of `category` is never `clothing`. + +In this section you'll introspect your database to generate the Prisma models for these tables. + +> **Note**: Check constraints are currently not included in the generated Prisma schema - however, the underlying database still enforces the constraints. + +To start, set up a new Node.js project and add the `prisma` CLI as a development dependency: + +``` +npm init -y +npm install prisma --save-dev +``` + +In order to introspect your database, you need to tell Prisma how to connect to it. You do so by configuring a `datasource` in your Prisma schema. + +Create a new file named `schema.prisma` and add the following code to it: + +```prisma file=schema.prisma +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +The database connection URL is set via an environment variable. The Prisma CLI automatically supports the [`dotenv`](https://github.com/motdotla/dotenv) format which automatically picks up environment variables defined in a file named `.env`. + +Create a new file named `.env` and set your database connection URL as the `DATABASE_URL` environment variable: + +``` +DATABASE_URL=postgresql://__USER__:__PASSWORD__@__HOST__:__PORT__/CheckDemo +``` + +In the above code snippet, you need to replace the uppercase placeholders with your own connection details. For example, if your database is running locally it could look like this: + +``` +DATABASE_URL=postgresql://janedoe:mypassword@localhost:5432/CheckDemo +``` + +With both the `schema.prisma` and `.env` files in place, you can run Prisma's introspection with the following command: + +``` +npx prisma db pull +``` + +This command introspects your database and for each table adds a Prisma model to the Prisma schema: + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model anotherproduct { + price Float? + productid Int @id + reducedprice Float? +} + +model lastproduct { + category String? + productid Int @id +} + +model product { + price Float? + productid Int @id +} + +model secondtolastproduct { + price Float? + productid Int @id + reducedprice Float? + tags String[] +} +``` + +## 7. Generate Prisma Client + +To validate whether the check constraints work, you'll now generate Prisma Client and send a few sample queries to the database. + +First, add a `generator` block to your Prisma schema (typically added right below the `datasource` block): + +```prisma file=schema.prisma +generator client { + provider = "prisma-client-js" +} +``` + +Run the following command to install and generate Prisma Client in your project: + +``` +npx prisma generate +``` + +Now you can use Prisma Client to send database queries in Node.js. + +## 8. Validate the check constraints in a Node.js script + +Create a new file named `index.js` and add the following code to it: + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +async function main() { + const newProduct = await prisma.product.create({ + data: { + price: 0.0, + }, + }) + + console.log(newProduct) +} + +main() +``` + +In this code, you're creating a product with a price of `0.00`, which does not meet the check constraint configured for the `price` column. + +Run the code with this command: + +``` +node index.js +``` + +The script throws an error indicating that the `price_check_value` check constraint was not met: + +``` +Error occurred during query execution: +ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Error { kind: Db, cause: Some(DbError { severity: "ERROR", parsed_severity: Some(Error), code: SqlState("23514"), message: "new row for relation \"product\" violates check constraint \"price_value_check\"", detail: Some("Failing row contains (0, 11)."), hint: None, position: None, where_: None, schema: Some("public"), table: Some("product"), column: None, datatype: None, constraint: Some("price_value_check"), file: Some("d:\\pginstaller_12.auto\\postgres.windows-x64\\src\\backend\\executor\\execmain.c"), line: Some(2023), routine: Some("ExecConstraints") }) }) }) +``` + +To validate the multi-column check constraint, replace the code in `index.js` with the following: + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +async function main() { + const newProduct = await prisma.anotherproduct.create({ + data: { + price: 50.0, + reducedprice: 100.0, + }, + }) + + console.log(newProduct) +} + +main() +``` + +In this code, you're creating a product where the reduced price is higher than the actual price. + +Run the script again with this command: + +``` +node index.js +``` + +This time, you'll see a similar error message indicating the `reduce_price_check` check constraint was not met: + +``` +ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Error { kind: Db, cause: Some(DbError { severity: "ERROR", parsed_severity: Some(Error), code: SqlState("23514"), message: "new row for relation \"anotherproduct\" violates check constraint \"reduced_price_check\"", detail: Some("Failing row contains (100, 50, 1)."), hint: None, position: None, where_: None, schema: Some("public"), table: Some("anotherproduct"), column: None, datatype: None, constraint: Some("reduced_price_check"), file: Some("d:\\pginstaller_12.auto\\postgres.windows-x64\\src\\backend\\executor\\execmain.c"), line: Some(2023), routine: Some("ExecConstraints") }) }) }) + at PrismaClientFetcher.request (C:\Work\Personal\prisma-check-constraint\node_modules\@prisma\client\index.js:89:17) +``` + +Finally, modify the script to include multiple check constraint violations: + +```js +const { PrismaClient } = require('@prisma/client') + +const prisma = new PrismaClient() + +async function main() { + const newProduct = await prisma.secondtolastproduct.create({ + data: { + tags: { + set: ['wrongtag'], + }, + price: 90.0, + reducedprice: 100.0, + }, + }) + + console.log(newProduct) +} + +main() +``` + +In this code, you're creating a product where the reduced price is higher than the actual price, and omitting the required `product` tag. + +Run the script again with this command: + +``` +node index.js +``` + +Notice that the error message only mentions the `reduced_price_check` constraint: + +``` +ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Error { kind: Db, cause: Some(DbError { severity: "ERROR", parsed_severity: Some(Error), code: SqlState("23514"), message: "new row for relation \"secondtolastproduct\" violates check constraint \"reduced_price_check\"", detail: Some("Failing row contains (100, 90, {wrongtag}, 7)."), hint: None, position: None, where_: None, schema: Some("public"), table: Some("secondtolastproduct"), column: None, datatype: None, constraint: Some("reduced_price_check"), file: Some("d:\\pginstaller_12.auto\\postgres.windows-x64\\src\\backend\\executor\\execmain.c"), line: Some(2023), routine: Some("ExecConstraints") }) }) }) +``` + +Check constraints are resolved in alphabetical order, and only the first constraint to fail appears in the error message. diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/index.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/index.mdx new file mode 100644 index 0000000000..c6c28918a4 --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/index.mdx @@ -0,0 +1,17 @@ +--- +title: 'Help articles' +metaTitle: 'Help articles' +metaDescription: 'Common problems and how to solve them.' +hidePage: false +toc: false +--- + + + +This section provides a number of common problems that developers might encounter when using Prisma and provides short, practical solutions to resolve them. + + + +## Help articles + + diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-build-command-filled.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-build-command-filled.png new file mode 100644 index 0000000000..f5696d91c5 Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-build-command-filled.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-edit-settings.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-edit-settings.png new file mode 100644 index 0000000000..55f0d241a4 Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/netlify-edit-settings.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/prisma-autocompletion-in-js.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/prisma-autocompletion-in-js.png new file mode 100644 index 0000000000..cf37000afc Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/prisma-autocompletion-in-js.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/unwanted-autocomplete-values-in-vscode.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/unwanted-autocomplete-values-in-vscode.png new file mode 100644 index 0000000000..b98aa961ce Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/unwanted-autocomplete-values-in-vscode.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command-filled.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command-filled.png new file mode 100644 index 0000000000..dacaf8ce9d Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command-filled.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command.png b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command.png new file mode 100644 index 0000000000..44d2b07c55 Binary files /dev/null and b/docs/200-orm/800-more/600-help-and-troubleshooting/100-help-articles/vercel-ui-build-command.png differ diff --git a/docs/200-orm/800-more/600-help-and-troubleshooting/index.mdx b/docs/200-orm/800-more/600-help-and-troubleshooting/index.mdx new file mode 100644 index 0000000000..a46f25930f --- /dev/null +++ b/docs/200-orm/800-more/600-help-and-troubleshooting/index.mdx @@ -0,0 +1,9 @@ +--- +title: 'Help & troubleshooting' +metaTitle: 'Help & troubleshooting' +metaDescription: 'Help & troubleshooting in Prisma projects.' +--- + +## In this section + + diff --git a/docs/200-orm/800-more/700-releases.mdx b/docs/200-orm/800-more/700-releases.mdx new file mode 100644 index 0000000000..80a8505de7 --- /dev/null +++ b/docs/200-orm/800-more/700-releases.mdx @@ -0,0 +1,95 @@ +--- +title: 'ORM releases and maturity levels' +metaTitle: 'ORM releases and maturity levels' +metaDescription: 'Learn about the release process, versioning, and maturity of Prisma components and how to deal with breaking changes that might happen throughout releases.' +tocDepth: 3 +--- + + + +This page explains the release process of the Prisma ORM, how it's versioned and how to deal with breaking changes that might happen throughout releases. + + + +## Releases + +Prisma releases typically happen every three weeks. Note that this is _not_ a hard rule – releases might be postponed for internal reasons. + +[Check out all the releases notes in GitHub](https://github.com/prisma/prisma/releases). + +## Product maturity levels + +A release can include products or features at different maturity levels. Maturity level describes a product or feature's completeness and what users can expect in terms of breaking changes. + +> **Note**: As of version [2.13.0](https://github.com/prisma/prisma/releases/2.13.0), 'Experimental' is no longer part of the product maturity scale. + +### Early Access + +If a feature or product is **Early Access**: + +- We have validated a problem and are considering a solution to it but are not certain whether that solution is complete or a perfect fit. +- We want to gather more feedback and adjust the solution as necessary, knowing that users are prepared for significant breaking changes + +We don't recommend using Early Access features or products in production. + +### Preview + +If a feature or product is a **Preview**: + +- We have validated the feature or product in terms of direction and surface. +- Users can count on the feature or product and associated API to be mostly stable unless stated otherwise in the release notes and documentation. +- There are no significant known issues, although minor bugs may exist. +- We welcome feedback on these to make the solution stable as quickly as possible. + +Previews are typically available behind a feature flag or require some form of opt-in (for example, by providing a `--preview-feature` flag in the CLI or [adding them to a `previewFeatures` property in the `generator` block](/orm/reference/preview-features/cli-preview-features) for Prisma Client in your Prisma schema). + +We don't recommend using Preview features or products in production. + +See also: [All currently available Preview features](/orm/reference/preview-features). + +### Generally Available (GA) + +If a feature or product is **Generally Available**: + +- The solution has been tested for some time and we received enough feedback to consider it stable and ready for production use. +- There should be no bugs in 99% of cases (completely bug-free software cannot be guaranteed) + +## Roadmap + +Our roadmap helps us share our current priorities: what we are currently working on and what we are planning to work on in the near term. This reflects our _current plans_ today, and the content is subject to change at any time. Actual results and plans may differ as a result of changing our product strategy or reacting to demands from our user base. + +You can [check out the full roadmap here](https://pris.ly/roadmap). + +## Versioning + +Prisma's release scheme adheres to Semantic Versioning ([SemVer](https://semver.org/)) starting with version `3.x.x`. + +### Prisma and Semantic Versioning (SemVer) + +#### How does SemVer versioning work? + +Semantic Versioning (SemVer) uses the following rules for version upgrade (quoted from the [SemVer](https://semver.org/) spec): + +_Given a version number `MAJOR.MINOR.PATCH`, increment the:_ + +1. _`MAJOR` version when you make incompatible API changes,_ +1. _`MINOR` version when you add functionality in a backward compatible manner, and_ +1. _`PATCH` version when you make backward compatible bug fixes._ + +#### How does Prisma versioning follow SemVer? + +Beginning with version `3.x.x`, Prisma adheres strictly to the [SemVer](https://semver.org/) versioning scheme. + +Here is a brief overview of how Prisma's follows SemVer: + +- Breaking changes in stable surface (i.e. [General Availability](#generally-available-ga)) will only be introduced in new `MAJOR` releases. +- Breaking changes can still be rolled out in `MINOR` but only for opt-in Preview and Early Access features that are not active by default (e.g. via a Preview feature flag or a specific opt-in option or new CLI command). +- Opt-in breaking changes, i.e. Preview and Early Access, released in `MINOR`, will only be promoted to General Availability (no requirement for opt-in) in new `MAJOR` releases. + +Given a version number `MAJOR.MINOR.PATCH`, Prisma's version number is incremented as follows: + +1. `MAJOR` version is incremented when major product updates **with breaking changes** are released to General Availability. +1. `MINOR` version is incremented when product updates adding backward compatible new functionality are released. Features with breaking changes may only be introduced if they are **opt-in**, i.e. Early Access and Preview. +1. `PATCH` version is incremented when functionality bugs are fixed and are always **backward compatible**. + +> **Note:** Up until version version `2.28.0`, Prisma did not follow SemVer versioning strictly. This means that releases in the `2.MINOR.PATCH` range, `MINOR` versions may have included breaking changes. To learn more about Prisma's adoption of SemVer, check out the [blog post](https://www.prisma.io/blog/prisma-adopts-semver-strictly). diff --git a/docs/200-orm/800-more/index.mdx b/docs/200-orm/800-more/index.mdx new file mode 100644 index 0000000000..cb53782d2b --- /dev/null +++ b/docs/200-orm/800-more/index.mdx @@ -0,0 +1,11 @@ +--- +title: 'More' +metaTitle: 'More' +metaDescription: 'Learn more about Prisma ORM.' +staticLink: true +toc: false +--- + +## In this section + + diff --git a/docs/200-orm/index.mdx b/docs/200-orm/index.mdx new file mode 100644 index 0000000000..c9de440270 --- /dev/null +++ b/docs/200-orm/index.mdx @@ -0,0 +1,16 @@ +--- +title: 'ORM' +metaTitle: 'ORM' +metaDescription: 'ORM' +toc: false +--- + + + +[Prisma ORM](https://github.com/prisma/prisma) is a Node.js and TypeScript ORM with an intuitive data model, automated migrations, type-safety, and auto-completion. + + + +## In this section + + diff --git a/package-lock.json b/package-lock.json index 63dd5b69ed..59a3c950fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "name": "docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/preset-classic": "3.1.0", + "@docusaurus/core": "^3.1.1", + "@docusaurus/preset-classic": "^3.1.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", @@ -17,9 +17,9 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.1.0", - "@docusaurus/tsconfig": "3.1.0", - "@docusaurus/types": "3.1.0", + "@docusaurus/module-type-aliases": "^3.1.1", + "@docusaurus/tsconfig": "^3.1.1", + "@docusaurus/types": "^3.1.1", "typescript": "~5.2.2" }, "engines": { @@ -2164,9 +2164,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.0.tgz", - "integrity": "sha512-GWudMGYA9v26ssbAWJNfgeDZk+lrudUTclLPRsmxiknEBk7UMp7Rglonhqbsf3IKHOyHkMU4Fr5jFyg5SBx9jQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.1.tgz", + "integrity": "sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2178,13 +2178,13 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.1.0", - "@docusaurus/logger": "3.1.0", - "@docusaurus/mdx-loader": "3.1.0", + "@docusaurus/cssnano-preset": "3.1.1", + "@docusaurus/logger": "3.1.1", + "@docusaurus/mdx-loader": "3.1.1", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-common": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-common": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.5.1", "autoprefixer": "^10.4.14", @@ -2250,9 +2250,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.0.tgz", - "integrity": "sha512-ned7qsgCqSv/e7KyugFNroAfiszuxLwnvMW7gmT2Ywxb/Nyt61yIw7KHyAZCMKglOalrqnYA4gMhLUCK/mVePA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz", + "integrity": "sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g==", "dependencies": { "cssnano-preset-advanced": "^5.3.10", "postcss": "^8.4.26", @@ -2264,9 +2264,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.1.0.tgz", - "integrity": "sha512-p740M+HCst1VnKKzL60Hru9xfG4EUYJDarjlEC4hHeBy9+afPmY3BNPoSHx9/8zxuYfUlv/psf7I9NvRVdmdvg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.1.1.tgz", + "integrity": "sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2276,15 +2276,15 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.0.tgz", - "integrity": "sha512-D7onDz/3mgBonexWoQXPw3V2E5Bc4+jYRf9gGUUK+KoQwU8xMDaDkUUfsr7t6UBa/xox9p5+/3zwLuXOYMzGSg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", + "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", "dependencies": { "@babel/parser": "^7.22.7", "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/logger": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2316,12 +2316,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.0.tgz", - "integrity": "sha512-XUl7Z4PWlKg4l6KF05JQ3iDHQxnPxbQUqTNKvviHyuHdlalOFv6qeDAm7IbzyQPJD5VA6y4dpRbTWSqP9ClwPg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz", + "integrity": "sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A==", "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.1.0", + "@docusaurus/types": "3.1.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2335,17 +2335,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.0.tgz", - "integrity": "sha512-iMa6WBaaEdYuxckvJtLcq/HQdlA4oEbCXf/OFfsYJCCULcDX7GDZpKxLF3X1fLsax3sSm5bmsU+CA0WD+R1g3A==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/logger": "3.1.0", - "@docusaurus/mdx-loader": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-common": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz", + "integrity": "sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/logger": "3.1.1", + "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-common": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2366,17 +2366,17 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.0.tgz", - "integrity": "sha512-el5GxhT8BLrsWD0qGa8Rq+Ttb/Ni6V3DGT2oAPio0qcs/mUAxeyXEAmihkvmLCnAgp6xD27Ce7dISZ5c6BXeqA==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/logger": "3.1.0", - "@docusaurus/mdx-loader": "3.1.0", - "@docusaurus/module-type-aliases": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz", + "integrity": "sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/logger": "3.1.1", + "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/module-type-aliases": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2395,15 +2395,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.0.tgz", - "integrity": "sha512-9gntYQFpk+93+Xl7gYczJu8I9uWoyRLnRwS0+NUFcs9iZtHKsdqKWPRrONC9elfN3wJ9ORwTbcVzsTiB8jvYlg==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/mdx-loader": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz", + "integrity": "sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2417,13 +2417,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.0.tgz", - "integrity": "sha512-AbvJwCVRbmQ8w9d8QXbF4Iq/ui0bjPZNYFIhtducGFnm2YQRN1mraK8mCEQb0Aq0T8SqRRvSfC/far4n/s531w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz", + "integrity": "sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ==", "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", + "@docusaurus/core": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2437,13 +2437,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.0.tgz", - "integrity": "sha512-zvUOMzu9Uhz0ciqnSbtnp/5i1zEYlzarQrOXG90P3Is3efQI43p2YLW/rzSGdLb5MfQo2HvKT6Q5+tioMO045Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz", + "integrity": "sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ==", "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/core": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "tslib": "^2.6.0" }, "engines": { @@ -2455,13 +2455,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.0.tgz", - "integrity": "sha512-0txshvaY8qIBdkk2UATdVcfiCLGq3KAUfuRQD2cRNgO39iIf4/ihQxH9NXcRTwKs4Q5d9yYHoix3xT6pFuEYOg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz", + "integrity": "sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw==", "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/core": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2474,13 +2474,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.0.tgz", - "integrity": "sha512-zOWPEi8kMyyPtwG0vhyXrdbLs8fIZmY5vlbi9lUU+v8VsroO5iHmfR2V3SMsrsfOanw5oV/ciWqbxezY00qEZg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz", + "integrity": "sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA==", "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/core": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "tslib": "^2.6.0" }, "engines": { @@ -2492,16 +2492,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.0.tgz", - "integrity": "sha512-TkR5vGBpUooEB9SoW42thahqqwKzfHrQQhkB+JrEGERsl4bKODSuJNle4aA4h6LSkg4IyfXOW8XOI0NIPWb9Cg==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/logger": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-common": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz", + "integrity": "sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/logger": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-common": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2515,23 +2515,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.0.tgz", - "integrity": "sha512-xGLQRFmmT9IinAGUDVRYZ54Ys28USNbA3OTXQXnSJLPr1rCY7CYnHI4XoOnKWrNnDiAI4ruMzunXWyaElUYCKQ==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/plugin-content-blog": "3.1.0", - "@docusaurus/plugin-content-docs": "3.1.0", - "@docusaurus/plugin-content-pages": "3.1.0", - "@docusaurus/plugin-debug": "3.1.0", - "@docusaurus/plugin-google-analytics": "3.1.0", - "@docusaurus/plugin-google-gtag": "3.1.0", - "@docusaurus/plugin-google-tag-manager": "3.1.0", - "@docusaurus/plugin-sitemap": "3.1.0", - "@docusaurus/theme-classic": "3.1.0", - "@docusaurus/theme-common": "3.1.0", - "@docusaurus/theme-search-algolia": "3.1.0", - "@docusaurus/types": "3.1.0" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz", + "integrity": "sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/plugin-content-blog": "3.1.1", + "@docusaurus/plugin-content-docs": "3.1.1", + "@docusaurus/plugin-content-pages": "3.1.1", + "@docusaurus/plugin-debug": "3.1.1", + "@docusaurus/plugin-google-analytics": "3.1.1", + "@docusaurus/plugin-google-gtag": "3.1.1", + "@docusaurus/plugin-google-tag-manager": "3.1.1", + "@docusaurus/plugin-sitemap": "3.1.1", + "@docusaurus/theme-classic": "3.1.1", + "@docusaurus/theme-common": "3.1.1", + "@docusaurus/theme-search-algolia": "3.1.1", + "@docusaurus/types": "3.1.1" }, "engines": { "node": ">=18.0" @@ -2554,22 +2554,22 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.0.tgz", - "integrity": "sha512-/+jMl2Z9O8QQxves5AtHdt91gWsEZFgOV3La/6eyKEd7QLqQUtM5fxEJ40rq9NKYjqCd1HzZ9egIMeJoWwillw==", - "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/mdx-loader": "3.1.0", - "@docusaurus/module-type-aliases": "3.1.0", - "@docusaurus/plugin-content-blog": "3.1.0", - "@docusaurus/plugin-content-docs": "3.1.0", - "@docusaurus/plugin-content-pages": "3.1.0", - "@docusaurus/theme-common": "3.1.0", - "@docusaurus/theme-translations": "3.1.0", - "@docusaurus/types": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-common": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz", + "integrity": "sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==", + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/module-type-aliases": "3.1.1", + "@docusaurus/plugin-content-blog": "3.1.1", + "@docusaurus/plugin-content-docs": "3.1.1", + "@docusaurus/plugin-content-pages": "3.1.1", + "@docusaurus/theme-common": "3.1.1", + "@docusaurus/theme-translations": "3.1.1", + "@docusaurus/types": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-common": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -2593,17 +2593,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.0.tgz", - "integrity": "sha512-YGwEFALLIbF5ocW/Fy6Ae7tFWUOugEN3iwxTx8UkLAcLqYUboDSadesYtVBmRCEB4FVA2qoP7YaW3lu3apUPPw==", - "dependencies": { - "@docusaurus/mdx-loader": "3.1.0", - "@docusaurus/module-type-aliases": "3.1.0", - "@docusaurus/plugin-content-blog": "3.1.0", - "@docusaurus/plugin-content-docs": "3.1.0", - "@docusaurus/plugin-content-pages": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-common": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.1.tgz", + "integrity": "sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg==", + "dependencies": { + "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/module-type-aliases": "3.1.1", + "@docusaurus/plugin-content-blog": "3.1.1", + "@docusaurus/plugin-content-docs": "3.1.1", + "@docusaurus/plugin-content-pages": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-common": "3.1.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2622,18 +2622,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.0.tgz", - "integrity": "sha512-8cJH0ZhPsEDjq3jR3I+wHmWzVY2bXMQJ59v2QxUmsTZxbWA4u+IzccJMIJx4ooFl9J6iYynwYsFuHxyx/KUmfQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz", + "integrity": "sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.1.0", - "@docusaurus/logger": "3.1.0", - "@docusaurus/plugin-content-docs": "3.1.0", - "@docusaurus/theme-common": "3.1.0", - "@docusaurus/theme-translations": "3.1.0", - "@docusaurus/utils": "3.1.0", - "@docusaurus/utils-validation": "3.1.0", + "@docusaurus/core": "3.1.1", + "@docusaurus/logger": "3.1.1", + "@docusaurus/plugin-content-docs": "3.1.1", + "@docusaurus/theme-common": "3.1.1", + "@docusaurus/theme-translations": "3.1.1", + "@docusaurus/utils": "3.1.1", + "@docusaurus/utils-validation": "3.1.1", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2652,9 +2652,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.0.tgz", - "integrity": "sha512-DApE4AbDI+WBajihxB54L4scWQhVGNZAochlC9fkbciPuFAgdRBD3NREb0rgfbKexDC/rioppu/WJA0u8tS+yA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz", + "integrity": "sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2664,15 +2664,15 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.1.0.tgz", - "integrity": "sha512-PE6fSuj5gJy5sNC1OO+bYAU1/xZH5YqddGjhrNu3/T7OAUroqkMZfVl13Tz70CjYB8no4OWcraqSkObAeNdIcQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.1.1.tgz", + "integrity": "sha512-FTBuY3KvaHfMVBgvlPmDQ+KS9Q/bYtVftq2ugou3PgBDJoQmw2aUZ4Sg15HKqLGbfIkxoy9t6cqE4Yw1Ta8Q1A==", "dev": true }, "node_modules/@docusaurus/types": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.1.0.tgz", - "integrity": "sha512-VaczOZf7+re8aFBIWnex1XENomwHdsSTkrdX43zyor7G/FY4OIsP6X28Xc3o0jiY0YdNuvIDyA5TNwOtpgkCVw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.1.1.tgz", + "integrity": "sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2690,11 +2690,11 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-LgZfp0D+UBqAh7PZ//MUNSFBMavmAPku6Si9x8x3V+S318IGCNJ6hUr2O29UO0oLybEWUjD5Jnj9IUN6XyZeeg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.1.tgz", + "integrity": "sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg==", "dependencies": { - "@docusaurus/logger": "3.1.0", + "@docusaurus/logger": "3.1.1", "@svgr/webpack": "^6.5.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2725,9 +2725,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.0.tgz", - "integrity": "sha512-SfvnRLHoZ9bwTw67knkSs7IcUR0GY2SaGkpdB/J9pChrDiGhwzKNUhcieoPyPYrOWGRPk3rVNYtoy+Bc7psPAw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.1.tgz", + "integrity": "sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg==", "dependencies": { "tslib": "^2.6.0" }, @@ -2744,12 +2744,12 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.0.tgz", - "integrity": "sha512-dFxhs1NLxPOSzmcTk/eeKxLY5R+U4cua22g9MsAMiRWcwFKStZ2W3/GDY0GmnJGqNS8QAQepJrxQoyxXkJNDeg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz", + "integrity": "sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA==", "dependencies": { - "@docusaurus/logger": "3.1.0", - "@docusaurus/utils": "3.1.0", + "@docusaurus/logger": "3.1.1", + "@docusaurus/utils": "3.1.1", "joi": "^17.9.2", "js-yaml": "^4.1.0", "tslib": "^2.6.0" diff --git a/package.json b/package.json index 441a9ac29f..392a153020 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "3.1.0", - "@docusaurus/preset-classic": "3.1.0", + "@docusaurus/core": "^3.1.1", + "@docusaurus/preset-classic": "^3.1.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", @@ -24,9 +24,9 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.1.0", - "@docusaurus/tsconfig": "3.1.0", - "@docusaurus/types": "3.1.0", + "@docusaurus/module-type-aliases": "^3.1.1", + "@docusaurus/tsconfig": "^3.1.1", + "@docusaurus/types": "^3.1.1", "typescript": "~5.2.2" }, "browserslist": { diff --git a/src/theme/MDXComponents.tsx b/src/theme/MDXComponents.tsx new file mode 100644 index 0000000000..eb8129d42e --- /dev/null +++ b/src/theme/MDXComponents.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +// Import the original mapper +import MDXComponents from '@theme-original/MDXComponents'; + +// Import components we'd like to use across Docs +import Subsections from '@theme/DocCardList' // DocCardList renamed to Subsections for backwards compat +import Admonition from '@theme/Admonition'; +import TabbedContent from '@theme/Tabs'; // Tabs renamed to TabbedContent for backwards compat +import TabItem from '@theme/TabItem'; +import Link from '@docusaurus/Link'; + +// do we want to fix this? +const TopBlock: React.FC = ({ children }) => { + return

{children}

+} + +// we should fix this +const CodeWithResult: React.FC<{ children: React.ReactElement[] }> = ({ children }) => { + return <> +

{children[0]}

+

{children[1]}

+ +} + +// we should fix this +const SwitchTech: React.FC = ({ children }) => { + return

{children}

+} + +const ParallelBlocks: React.FC = ({ children }) => { + return <>{children} +} + +export default { + // Re-use the default mapping + ...MDXComponents, + Subsections, + Admonition, + TabbedContent, + TabItem, + Link, + TopBlock, + CodeWithResult, + SwitchTech, + ParallelBlocks, +}; \ No newline at end of file diff --git a/static/img/baseline-production-from-local.png b/static/img/baseline-production-from-local.png new file mode 100644 index 0000000000..9489a4fb41 Binary files /dev/null and b/static/img/baseline-production-from-local.png differ diff --git a/static/img/connect-sql-server.png b/static/img/connect-sql-server.png new file mode 100644 index 0000000000..2490f77f16 Binary files /dev/null and b/static/img/connect-sql-server.png differ diff --git a/static/img/cursor-1.png b/static/img/cursor-1.png new file mode 100644 index 0000000000..88aa02cfa0 Binary files /dev/null and b/static/img/cursor-1.png differ diff --git a/static/img/cursor-2.png b/static/img/cursor-2.png new file mode 100644 index 0000000000..9b85526780 Binary files /dev/null and b/static/img/cursor-2.png differ diff --git a/static/img/cursor-3.png b/static/img/cursor-3.png new file mode 100644 index 0000000000..a0481e0357 Binary files /dev/null and b/static/img/cursor-3.png differ diff --git a/static/img/offset-skip-take.png b/static/img/offset-skip-take.png new file mode 100644 index 0000000000..dc1685b02e Binary files /dev/null and b/static/img/offset-skip-take.png differ diff --git a/static/img/prisma-db-pull-generate-schema.png b/static/img/prisma-db-pull-generate-schema.png new file mode 100644 index 0000000000..4328184337 Binary files /dev/null and b/static/img/prisma-db-pull-generate-schema.png differ diff --git a/static/img/prisma-evolve-app-workflow.png b/static/img/prisma-evolve-app-workflow.png new file mode 100644 index 0000000000..07030ab64f Binary files /dev/null and b/static/img/prisma-evolve-app-workflow.png differ diff --git a/static/img/prisma-migrate-development-workflow.png b/static/img/prisma-migrate-development-workflow.png new file mode 100644 index 0000000000..1b85b62bae Binary files /dev/null and b/static/img/prisma-migrate-development-workflow.png differ