install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/prisma-relations-pattern" ~/.claude/skills/intense-visions-harness-engineering-prisma-relations-pattern-6cd2e5 && rm -rf "$T"
manifest:
agents/skills/claude-code/prisma-relations-pattern/SKILL.mdsource content
Prisma Relations Pattern
Model one-to-one, one-to-many, many-to-many, and self-relations with @relation in Prisma
When to Use
- Adding foreign key relationships between Prisma models
- Modeling one-to-one, one-to-many, or many-to-many associations
- Creating self-referential relations (e.g., manager/employee, parent/child)
- Resolving ambiguous relations when two models have multiple connections
Instructions
- One-to-many — place the scalar foreign key and
on the "many" side. The "one" side gets an array field:@relation
model User { id String @id @default(cuid()) posts Post[] } model Post { id String @id @default(cuid()) author User @relation(fields: [authorId], references: [id]) authorId String }
- One-to-one — identical to one-to-many but the relation field is singular on both sides. Add
to the foreign key:@unique
model User { id String @id @default(cuid()) profile Profile? } model Profile { id String @id @default(cuid()) user User @relation(fields: [userId], references: [id]) userId String @unique }
- Many-to-many (implicit) — use array fields on both sides. Prisma creates the join table automatically:
model Post { id String @id @default(cuid()) tags Tag[] } model Tag { id String @id @default(cuid()) posts Post[] }
- Many-to-many (explicit) — create an explicit join model when you need extra fields on the relationship:
model PostTag { post Post @relation(fields: [postId], references: [id]) postId String tag Tag @relation(fields: [tagId], references: [id]) tagId String assignedAt DateTime @default(now()) @@id([postId, tagId]) }
- Self-relations — reference the same model. Use named
to disambiguate:@relation
model Employee { id String @id @default(cuid()) manager Employee? @relation("ManagerReports", fields: [managerId], references: [id]) managerId String? reports Employee[] @relation("ManagerReports") }
- Disambiguate multiple relations between the same two models using the relation name string:
model User { id String @id @default(cuid()) writtenPosts Post[] @relation("Author") editedPosts Post[] @relation("Editor") } model Post { id String @id @default(cuid()) author User @relation("Author", fields: [authorId], references: [id]) authorId String editor User? @relation("Editor", fields: [editorId], references: [id]) editorId String? }
- Referential actions — set
andonDelete
behavior:onUpdate
author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade)
Options:
Cascade, Restrict, NoAction, SetNull, SetDefault.
- Always add
on relation scalar fields for query performance.@@index([foreignKeyField])
Details
Prisma relations are defined at the Prisma schema level and map to foreign keys in the database. The
@relation attribute is required on the side that stores the foreign key (the scalar field side).
Implicit vs explicit many-to-many: Implicit join tables follow the naming convention
_ModelAToModelB (alphabetical). You cannot query the join table directly or add columns to it. Switch to explicit if you ever need metadata on the relationship.
Referential integrity modes:
(default for relational databases) — enforced by the databaseforeignKeys
— enforced by Prisma Client at the application level, required for databases that do not support foreign keys (e.g., PlanetScale with Vitess)prisma
Cascade gotchas:
on a required relation means deleting a parent deletes all children — this is often not what you want for audit-sensitive dataonDelete: Cascade
requires the foreign key field to be optional (onDelete: SetNull
)String?- The default referential action varies by provider — PostgreSQL defaults to
, which throws on violationNoAction
Performance considerations:
- Every
on a relation triggers an additional SQL query (not a JOIN). For deeply nested includes, consider using raw queries with explicit JOINsinclude - Implicit many-to-many creates an unindexed join table — add indexes manually via a migration if query performance degrades
Source
https://prisma.io/docs/orm/prisma-schema/data-model/relations
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.