diff --git a/.claude/skills/docs-writer/SKILL.md b/.claude/skills/docs-writer/SKILL.md index ce4c613e0d..091ff52534 100644 --- a/.claude/skills/docs-writer/SKILL.md +++ b/.claude/skills/docs-writer/SKILL.md @@ -120,6 +120,28 @@ Better: - Use exact product names: Prisma Postgres, Prisma Compute, Prisma Next. Don't shorten "Prisma Postgres" to "the database" or "Prisma Next" to "the ORM" once a page covers more than one product. - When you tell the reader something is automatic, show the trigger that makes it happen and how to confirm it did. "Compute injects `DATABASE_URL` automatically" needs a follow-up: "Run `prisma compute env` to confirm it's set." +## Teach in plain language + +Open every concept with the plain-words version a newcomer can repeat, then a concrete everyday example, and only then the precise terms. Jargon may appear after the reader has the idea, never as the introduction to it. + +Weak (jargon-first): + +> Data modeling is the step where you describe the shape of your application's data: the entities it works with, the fields each entity carries, and how those entities connect. You author that description as a schema, and it compiles into a versioned contract that your code, migrations, and tooling all read from. + +Better (idea first, example second, terms last): + +> Data modeling is the process of describing the data your application needs and how that data is connected. +> +> For example, a blog has users, posts, and comments. A user has fields like an email and a name. These models also relate to each other: a user can write many posts, and a post can have many comments. +> +> In Prisma Next, you define this structure in a `contract.prisma` file. This file becomes the shared contract between your application code, database migrations, and developer tools. + +The same rule applies inside sections: when a paragraph packs several decisions together, split it into short subsections, one decision each, and show a code block for every option you name. Guidance that lives only in inline code (`Int @id @default(autoincrement())` mid-sentence) belongs in a fenced block with a sentence of its own. + +Never lean on internal vocabulary ("runtime family", "lowering", "execution stack") without a one-line plain definition at first use. + +For hands-on guides (anything that builds something), follow the house guide style used by /docs/guides/runtimes/bun and the middleware authoring guide: an Introduction stating what the reader builds, Prerequisites, short numbered step headings ("## 1. Create the middleware"), one action per step with the exact file path on every code block, the real expected output after the step that produces it, a likely-failure line where a step commonly breaks, and options or reference material only after the working result. + ## Cut the slop Delete these on sight. They add length, not clarity. diff --git a/apps/docs/content/docs/orm/next/data-modeling/index.mdx b/apps/docs/content/docs/orm/next/data-modeling/index.mdx new file mode 100644 index 0000000000..a22b229bb1 --- /dev/null +++ b/apps/docs/content/docs/orm/next/data-modeling/index.mdx @@ -0,0 +1,248 @@ +--- +title: Overview +description: Describe the data your application needs with models, primary keys, scalar fields, and relations. +url: /orm/next/data-modeling +metaTitle: Data modeling in Prisma Next +metaDescription: Learn the building blocks of a Prisma Next data model, models, primary keys, scalar field types, and relations, and how they map to relational and document databases. +badge: early-access +--- + +Data modeling is the process of describing the data your application needs and how that data is connected. + +For example, a blog has users, posts, and comments. A user has fields like an email and a name. A post has fields like a title and content. These models also relate to each other: a user can write many posts, and a post can have many comments. + +In Prisma Next, you define this structure in a `contract.prisma` file. This file becomes the shared contract between your application code, your database migrations, and your developer tools. + +If you are coming from Prisma 7, `contract.prisma` plays a similar role to the `schema.prisma` file you used before. + +This page introduces the four building blocks of every Prisma Next schema: + +- [Models](#models): the things your application works with +- [Primary keys](#primary-keys): how each record is identified +- [Scalar fields](#scalar-fields): the values a model stores +- [Relations](#relations): how models connect to each other + +These building blocks apply to every database. Where databases differ is in how you model relations, and the [relational](/orm/next/data-modeling/relational-databases) and [MongoDB](/orm/next/data-modeling/mongodb) guides cover that in depth. + +## Models + +A model describes one kind of record: a user, an order, a blog post. Declare a model with the `model` keyword and give it fields: + +```prisma +model User { + id Int @id @default(autoincrement()) + email String + name String? +} +``` + +Every record of a model has its own identity and its own lifecycle. Two posts with the same title are still two different posts, and a user who changes their email address is still the same user. + +On a relational database, a model becomes a table. On MongoDB, it becomes a collection. + +## Primary keys + +A primary key is the field that uniquely identifies each record. Every model needs one. Mark it with `@id`: + +```prisma tab="PostgreSQL" +model User { + id Int @id @default(autoincrement()) + email String +} +``` + +```prisma tab="MongoDB" +model User { + id ObjectId @id @map("_id") + email String + @@map("users") +} +``` + +On MongoDB, the primary key maps to the document's mandatory `_id` field, and `ObjectId` is the idiomatic type for it. + +### Natural keys + +A natural key is a value that already identifies the record in the real world. Use one when the value is stable, unique, and assigned outside your application: a standardized code you receive rather than invent. + +Reference tables are the classic fit. A country is its ISO code, and the code never changes: + +```prisma +model Country { + code String @id + name String +} +``` + +Records that point at `Country` now store a readable value (`US`, `DE`) instead of an opaque number. Currencies (`USD`, `EUR`) and similar lookup tables work the same way. + +### Surrogate keys + +A surrogate key is a generated value with no business meaning: an auto-incrementing integer, a UUID, an ObjectId. It is the better default for records your application creates. + +The reason is stability. Values you might be tempted to use as a key, like an email address or a product SKU, change in practice. Changing a primary key is expensive because every record that points at the old value must be updated too. A surrogate key never changes. + +Keep the natural value as a regular field and enforce its uniqueness with `@unique`. You get a stable key and the uniqueness guarantee: + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique +} + +model Product { + id Int @id @default(autoincrement()) + sku String @unique +} +``` + +### Which surrogate type to pick + +Pick by how the record is created and where its id travels: + +```prisma +// Auto-incrementing integer: smallest and fastest to index, +// ordered by insertion. Good for internal records whose id +// never leaves your system. +model Invoice { + id Int @id @default(autoincrement()) +} +``` + +The database assigns the value, so you only know it after the insert. It is also sequential, so it leaks row counts and invites guessing if you expose it in URLs. + +```prisma +// UUID: globally unique, generated before the insert. +// Good for ids that appear in URLs or are created across services. +model ApiToken { + id String @id @default(uuid()) +} +``` + +A UUID is wider than an integer and random UUIDs index a little worse, but independent services never collide and nothing is leaked. + +```prisma +// ObjectId: the idiomatic MongoDB key. Generated without +// coordination and embeds its own creation time. +model Post { + id ObjectId @id @map("_id") + @@map("posts") +} +``` + +### Composite keys + +A composite key spans several fields, declared with `@@id`. Use one when the identity really is the combination, which usually means a model that links two others: + +```prisma +model UserTag { + userId Int + tagId Int + + @@id([userId, tagId]) +} +``` + +A duplicate `(userId, tagId)` pair would be meaningless, so the pair is the key. Avoid composite keys on ordinary models: everything that points at the model then has to carry all of the key's fields. + +## Scalar fields + +A scalar field holds a single value. The common types: + +| Type | Stores | +| ---------- | --------------------------- | +| `String` | Text | +| `Int` | 32-bit integer | +| `BigInt` | 64-bit integer | +| `Float` | Floating-point number | +| `Boolean` | `true` / `false` | +| `DateTime` | Timestamp | +| `Json` | Arbitrary JSON value | +| `ObjectId` | MongoDB document identifier | + +Prisma Next's type system is extensible, so extensions can add more types, such as vectors or geometry. + +Two modifiers change a field's shape: + +- A trailing `?` makes the field optional: `name String?` +- A trailing `[]` makes it a list: `tags String[]` + +### How to pick a data type + +Match the type to what the value means, not to what it looks like. + +An identifier is a `String`, even when it looks numeric. You never add zip codes or phone numbers together, they can have leading zeros, and they do not sort numerically: + +```prisma +model Address { + id Int @id @default(autoincrement()) + zip String +} +``` + +Money is not a `Float`. Floating-point numbers cannot represent decimal amounts exactly, so sums drift by fractions of a cent. Store an integer number of the smallest unit: + +```prisma +model Product { + id Int @id @default(autoincrement()) + priceCents Int +} +``` + +A point in time is a `DateTime`, not a `String`. A real timestamp type gives you correct comparison, sorting, and range queries: + +```prisma +model Post { + id Int @id @default(autoincrement()) + publishedAt DateTime +} +``` + +When you are unsure between two sizes, lean toward the wider one (`BigInt` over `Int` for a counter that could grow). Widening a type later is a migration; the wider type today is free. + +Mark a field optional (`?`) only when "absent" means something different from a sensible default. A required field with a default is often the clearer model. + +## Relations + +A relation connects two models: a user has many posts, a post belongs to one user. + +Two kinds of field describe a relation: + +- A field that stores the connection. This is a real column or document field, like `authorId`, holding the other record's primary key. +- A relation field, typed as the other model, like `author User`. It stores nothing itself; it tells Prisma Next how to navigate the connection in queries. + +The `@relation` attribute ties them together: `fields` names the local field that stores the link, and `references` names the field it points at on the other model. + +```prisma +model Post { + id Int @id @default(autoincrement()) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +``` + +Relations come in three shapes: + +- One-to-one: a user has at most one profile. +- One-to-many: a user has many posts. +- Many-to-many: a post has many tags, and a tag appears on many posts. + +How each shape is stored, and which model should hold the connecting field, is where relational and document databases differ. Continue with the guide for your database: + +- [Relational data modeling](/orm/next/data-modeling/relational-databases) for PostgreSQL and other SQL databases: foreign keys, junction tables, and which side owns the key. +- [MongoDB data modeling](/orm/next/data-modeling/mongodb): embedding versus referencing, and polymorphic collections. + +## Prompt your coding agent + +Projects scaffolded with `create-prisma` install Prisma Next skills for your coding agent; the `prisma-next-contract` skill covers schema authoring. Prompts that map to each section: + +- "Using the prisma-next-contract skill, add a Product model with a surrogate id and a unique sku field." +- "Add a Country reference table keyed by its ISO code." +- "Review my schema for fields that should be an enum or a DateTime instead of a String." +- "Connect Post to User with a foreign key and a relation field." + +## Next steps + +- [Model relational data](/orm/next/data-modeling/relational-databases): one-to-one, one-to-many, many-to-many, and polymorphic relations on SQL databases. +- [Model MongoDB data](/orm/next/data-modeling/mongodb): embed or reference, and single-collection polymorphism. +- [Query your models](/orm/next/fundamentals/reading-data) once the schema is in place. diff --git a/apps/docs/content/docs/orm/next/data-modeling/meta.json b/apps/docs/content/docs/orm/next/data-modeling/meta.json new file mode 100644 index 0000000000..a8beab36d3 --- /dev/null +++ b/apps/docs/content/docs/orm/next/data-modeling/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Data modeling", + "pages": ["index", "relational-databases", "mongodb"] +} diff --git a/apps/docs/content/docs/orm/next/data-modeling/mongodb.mdx b/apps/docs/content/docs/orm/next/data-modeling/mongodb.mdx new file mode 100644 index 0000000000..4217dce559 --- /dev/null +++ b/apps/docs/content/docs/orm/next/data-modeling/mongodb.mdx @@ -0,0 +1,168 @@ +--- +title: MongoDB data modeling +description: Model documents, embedded documents, and references for MongoDB, and decide when to embed and when to reference. +url: /orm/next/data-modeling/mongodb +metaTitle: MongoDB data modeling in Prisma Next +metaDescription: How to model data in Prisma Next for MongoDB, including documents and collections, embedded documents versus references, and polymorphic collections. +badge: early-access +--- + +MongoDB stores data as documents grouped into collections. A document can nest objects and arrays directly, which gives you a real choice for related data: keep it inside the document (embed it) or store it in its own collection and link to it (reference it). + +Making that choice well is the core of MongoDB data modeling, and it is what this page focuses on. New to models and keys? Start with the [data modeling overview](/orm/next/data-modeling). + +## Documents, collections, and the _id key + +A Prisma Next model maps to a collection, and each record is a document. Every document has an `_id` field as its primary key. `ObjectId` is the idiomatic type for it: + +```prisma +model User { + id ObjectId @id @map("_id") + name String + email String + @@map("users") +} +``` + +`@map("_id")` maps the model's `id` field to MongoDB's mandatory `_id` field, and `@@map("users")` names the collection. + +## Embed or reference + +Two pieces of related data can live together in one document (embedded) or in separate collections linked by an id (referenced). + +Embedding nests the data inside its parent: an order and its line items in one document. One read returns everything, and a write updates parent and children together, atomically. The trade-off: embedded data has no independent life. You cannot query it on its own, and it rides along on every read of the parent. + +Referencing keeps the data in its own collection and stores the linked document's `_id`. Loading both takes a second lookup, but each record stands on its own: it can be queried, listed, and updated independently, and it is not duplicated when several parents point at it. + +A quick way to decide: + +| Signal | Example | Lean toward | +| ------ | ------- | ----------- | +| Always loaded with the parent | An order's line items | Embed | +| Small and bounded in size | A user's mailing address | Embed | +| No meaning outside the parent | A post's SEO metadata | Embed | +| Grows without limit | A user's activity events | Reference | +| Queried or updated on its own | Products in a catalog | Reference | +| Shared by many parents | A tag on thousands of posts | Reference | + +A blog post's comments make it concrete. If you always render them with the post and their count stays modest, embed them. If comments can grow into the thousands, or you need "every comment by this author across posts", reference them. + +## Embedded documents + +Describe the shape of embedded data with a `type` block, then use it as a field. An embedded type has no collection of its own; it exists only inside its parent document. + +```prisma +type Address { + street String + city String + zip String? + country String +} + +model User { + id ObjectId @id @map("_id") + name String + address Address? + @@map("users") +} +``` + +A single embedded value models a one-to-one. A list of embedded values models a one-to-many: + +```prisma +type CartItem { + productId String + name String + amount Int +} + +model Cart { + id ObjectId @id @map("_id") + items CartItem[] + @@map("carts") +} +``` + +`Cart.items` lives inside the cart document. There is no separate collection and no join to load the items; every read of a cart returns them. + +An embedded `Address` has no `_id` and cannot be queried on its own. It is loaded, updated, and deleted with the document that holds it. That is the right behavior for values that belong to their parent, and the wrong one for records with their own life. + +:::warning + +Embedded arrays are only a good idea while they stay bounded. An array that grows forever slows every read of the parent and pushes the document toward MongoDB's 16 MB limit. When a list has no natural cap, reference it instead. + +::: + +## References across collections + +When related records need their own collection, store one document's `_id` on the other and declare the relation with the same `@relation(fields:, references:)` used everywhere in Prisma Next: + +```prisma +model User { + id ObjectId @id @map("_id") + name String + posts Post[] + @@map("users") +} + +model Post { + id ObjectId @id @map("_id") + title String + authorId ObjectId + author User @relation(fields: [authorId], references: [id]) + @@map("posts") +} +``` + +This is a one-to-many by reference: many posts point at one user, and each post is queryable on its own. When you [include the relation in a query](/orm/next/fundamentals/relations-and-joins), Prisma Next resolves it with a `$lookup` aggregation. + +A `$lookup` is a real cost on hot paths, so document models often denormalize: embed a small copy of the fields a read needs (a comment stores its author's name) while the full record stays in its own collection. You trade some duplication, and the work of keeping the copy in sync, for reads that touch one document. + +## Polymorphic collections + +MongoDB collections do not enforce a single shape, so it is idiomatic to store documents of different kinds in one collection and tell them apart with a discriminator field. + +Prisma Next models this with a base model plus variant models, all sharing one collection: + +```prisma +model Post { + id ObjectId @id @map("_id") + title String + kind String + + @@discriminator(kind) + @@map("posts") +} + +model Article { + summary String + + @@base(Post, "article") +} + +model Tutorial { + difficulty String + duration Int + + @@base(Post, "tutorial") +} +``` + +`@@discriminator(kind)` names the field that records each document's variant. `@@base(Post, "article")` declares `Article` as the `"article"` variant of `Post`. A query for posts returns articles and tutorials together, and you can narrow to one variant in queries. + +Use one polymorphic collection when the variants are handled together far more than separately: a notifications collection of email, SMS, and push messages read as one stream. Prefer separate collections when the types rarely appear in the same query or need very different indexes. + +## Prompt your coding agent + +Projects scaffolded with `create-prisma` install Prisma Next skills for your coding agent; the `prisma-next-contract` skill covers document modeling. Prompts that map to each section: + +- "Using the prisma-next-contract skill, add an embedded Address type to the User model." +- "Cart items are always read with the cart. Model them as an embedded list." +- "Comments can grow without limit. Move them from an embedded array to a referenced collection." +- "Split the posts collection into Article and Tutorial variants with a discriminator." + +## Next steps + +- [Query documents](/orm/next/fundamentals/reading-data) and [resolve references](/orm/next/fundamentals/relations-and-joins) with `.include(...)`. +- Aggregate and reshape documents with the [pipeline builder](/orm/next/fundamentals/advanced-queries#mongodb-pipeline-builder). +- To model relational data, see the [relational data modeling guide](/orm/next/data-modeling/relational-databases). diff --git a/apps/docs/content/docs/orm/next/data-modeling/relational-databases.mdx b/apps/docs/content/docs/orm/next/data-modeling/relational-databases.mdx new file mode 100644 index 0000000000..850deb9945 --- /dev/null +++ b/apps/docs/content/docs/orm/next/data-modeling/relational-databases.mdx @@ -0,0 +1,215 @@ +--- +title: Relational data modeling +description: Model one-to-one, one-to-many, many-to-many, and polymorphic relations for PostgreSQL and other SQL databases. +url: /orm/next/data-modeling/relational-databases +metaTitle: Relational data modeling in Prisma Next +metaDescription: How to model one-to-one, one-to-many, many-to-many, and polymorphic relations in Prisma Next for PostgreSQL, including which side owns the foreign key. +badge: early-access +--- + +In a relational database, each model becomes a table and each scalar field becomes a column. Models connect through foreign keys: a column in one table that holds the primary key of a row in another table. + +This page shows how to model each relation shape and, for each one, where the foreign key belongs. New to models and keys? Start with the [data modeling overview](/orm/next/data-modeling). + +## How relations are declared + +The model that stores the connection carries two fields: a scalar field for the foreign key column, and a relation field that navigates it. `@relation(fields:, references:)` ties them together: + +```prisma +model Post { + id Int @id @default(autoincrement()) + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +``` + +The side that carries the `fields` argument owns the foreign key. Which side that should be is the main decision in the shapes below. + +## One-to-many + +A one-to-many relation (`1:n`) connects one record to many: one user writes many posts, and each post belongs to one user. This is the most common relation shape. + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + authorId Int + author User @relation(fields: [authorId], references: [id]) +} +``` + +The foreign key lives on the "many" side, `Post.authorId`, because each post points at exactly one user, while a user points at an open-ended list of posts. The `posts Post[]` field on `User` is the back-relation: it stores nothing in the database and is inferred from the foreign key on `Post`. + +Use a one-to-many whenever a record belongs to one parent and a parent has many children: comments on a post, line items on an order, employees in a department. + +Because the list field is virtual, you never write to it. To connect a post to a user, set the foreign key: + +```typescript +const post = await db.orm.public.Post.create({ + title: "Hello", + authorId: user.id, +}); +``` + +See [Relations and joins](/orm/next/fundamentals/relations-and-joins#one-to-many) for querying in both directions. + +## One-to-one + +A one-to-one relation (`1:1`) connects at most one record on each side: a user has at most one profile, and a profile belongs to exactly one user. + +Model it like a one-to-many, then add `@unique` to the foreign key. The unique constraint is what turns "many" into "at most one": no two profiles can reference the same user. + +```prisma +model User { + id Int @id @default(autoincrement()) + email String @unique +} + +model Profile { + id Int @id @default(autoincrement()) + bio String + userId Int @unique + user User @relation(fields: [userId], references: [id]) +} +``` + +Declare the relation on the side that holds the foreign key (`Profile` here). The mirror field on the other model (`profile Profile?` on `User`) is not supported yet, so [query the relation from the profile side](/orm/next/fundamentals/relations-and-joins#one-to-one). + +### Which side owns the foreign key + +Put the foreign key on the dependent side: the record that cannot exist on its own. + +A profile needs a user. A user does not need a profile. So `Profile` carries `userId`: + +```prisma +model Profile { + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) +} +``` + +The other signals all point at the same side. The dependent record is: + +- The one you create second. Insert the `User`, then its `Profile`. +- The one you would delete when the parent goes away. +- The one whose absence is normal. A user without a profile is fine; a profile without a user is broken data. + +Placing the key on the dependent side keeps `User` free of profile concerns, lets a user exist with no profile, and guarantees every profile has exactly one user. + +If neither side is clearly dependent, put the key on the side you query from less often. + +## Many-to-many + +A many-to-many relation (`m:n`) connects many records on each side: a post has many tags, and a tag applies to many posts. + +A single foreign key cannot express this, because each side needs to point at many records. The answer is a junction model: a third model that holds one record per connected pair. + +```prisma +model Post { + id Int @id @default(autoincrement()) + title String + tags PostTag[] +} + +model Tag { + id Int @id @default(autoincrement()) + label String @unique + posts PostTag[] +} + +model PostTag { + postId Int + tagId Int + post Post @relation(fields: [postId], references: [id]) + tag Tag @relation(fields: [tagId], references: [id]) + + @@id([postId, tagId]) +} +``` + +The junction model is two one-to-many relations back to back. Its composite primary key, `@@id([postId, tagId])`, enforces one record per pair. + +Connecting a post to a tag is a plain create on the junction model, and disconnecting is a delete: + +```typescript +await db.orm.public.PostTag.create({ postId: post.id, tagId: tag.id }); +``` + +The junction is an ordinary model, so it can carry data about the pairing itself. When the tag was added, who added it, a sort order: add the field to the junction model. + +```prisma +model PostTag { + postId Int + tagId Int + addedAt DateTime @default(now()) + post Post @relation(fields: [postId], references: [id]) + tag Tag @relation(fields: [tagId], references: [id]) + + @@id([postId, tagId]) +} +``` + +An implicit many-to-many (list fields on both sides with no junction model, as in Prisma 7) is not supported yet: the schema compiler rejects it and asks for an explicit join model. Model the junction explicitly; [Relations and joins](/orm/next/fundamentals/relations-and-joins#many-to-many) shows how to traverse it in one query. + +## Polymorphic relations + +Sometimes several kinds of record share a common core but each carries its own extra fields: every `Task` has a title, but a `Bug` also has a severity and a `Feature` has a target release. + +Prisma Next models this with a base model plus variant models. A discriminator field records which variant each row is: + +```prisma +model Task { + id Int @id @default(autoincrement()) + title String + type String + + @@discriminator(type) +} + +model Bug { + severity String + + @@base(Task, "bug") +} + +model Feature { + targetRelease String? + + @@base(Task, "feature") + + @@map("features") +} +``` + +`@@discriminator(type)` marks the field that records the variant. `@@base(Task, "bug")` declares that `Bug` is the `"bug"` variant of `Task`. + +Variants appear as their own models in queries (`db.orm.public.Bug` alongside `db.orm.public.Task`), and a query on the base model returns every variant. One thing to know when writing: pass the discriminator value explicitly when you create through a variant (`{ title, severity, type: "bug" }`); it is not filled in automatically yet. + +Each variant picks one of two storage layouts: + +- A variant with no `@@map`, like `Bug`, shares the base table. Its extra columns live alongside the base columns and must be nullable at the storage level, because a `Feature` row has no severity. +- A variant with its own `@@map`, like `Feature`, gets its own table holding only its extra columns, linked back to the base table's primary key. Its columns keep their constraints, at the cost of a join to read a full variant. + +Use polymorphism when you query the variants together (one feed of tasks, one stream of events) and each variant carries real structure of its own. If you never query them together, separate models are simpler. If the variants differ by one nullable field, a single model is simpler still. + +## Prompt your coding agent + +Projects scaffolded with `create-prisma` install Prisma Next skills for your coding agent; the `prisma-next-contract` skill covers relation modeling. Prompts that map to each section: + +- "Using the prisma-next-contract skill, add a one-to-many between User and Post with the foreign key on Post." +- "Make the Profile relation one-to-one by adding a unique constraint to its foreign key." +- "Model a many-to-many between Post and Tag with an explicit junction model and an addedAt timestamp." +- "Split Task into Bug and Feature variants with a discriminator field." + +## Next steps + +- [Query relations](/orm/next/fundamentals/relations-and-joins) with `.include(...)`, relation predicates, and junction traversal. +- To model for MongoDB, see the [MongoDB data modeling guide](/orm/next/data-modeling/mongodb). +- Referential actions (what happens to related rows on delete) are covered in the relations reference as it lands. diff --git a/apps/docs/content/docs/orm/next/meta.json b/apps/docs/content/docs/orm/next/meta.json index 09abc9d398..83bff2fb31 100644 --- a/apps/docs/content/docs/orm/next/meta.json +++ b/apps/docs/content/docs/orm/next/meta.json @@ -5,6 +5,9 @@ "pages": [ "---Introduction---", "index", + "---Data Modeling---", + "...data-modeling", + "---Fundamentals---", "...fundamentals", diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index f1f1fbfc13..3fae170b2e 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -319,6 +319,11 @@ const config = { // SEO owner at cutover): /orm/prisma-client/queries/full-text-search, // /orm/prisma-client/queries/advanced/query-optimization-performance, // /orm/prisma-client/queries/excluding-fields. + // + // DR-8679 Data modeling: + // { source: "/orm/prisma-schema", destination: "/orm/next/data-modeling", permanent: false }, + // { source: "/orm/prisma-schema/data-model/models", destination: "/orm/next/data-modeling", permanent: false }, + // { source: "/orm/prisma-schema/data-model/relations", destination: "/orm/next/data-modeling/relational-databases", permanent: false }, // ─────────────────────────────────────────────────────────────────────── ]; },