Same type relationships in GraphQL / Prisma - graphql

I am trying to create a one-to-many relation of the same type. In this case, a user can report to one user and in turn have many users reporting to them.
My data model currently looks like this:
type User {
id: ID! #id
name: String!
email: String! #unique
reportsTo: User #relation(name: "UserReports")
reports: [User] #relation(name: "UserReports")
}
I expect adding a userId to reportsTo should add the corresponding user's ID to reports.
However, adding a userId to reportsTo is adding the userId to the same user's reports, rather than other users reports.

You can't have a relationship with different elements of the one type. A relation is connecting two different types so we know they share information. Therefore we need to create some addition types that we can base a relationship on.
I've created two different types, one a "supervisor" who is a user who supervises other users, but we will have those users as a second type, "supervisee". Each user can be both a supervisor and a supervisee. There is a relation between a user and each of these two types and a relation between these two types as well.
This is the datamodel:
type User {
id: ID! #id
name: String!
email: String! #unique
supervisor: Supervisor #relation(name: "UserToSupervisor")
supervisee: Supervisee #relation(name: "UserToSupervisee")
}
type Supervisor {
id: ID! #id
user: User! #relation(name: "UserToSupervisor")
supervisees: [Supervisee!]! #relation(name: "SupervisorToSupervisee")
}
type Supervisee {
id: ID! #id
user: User! #relation(name: "UserToSupervisee")
supervisor: Supervisor! #relation(name: "SupervisorToSupervisee")
}
You must view the "supervisor" field in user not as who that users supervior is, but that the user might be a supervisor themselves. This is also the case with supervisee. Basically supervisor and supervisee are extensions of user and the working relationship between them is defined between those two types.

Related

How to create interdependent objects in GraphQL?

I have a schema with two object types that link to each other through a field. What is the canonical way to create them in a mutation?
Let's take a simple schema with a User and an Account object type. The simplest case of interdependency is a one-to-one relationship, where a user has exactly one account and an account has exactly one user.
type User {
id: ID!
name: String!
account: Account!
}
type Account {
id: ID!
email: String!
user: User!
}
How would we the mutations to create a user and an account look like?
My first idea was to use separate createUser and createAccount mutations and providing the other object's id. But this doesn't work, since the circular dependency requires the other object to be known.
type Mutation {
createUser(id: ID! name: String! accountId: ID!): User
createAccount(id: ID! email: String! userId: ID!): Account
}
We could make the connection field type in one object optional. Then we could create that object with an empty link to the yet non-existing second object, create the second object (with a link to the first), and then go back to the first and update it with a link to the second.
But I want to avoid making the connection field type optional.
The only way I see is to create both in a single mutation.
type Mutation {
createUserAndAccount(userId: ID! userName: String! accountId: ID! accountEmail: String!): UserAndAccount
}
type UserAndAccount {
user: User
account: Account
}
But now assume we have a one-to-many relationship, where a user can have one or more accounts.
type User {
id: ID!
name: String!
account: [Account!]!
}
type Account {
id: ID!
email: String!
user: User!
}
How would we the mutations to create a user and an account look like now?
When creating a new user, we must create a new account. So, we have to use the previous mutation createUserAndAccount. I'm assuming we don't want to create more than one account initially, and instead go back and make an update mutation to the user when we create additional accounts.
But when creating a new account, the corresponding user might or might not already exist. It might be the first account for a new user, or an additional account for an existing user. We would want a mutation for both possibilities, to either link the account to an existing user or create a new user with it.
The only way I can see, is to add a separate mutation createAccount for creating an additional account for an existing user, while keeping the previous mutation createUserAndAccount for creating an account and a new user.
type Mutation {
createUserAndAccount(userId: ID! userName: String! accountId: ID! accountEmail: String!): UserAndAccount
createAccount(id: ID! email: String! userId: ID!): Account
}
type UserAndAccount {
user: User
account: Account
}
But let's now look at the general case of a many-to-many relationship, where a user can have one or more accounts and an account can have one or more users.
type User {
id: ID!
name: String!
account: [Account!]!
}
type Account {
id: ID!
email: String!
user: [User!]!
}
How would we the mutations to create a user and an account look like now?
When creating either object, the corresponding other object might or might not already exist. Again, I'm assuming we initially don't want to link an object to more than one other object, and instead go back and make an update mutation to it when we create additional other objects.
We would want a mutation for both possibilities, to either link to an existing object or create a new object with it.
The only way I can see is, to keep the createUserAndAccount mutation like previously for creating both objects at the same time, while adding separate mutations when the linked object already exists.
type Mutation {
createUserAndAccount(userId: ID! userName: String! accountId: ID! accountEmail: String!): UserAndAccount
createUser(id: ID! email: String! accountId: ID!): User
createAccount(id: ID! email: String! userId: ID!): Account
}
type UserAndAccount {
user: User
account: Account
}
I recognise this touches on the difficult problem of circular dependencies.
How would you design this API? Would you delete the link in one of the object types to avoid circular dependencies at all cost?

