Working with Self-Relations

1. Defining Self-Referencing Model

model Category {
  id       Int        @id @default(autoincrement())
  name     String
  parentId Int?
  parent   Category?  @relation("CategoryHierarchy", fields:[parentId], references:[id])
  children Category[] @relation("CategoryHierarchy")
}
RequiredDetail
Relation nameMandatory for self-relations

2. Creating One-to-One Self-Relation

model User {
  id          Int   @id @default(autoincrement())
  mentorId    Int?  @unique
  mentor      User? @relation("Mentorship", fields:[mentorId], references:[id])
  mentee      User? @relation("Mentorship")
}
KeyDetail
Uniqueness@unique on FK forces 1:1

3. Creating One-to-Many Self-Relation

ExampleNotes
Tree / hierarchyCategory → children
Comments treeComment → replies

4. Creating Many-to-Many Self-Relation

model User {
  id        Int    @id @default(autoincrement())
  followers User[] @relation("Followers")
  following User[] @relation("Followers")
}
AspectDetail
Join tableAuto-created _Followers
DirectionTwo virtual fields point to same relation name

5. Setting Relation Names

WhyDetail
RequiredSchema invalid otherwise for self-relations
ConventionPascalCase descriptive label
const root = await prisma.category.findUnique({
  where: { id: 1 },
  include: { children: { include: { children: true } } }
});
PatternDetail
Fixed depthManually nest include
Arbitrary depthUse recursive raw CTE

7. Creating Hierarchical Structures

ModelUse
Adjacency listparentId — easy writes, expensive reads
Materialized pathAdd path String column
Nested setFastest reads, complex writes

8. Implementing Parent-Child Patterns

await prisma.category.create({
  data: {
    name: "Sub",
    parent: { connect: { id: parentId } }
  }
});
OpEffect
connect parentSet FK to existing node
nested create childrenBuild subtree in one call

9. Handling Recursive Queries

const tree = await prisma.$queryRaw`
  WITH RECURSIVE t AS (
    SELECT id, name, "parentId" FROM "Category" WHERE id = ${rootId}
    UNION ALL
    SELECT c.id, c.name, c."parentId" FROM "Category" c JOIN t ON c."parentId" = t.id
  ) SELECT * FROM t`;
TipDetail
PostgreSQLWITH RECURSIVE supported
MySQL 8+Same syntax

10. Managing Circular References

RiskMitigation
Cycles in hierarchyValidate at app level before writes
Cascade loopsAvoid mutual Cascade — use NoAction or Restrict
JSON serializationBreak cycles before JSON.stringify