How can I condition GraphQL mutation based on exists? - graphql

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.

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?

Same type relationships in GraphQL / Prisma

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.

GraphQL query for find equal field on realations

I have a schema for GraphQL like this (it's good to mention that I'm using Prisma) :
enum PollResult {
HOME_WIN
AWAY_WIN
DRAW
}
type UserPoll {
id: ID! #unique
user: User!
predict: PollResult!
}
type Poll {
id: ID! #unique
away: Team #relation(name: "AwayTeam")
home: Team #relation(name: "HomeTeam")
group: Group!
country: Country!
sport: Sport!
result: PollResult
state: PollState! #relation(name: "PollState")
users: [User] #relation(name: "Users")
usersPrediction: [UserPoll] #relation(name: "UserPoll")
}
as you see in UserPoll I have predict with type of PollResult and in Poll
I have result with the type of PollResult. now I want to query on Poll and find the specific user (with id or email) that has the same value of usersPrediction -> predict with Poll -> result.
one query that I try is something like this :
query{
userPolls(where:{user:{id:"someid"}}){
}
}
but here I don't know how to find users with equal predict with polls result.If it's the problem with my schema please let me know.
I can't think of a way to express that in a single query, but since you're using an Enum it would only be three similar queries.
query predict_HOME_WIN{
polls(where:{result: HOME_WIN}){
usersPrediction(where:{predict: HOME_WIN})
user{
id
}
}
}
This will give you all users that predicted a HOME_WIN when the result was a HOME_WIN. You could then do the same query for the other two enum values and sum up the results. Then you have all users who predicted the correct outcome. You could send the queries in one go to Prisma by naming them.
Hope that helps
Could you replace your usersPrediction field with three fields:
allUsersPrediction
rightUsersPrediction
wrongUsersPrediction
Then whole schema will be:
enum PollResult {
HOME_WIN
AWAY_WIN
DRAW
}
type UserPoll {
id: ID! #unique
user: User!
predict: PollResult!
}
type Poll {
id: ID! #unique
away: Team #relation(name: "AwayTeam")
home: Team #relation(name: "HomeTeam")
group: Group!
country: Country!
sport: Sport!
result: PollResult
state: PollState! #relation(name: "PollState")
users: [User] #relation(name: "Users")
allUsersPrediction: [UserPoll] #relation(name: "UserPoll")
rightUsersPrediction: [UserPoll] #relation(name: "UserPoll")
wrongUsersPrediction: [UserPoll] #relation(name: "UserPoll")
}
Required users will be at rightUsersPrediction[].user

Prisma data modeling has many and belongs to

I have a prisma data model that consists of a root Category and a Subcategory. A Category has many Subcategories and a Subcategory belongs to one Category. My model looks like this:
type Category {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
name: String!
subCategories: [SubCategory!]! #relation(name: "Subcategories")
}
type SubCategory {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
name: String!
category: Category! #relation(name: "ParentCategory")
cards: [Card!]! #relation(name: "SubCategoryCards") #Category #relation(name: "CardCategory")
}
Now when i go to create a new subcategory and via
mutation {
createSubCategory(data:{
name:"This is a test"
category:{
connect:{
id:"cjp4tyy8z01a6093756xxb04i"
}
}
}){
id
category{
name
id
}
}
}
This appears to work fine. Below I query for the subcategories and their parent Category and I get the results that I expect.
{
subCategories{
id
name
category{
id
name
}
}
}
However, when i try to query a category, and get all of it's sub categories I'm getting an empty array:
{
categories{
id
name
subCategories{
id
name
}
}
}
How can I query all categories and get their sub categories?
As per the documentation, the #relation directive is used to specify both ends of a relation.
Let's take the following datamodel:
type User {
postsWritten: [Post!]!
postsLiked: [Post!]!
}
type Post {
author: User!
likes: [User!]!
}
Here, we have an ambiguous relation between Post and User. Prisma needs to know which User field (postsWritten? postsLiked?) to link to which Post field (author? likes?)
To resolve this, we use the #relation with a name used in both ends of the relation.
This would make the datamodel look like this:
type User {
postsWritten: [Post!]! #relation(name: "AuthorPosts")
postsLiked: [Post!]! #relation(name: "UserLikes")
}
type Post {
author: User! #relation(name: "AuthorPosts")
likes: [User!]! #relation(name: "UserLikes")
}
Because we used the same name for the postsWritten and author fields, Prisma is now able to link these two in the database. Same for postsLiked and likes.
In conclusion, the problem with your datamodel is that you used different names in your relation. This confuses Prisma which think those are different relations. Which explains why you can query one way but not another.

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