question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Provide similar "connect" behavior when using explicit many-to-many tables compared to implicit tables

See original GitHub issue

Problem

It would appear that when using explicit many-to-many tables, I am unable to use the connect feature when creating an item since it is required to know both foreign keys to make the “connection”. Thus, I would like to be able to emulate the same behavior of an implicit join table when connecting an entity during a model.create() with an explicit join table since you only need to provide the “known” foreign key during creation.

Given the following models defining an explicit table (taken from docs):

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)
  @@id([postId, categoryId])
}

I would be unable to perform the following create and connect operation since an explicit many to many table requires that you know both foreign keys to make the connection. Here’s a sample resolver displaying this issue:

async resolve(_root, args, ctx) {
  const post = await ctx.db.post.create({
    data: {
      title: 'test title'
      categories: {
        connect: {
          postId_categoryId: {
              // I don't know this yet because the post hasn't been created, but typescript requires I add it,
              postId: ???
              categoryId: args.categoryId
          }
        },
      },
    },
  })

For example, if I had an implicit table I could just do:

const post = await ctx.db.post.create({
  data: {
    title: 'my title,
    categories: {
      connect: {
        // this doesn't require the postId but adds it when making the join table
        id: args.categoryId,
      },
    },
  },
})

Suggested solution

Provide the ability to add just the “known” foreign key when connecting an element to a new item, similar to the implicit many to many join tables:

async resolve(_root, args, ctx) {
  const post = await ctx.db.post.create({
    data: {
      title: 'test title'
      categories: {
        connect: {
          postId_categoryId: {
              // only require the "known" id
              categoryId: args.categoryId
          }
        },
      },
    },
  })

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:17
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
m1plcommented, Mar 28, 2022

@janpio I can confirm this is not a bug, but a documentation issue.

Assuming the following schema:

model Foo {
  id   Int   @id @default(autoincrement())
  bars Bar[]
}

model Bar {
  id   Int   @id @default(autoincrement())
  foos Foo[]
}

This succeeds:

await prisma.foo.create({
  data: {
    bars: {
      connect: {
        id: 1
      },
    },
  },
});

I change the schema to use an explicit many-to-many relationship:

model Foo {
  id   Int   @id @default(autoincrement())
  bars FooBar[]
}

model Bar {
  id   Int   @id @default(autoincrement())
  foos FooBar[]
}

model FooBar {
  foo   Foo @relation(fields: [fooId], references: [id])
  fooId Int

  bar   Bar @relation(fields: [barId], references: [id])
  barId Int

  @@id([fooId, barId])
}

The previous create fails, but this one succeeds:

await prisma.foo.create({
  data: {
    bars: {
      create: {
        bar: {
          connect: {
            id: 5,
          },
        },
      },
    },
  },
});

connectOrCreate succeeds as well.

I think this difference should be described in more detail in the docs.

4reactions
m1plcommented, Mar 28, 2022

EDIT: Do not use this, real solution below!


Until this is implemented, I found a workaround.

Schema:

model Foo {
  ...
  foos Foo[]
  FooBar FooBar[]
}

model Bar {
  ...
  bars Bar[]
  FooBar FooBar[]
}

model FooBar {
  foo Foo @relation(fields: [A], references: [id])
  A   Int
  
  bar Bar @relation(fields: [B], references: [id])
  B   Int
  
  @@id([A, B])  @@map("_FooToBar")
}

It’s a mix of explicit and implicit. I’m defining an explicit many-to-many relationship, while mapping it onto the implicit one. This allows me to define additional fields on the junction table, use connect, e.t.c. The only drawback I found so far (apart from the ugly names when working directly with the junction table) is that I have to run prisma db push twice on an empty database. It fails the first time and succeeds the second time. It’s not pretty but it will do until this is supported by Prisma.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Prisma many-to-many relations: create and connect
My schema is similar to the one in question, only difference is I have Files instead of Posts and Tags instead of Categories....
Read more >
Data Modeling with Prisma - JavaScript in Plain English
In an Explicit m-n relation intermediate relation table is represented as a model in the Prisma schema. Unlike in implicit m-n relation you...
Read more >
Configuring how Relationship Joins
relationship() will normally create a join between two tables by examining the foreign key relationship between the two tables to determine which columns...
Read more >
Many-to-many relations - Prisma
When using Prisma, you can create relation tables by defining models similar to how you would model them as tables. The main difference...
Read more >
Breaking changes in EF Core 6.0 - Microsoft Learn
Simple join tables containing only two foreign key properties to other tables are no longer mapped to explicit entity types, but are instead ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found