GraphQL query for find equal field on realations - graphql

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

Related

How do you expose different subsets of a GraphQL type to different users?

I have a User type with many fields on it. I want to expose different fields on it depending on who is querying information about the User. What is a good way to organize this without having many many different types each representing a slightly different view of a user? Here is an example with 4 different types representing different views of the same user. Is there a better way to organize this?
Of course I can make all the fields nullable but that doesn't seem helpful to the developer querying the data.
type UserForSelf {
id: ID!
username: String!
avatarUrl: String!
email: String!
mailingAddress: Address!
team: Team!
lastLogin: DateTime!
}
type UserForPublic {
id: ID!
username: String!
avatarUrl: String!
}
type UserForAdmin {
id: ID!
username: String!
avatarUrl: String!
email: String!
team: Team!
lastLogin: DateTime!
}
type UserForTeamMember {
id: ID!
username: String!
avatarUrl: String!
email: String!
team: Team!
}
You should consider using Schema Directives for this use case.
That basically allows you to only resolve some specific field if the user has permission for it. Otherwise you can return null or throw an error.
So in the end you would have a single type User like this:
directive #hasRole(role: String) on FIELD_DEFINITION
type User {
id: ID!
username: String!
avatarUrl: String!
email: String! #hasRole(role: "USER")
mailingAddress: Address! #hasRole(role: "USER")
team: Team! #hasRole(role: "USER")
lastLogin: DateTime! #hasRole(role: "USER")
}
Then you can have a directive resolver kinda like this:
const directiveResolvers = {
...,
hasRole: (next, source, {role}, ctx) => {
const user = getUser()
if (role === user.role) return next();
throw new Error(`Must have role: ${role}, you have role: ${user.role}`)
},
...
}
If you have a field that only ADMIN can query, you would just use the #hasRole(role: "USER") directive.
Then your service layer (or your resolver if you don't have a service layer) would be responsible to define which User to fetch (if your own user or some user based on ID as long as you have permission).
You can use directives for a lot of different use cases. Here are a few good references:
https://www.prisma.io/blog/graphql-directive-permissions-authorization-made-easy-54c076b5368e
https://www.apollographql.com/docs/apollo-server/schema/directives/

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.

change a related field without mutating the original one

I have this schema:
type Game {
id: ID! #id
status: Boolean #default(value: false)
time: DateTime!
location: String!
stadium: String!
teams: [Team!]! #relation(name: "BothTeams")
}
type Team {
id: ID! #id
name: String!
abbrName: String!
teamLogo: String!
score: Int #default(value: 0)
games: [Game!]! #relation(name: "BothTeams")
}
the Game type is gonna return typically two teams and each team is gonna have a score field.
so if i want to update a game later, specifically the score field,
i would have to change the score of every game with that changed team.
So, is there a way to change the score of a specific game, without mutating the original score.
Conceptually, a score is not really a property of a particular team. You want to represent it as a separate entity that's associated to both a team and a game. One way to do that:
type Game {
id: ID! #id
status: Boolean #default(value: false)
time: DateTime!
location: String!
stadium: String!
teams: [Team!]! #relation(name: "BothTeams")
scores: [Score!]! #relation(name: "ScoresByGame")
}
type Team {
id: ID! #id
name: String!
abbrName: String!
teamLogo: String!
games: [Game!]! #relation(name: "BothTeams")
}
type Score {
id: ID! #id
value: Int #default(value: 0)
team: Team #relation(name: "ScoreTeam")
}
Or instead of scores, you could have a homeScore field and an awayScore field. Either way, you'll have a Score node that you can mutate without impacting other games. Any other properties of a Team that might vary with each game should be treated similarly (for example, if you wanted to include what players played that particular game).

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