How to create 4-level nested relationship with Prisma

I have 3 main types: Company, Service, and Specialty. Every Company has a list of Service. Every Service have a list of Specialty. Every Specialty have a list of Specialty (sub-specialties).
Company 1
- Service 1
-- Specialty 1
--- Sub-specialty 1
So
I am working on this datamodel on Prisma 1.34:
type Company {
id: ID! #id
name: String! #unique
services: [Service]
}
type Service {
id: ID! #id
name: String! #unique
companies: [Company]! #relation(link: TABLE)
specialties: [Specialty]
}
type Specialty {
id: ID! #id
name: String! #unique
companies: [Company]! #relation(link: TABLE)
services: [Service] #relation(link: TABLE)
sub_specialties: [Specialty] #relation(link: TABLE)
}
My problem is, when I add one Company and use the same Service for that Company in another Company, all other Specialty and Sub-specialty records are coming along with that Service. I want to make Specialty and Sub-specialty fields unique for every company but they also should be under related Service under every company.
I have been working on it for the past 3 days, I would appreciate any help.
If you're committed to a nested relationship, then you're going to have to make the Service type have non-unique names. This will force a new Service model to be created for each company while still allowing Specialty to be more generalized and have better reuse.
The logic here is that if there are multiple Services with the same name but with different Specialty attributes, then they are NOT the same.
Here is the solution I came up with: I am holding unique service and specialty names in ServiceItem and SpecialtyItem tables. Whenever I add a new Service to a company, I link that Service to a specific ServiceItem that is unique. That Service and Specialty receives it's information (only name property in this project) from linked ServiceItem and SpecialtyItem. That way, whenever I update my ServiceItem or SpecialtyItem, all related Service and Specialty fields under each company get updated. Here is the datamodel:
type Company {
id: ID! #id
name: String! #unique
services: [Service]! #relation(onDelete: CASCADE)
}
type ServiceItem {
id: ID! #id
name: String! #unique
}
type Service {
id: ID! #id
info: ServiceItem! #relation(link: INLINE)
company: Company! #relation(link: INLINE)
specialties: [Specialty] #relation(onDelete: CASCADE)
}
type SpecialtyItem {
id: ID! #id
name: String! #unique
}
type Specialty {
id: ID! #id
info: SpecialtyItem! #relation(link: INLINE)
service: Service #relation(link: INLINE)
sub_specialties: [Specialty] #relation(onDelete: CASCADE)
}

Is it possible to create two or more relations to a model in prisma?

