I have begun testing out prisma 2 and graphql in general for a new application. I am running into an issue with an explicit many to many table on being able to query relations.
Here is my apollo schema:
scalar DateTime
type Query {
user(id: String!): User
users: [User]
spaces: [Space]
roles: [Role]
}
type Mutation {
createUser(id: String!, email: String!): User!
createSpace(name: String!): Space!
}
type User {
id: ID!
email: String!
spaces: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Space {
id: ID!
name: String!
users: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Role {
id: ID!
name: String!
description: String!
users: UserSpace
createdAt: DateTime!
updatedAt: DateTime!
}
type UserSpace {
id: ID!
user: User!
space: Space!
role: Role!
createdAt: DateTime!
updatedAt: DateTime!
}
Here is my prisma schema:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// npx prisma migrate dev
// npx prisma generate
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id
email String @unique
spaces UserSpace[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Space {
id Int @default(autoincrement()) @id
name String @unique
users UserSpace[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Role {
id Int @default(autoincrement()) @id
name String @unique
description String
users UserSpace[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model UserSpace {
id Int @default(autoincrement()) @id
user User @relation(fields: [userId], references: [id])
userId String
space Space @relation(fields: [spaceId], references: [id])
spaceId Int
role Role @relation(fields: [roleId], references: [id])
roleId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Here is my mutations resolver:
const { prisma } = require(".prisma/client");
async function createUser(parent, args, context, info) {
return await context.prisma.user.create({
data: {
...args,
},
});
}
async function createSpace(parent, args, context, info) {
const isAuthenticated = context.authentication.isAuthenticated;
let role = null;
if(!isAuthenticated) {
throw new Error("Not Authenticated");
}
try {
role = await context.prisma.role.findUnique({
where: {
name: "Space Administrator",
},
});
}
catch(err) {
throw new Error(err);
}
return await context.prisma.space.create({
data: {
...args,
users: {
create: {
role: {
connect: { id: role.id },
},
user: {
connect: { id: context.authentication.auth0Id },
},
},
},
},
});
}
module.exports = {
createUser,
createSpace,
}
Here is my user resolver (I know this is where the problem is however I do not know how to solve the issue):
function spaces(parent, args, context, info) {
return context.prisma.user.findUnique({ where: { id: parent.id } }).spaces();
}
module.exports = {
spaces,
}
Basically when I create the space the user is added as a Space Administrator to the space and then should be able to be queried with the following:
query {
users {
id
email
spaces {
id
role {
name
}
space {
name
}
}
createdAt
}
}
However when I run the query I get the following error:
"message": "Cannot return null for non-nullable field UserSpace.role.",
How in prisma 2 do I make the resolver for the users work with an explicit many to many table and how it has the third relation in there? I am new to prisma and graphql so if there anything else that stands out also I would like to have the input.
I'm using the word type
to refer to object-models in your GraphQL schema and model
to refer to data-models in your Prisma Schema.
I see that you have a User
type resolver, that has a resolver function for User.spaces
field in your User
type. The query that you have defined in your User.spaces
resolver will return the relevant userSpace
records from the database.
However, these userSpace
records do not by default resolve the role
field, as it is a relation field. This is how prisma works (relation fields are not resolved by default, unless explicitly stated).
Create a resolver for the UserSpace
type and explicitly define the the resolver function for UserSpace.role
field. This is what it will look like
// UserSpace resolver module
function role(parent, args, context, info) {
return context.prisma.userSpace.findUnique({ where: { id: parent.id } }).role();
}
module.exports = {
role,
}
While there are some other ways to solve this problem, the way I have shown (along with the specific syntax) is recommended because under the hood it allows prisma to perform certain optimizations to solve the n+1 query problem. But, if you don't know what that is, you don't necessarily need to worry about it either.