Prisma data modeling has many and belongs to - graphql

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.

Related

Apollo GraphiQL browser playground: How to nest queries?

Wish to run a nested query within as Apollo GraphiQL browser playground.
Schema for two queries intended to be nested and a response type:
getByMaxResults(maxResults: Int = null): [ID!]!
appById(id: ID!): AppOutputGraphType
AppOutputGraphType = {
accountIdGuid: ID!
createdAt: DateTime!
description: String!
id: ID!
updatedAt: DateTime!
userId: ID
}
Query getByMaxResults: [ID!]!, the result is an array of element ID:
Query appById(id: ID!) requesting a couple of AppOutputGraphType fields:
Is there a way within the GraphiQL browser sandbox to nest these two Queries?:
query getByMaxResults gather an array of IDs.
Iterate this array and perform multiple queries appById with each ID.
Thank you
Change your definition of the getByMaxResults query to:
getByMaxResults: [AppOutputGraphType!]!
Change your resolver to return an array containing all the fields of the AppOutputGraphType objects and not just the id.
Then just:
query {
getByMaxResults {
id
updatedAt
}
}
There is no way in GraphQL to nest queries but the whole point of the language is to remove the need to do so.
With the proper resolvers you could even get the related account and user for each AppOutputGraphType.
Change:
AppOutputGraphType = {
account: Account <= don't include foreign keys, refer to the related type
createdAt: DateTime!
description: String!
id: ID!
updatedAt: DateTime!
user: User <= don't include foreign keys, refer to the related type
}
Then you can do queries like:
query {
getByMaxResults {
id
updatedAt
account {{
id
name
city
}
user {
id
firstName
lastName
}
}
}

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.

How to resolve subselections / relations in prisma (nested lists)

Let's take an example from the github repo of prisma:
We have a user, the user could have multiple posts, and one post could have multiple links.
My goal is, to retrieve all posts and all links.
This means, my response is a list (links) in a list (posts).
I want to map the values I get back as two nested lists.
datamodel.prisma
type User {
id: ID! #id
email: String! #unique
name: String
posts: [Post]!
}
type Post {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
published: Boolean! #default(value: false)
title: String!
content: String
author: User!
links: [Link]!
}
type Link {
id: ID! #id
url: String
title: String
post: Post!
}
schema.graphql
type Query {
...
}
type Mutation {
...
}
type Link {
id: ID!
url: String
title: String
post: Post!
}
type Post {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
published: Boolean!
title: String!
content: String
author: User!
}
type User {
id: ID!
email: String!
name: String
posts: [Post]!
}
I want to query all posts of a user, and all of the links for every post in the response.
How would I query this request?
user {
id
posts {
id
links {
id
}
}
}
The above code snipper would not work.
EDIT
I want to use the following:
User: {
listPosts: (parent, args, context, info) {
return context.prisma.posts().links()
}
}
So in my response (data in front-end via react-apollo Query Component), I want to map over posts AND the links in each post.
BUT the links attribute in posts is null.
Is there another way to achieve this?!
According to the docs:
Prisma client has a fluent API to query relations in your database. Meaning you can simply chain your method calls to navigate the relation properties of the returned records. This is only possible when retrieving single records, not for lists. Meaning you can not query relation fields of records that are returned in a list.
In order to get around that limitation, you can use the $fragment method:
const fragment = `
fragment UserWithPostsAndLinks on User {
id
email
name
posts {
id
title
content
links {
id
url
title
}
}
}
`
const userWithPostsAndLinks = await prisma.user({ id: args.id }).$fragment(fragment)

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

How to "upsert" an array/nested field when issuing update mutation in Graphcool / GraphQL?

I have a Post type than has a tag field which can be associated with many Tag entries (man-to-many relationship). The issue I am running into is when updating a Post - I need to both create and associate new Tag's for tags that do not yet exist, while preserving the existing Post->Tag relationships. Basically, what I am looking for is something akin to an upsert when issuing a mutation update on a nested one-to-many field.
Here is my schema:
type Post #model {
createdAt: DateTime!
createdBy: User #relation(name: "PostsByUser")
description: String #defaultValue(value: "''")
id: ID! #isUnique
tags: [Tag!]! #relation(name: "TagsOfPost")
...
}
type Tag #model {
id: ID! #isUnique
tag: String!
createdBy: User #relation(name: "TagsByUser")
createdAt: DateTime!
posts: [Post!]! #relation(name: "TagsOfPost")
}
This mutation works to update a Post and add new tags, but overwrite all of the existing values in the Post's tag field:
mutation updatePost(
$id: ID!
$createdById: ID!
$timestamp: DateTime!
$description: String
$tags: [PosttagsTag!]!
) {
updatePost(
id: $id
createdById: $createdById
timestamp: $timestamp
description: $description
tags: $tags
) {
id
timestamp
description
tags {
id
tag
}
createdBy {
id
username
}
}
}
I came across this post by #marktani but it's not clear how to implement the combined method he outlines:
Combined
You can also use tags and tagsIds in the same mutation, this would connect the new Tutorial node to all the tags in tagsIds and connect it to the new tags in tags. This is what you want to do if you want to only allow tags with a unique text, so for a new Tutorial, there will likely be some tags that already exists, and some that need to be created.
Is it currently impossible to do this with one mutation? Would a second mutation be required after updating the post with new tags to re-establish the associations between the Post and existing Tag ids, i.e., having to repeatedly call addToTagsOfPost(tagsTagId: ID!
postsPostId: ID!)? Thanks!
Ok, so there is currently a Graphcool bug where passing in both tags and tagsIds to the mutation will create and associate newly created Tags but will not add the tagsIds associations to existing Tags. I posted an issue on Graphcool's GitHub repo and they have acknowledged it - https://github.com/graphcool/framework/issues/1288

Resources