I'm trying to create two relations to a model in datamodel.prisma
datamodel.prisma
type User {
id: ID! #id
user_id: String! #unique
username: String!
email: String! #unique
}
type Operation {
id: ID! #id
teams: [User] #relation(link: INLINE)
created_by: User #relation(link: INLINE)
}
When I try to deploy this is the error I'm getting
Error
Errors:
Operation
✖ The relation field `teams` must specify a `#relation` directive: `#relation(name: "MyRelation")`
✖ The relation field `created_by` must specify a `#relation` directive: `#relation(name: "MyRelation")`
What I want to achieve is an operation can have multiple members(one to many) and can only be created by one member(one to one). How can I achieve this in Prisma?
Could you try creating it like this:
type User {
id: ID! #id
user_id: String! #unique
username: String!
email: String! #unique
}
type Operation {
id: ID! #id
teams: [User] #relation(name: "Teams", link: TABLE)
created_by: User #relation(name: "Createdby", link: TABLE)
}
The name field is required while creating multiple relations to the same model.
Also I'm assuming you are using Postgres.

How can I condition GraphQL mutation based on exists?

I am creating schemas in GraphQl and testing these in Playground. These seem to work fine in being able to create Club, User and Team, yet I only want to be able to create a team if a club exists and can therefore connect to the club. At the moment I can create a team without a club existing, which of course shouldn't happen. On create Team Mutation, I also want to stop any club being created, the club must exist prior to any mutation on a Team.
Here is what I have at the moment for my schemas
type Club {
id: ID! #unique
name: String! #unique
team: [Team!]! #relation(name: "TeamToClub", onDelete: CASCADE)
admin: User! #relation(name: "UserToClub", onDelete: SET_NULL)
}
type User {
id: ID! #unique
name: String!
team: [Team!]! #relation(name: "TeamToUser", onDelete: SET_NULL)
club: [Club!]! #relation(name: "UserToClub", onDelete: SET_NULL)
}
type Team {
id: ID! #unique
name: String!
club: Club! #relation(name: "TeamToClub", onDelete: SET_NULL)
creator: User! #relation(name: "TeamToUser", onDelete: SET_NULL)
coach:[Coach!]! #relation(name: "CoachToTeam", onDelete: SET_NULL)
}
Here are my mutations for creating a user
mutation {
createUser(
data:{
name:"Jack Jones",
email:"jack#example.com"
}
){
id
name
}
}
and creating a team..
mutation {
createTeam(
data:{
title:"Barcelona FC"
agegroup:"Under 12s",
published: false,
coachcreator:{
connect:{
id:"cka8qkd5h02dj0815m3odzh5s"
}
}
}
){
id
title
agegroup
published
coachcreator {
name
}
}
}
both of which work without connecting to a club.
Could anybody provide a possible solution to ensure a condition is in place to say a team can only be created when a club already exists.
Strange mutations - looks like they are completely not related to types.
Usually create mutation returns created type, f.e. createUser returns User ... and mutation argument - input type (UserInput) - with almost the same shape as returned type (without id).
The simplest method to ensure that createTeam has proper (existing) club relation is to define club as required argument of mutation.

GraphQL Prisma - define "vote" type that links to two users

I've just started using Prisma. Before was mainly using firebase and mongodb to define my schemas.
I'm trying to define the following schema:
Vote {
id: ID!
from: User! # The user who voted
for: User! # The user that received a vote
rate: Float!
}
Basically, what I want to achieve is enable users to vote for other users (give them a score).
In, say, MongoDB I would do it by creating a separate collection like following:
{
id: DocumentID
from: String // id of the user who voted
for: String // id of the user that received a vote
rate: Number
}
In here I just specify those fields (from and for) as strings and after link them with the User collection by the application logic.
For sure, it's gonna be different in GraphQL Prisma. But I'm still a bit confused on how the relationships are built. And what really happens underneath.
How can I create such schema using Prisma GraphQL?
When there are more than one relational field to the same type, you need to use the #relation directive to make it unambiguous.
type Vote {
id: ID! #unique
votingUser: User! #relation(name: "VoteAuthor")
votedUser: User! #relation(name: "VoteReceiver")
rate: Float!
}
type User {
id: ID! #unique
receivedVotes: [Vote!]! #relation(name: "VoteReceiver")
givenVotes: [Vote!]! #relation(name: "VoteAuthor")
name: String!
}

Resources