change a related field without mutating the original one - graphql

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).

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/

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.

Do I have to create a new type to define array of objects in GraphQL schemas?

I’m trying to replicate a REST API that I’ve built in the past and one part that got me thinking was if one of my tables had an array of objects. So for example, I have a table called Profile and it contains arrays Experience and Education that would strictly be under Profile but has its own fields as well but not its own table.
While I was adding fields in GraphQL, I bumped into this not really having a solid solution other than to create new types and then associating them with relationships and then have a resolver or a front-end make sure a Profile is created first before the Experience/Education portion is. I’m not sure if it’s the right way to do it or if there is a better way to do it. Down below is a snippet of what I ended up using… looking at the admin page, there are created tables for Profile, Experience and Education which is expected. But is there a way to only have just Profile and accomplish something similar? Or is this more of a way of life with GraphQL?
type Profile {
id: ID! #id
handle: String!
company: String
website: String
location: String
status: String!
githubUsername: String
experience: [Experience!] #relation(link: INLINE)
education: [Education!] #relation(link: INLINE)
}
type Experience {
id: ID! #id
title: String!
company: String!
}
type Education {
id: ID! #id
title: String!
company: String!
}
In Prisma, you can use embedded types. You would drop the #relation directive and add #embedded directives to the types you're embedding:
type Profile {
id: ID! #id
handle: String!
company: String
website: String
location: String
status: String!
githubUsername: String
experience: [Experience!]
education: [Education!]
}
type Experience #embedded {
title: String!
company: String!
}
type Education #embedded {
title: String!
company: String!
}
However, this only possible if you're using MongoDB for your database and there's some specific limitations listed in the docs when using embedded 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

Assigning a Type to a field in GraphQL Mutation

In the example below I am trying to create a Creature in my database each creature has many attributes so I created an CreatureAttribute Type with many different required String and Int fields. How do I attach that Type to the Creature Type Mutation?
mutation{
createCreature(data: {
creature_name: "Drake"
creature_type: "Dragon"
creature_size: "Huge"
description: "description Text..."
habitat: "habitat text..."
combat: "combat text..."
additional_info: "additional info text..."
attributes: ********this is where I would like to bring in my CreatureAttributes Type********
)
{
creature_name
creature_type
description
habitat
combat
additional_info
attributes
}
}
Thank you for your answers in advance :)
As per my understanding you want to create a relation "Creature has may attributes". So attribute should accept array as shown in following sample.
type Creature {
id: ID! #unique
creature_name: String!
creature_type: String!
creature_size: String!
description: String!
habitat: String!
combat: String!
additional_info: String!
attributes: [CreatureAttributes]!
}
type CreatureAttributes {
strength: Int!
health: Int!
stamina: Int!
mana: Int!
reaction: Int!
...}
Ref - https://graphql.org/learn/schema/#object-types-and-fields

Resources