Type as property resolves as null with graphql-yoga and prisma - node.js

I’m using a prisma server and graphql-yoga (graphql server on top of node.js) and I’m working for the first time with a type that has an other type as property. It’s one way so no relation I believe. In my case, a Product can be in a FavoriteList (so belong?) but a FavoriteList doesn’t have a relation the other way around.
type Product {
id: ID!
name: String!
releaseDate: DateTime!
brand: Brand!
createdAt: DateTime!
image: Image!
barcode: String
}
type FavoriteList {
id: ID!
products: [Product]
}
On my prisma admin I can add a record to FavoriteList with connecting [Product] and in the playground I can query for that by using the FavoriteLists query. But when I create a query (and resolver) for my graphql-yoga server, products are resolving as null. The query I’m using:
# prisma server & graphql server
query favoriteList {
favoriteLists {
id
products {
id
name
}
}
}
My setup for the graphql-server
type Query {
// ... other queries
favoriteLists: [FavoriteList]
}
// resovler in Query.js
async favoriteLists(root, args, context, info) {
return context.prisma.favoriteLists({}, info)
},
So having the same query, I am wondering if I have to do something else in my graphql-server? From what I can see the Product is not resolving on the FavoriteList type. Thanks in advance.

I would disagree and say you should have a relationship between Product and FavouriteList. When you break it down, one Product "belongs to" one or many FavouriteList and one FavouriteList "has" many Product. So I'd define your datamodel like this:
type Product {
id: ID!
name: String!
releaseDate: DateTime!
brand: Brand!
createdAt: DateTime!
image: Image!
barcode: String
favouriteLists: [FavouriteList!]! #relation(name: "ProductToFavouriteList")
}
type FavoriteList {
id: ID!
products: [Product!]! #relation(name: "ProductToFavouriteList")
}
Your query and resolvers look ok except maybe your resolver should return context.prisma.query.favoriteLists({}, info)?

Related

Apollo Graphql Client - How to attach new node to existing node

am trying to persist a new node in graphql using Apollo client. This new node should attach to an existing node. Not quite sure how to go about it. Suppose I have these type definitions:
type Person{
personId: ID!
name: String!
user: User #relationship(type: "LOGS_IN_AS", direction: OUT)
}
type User{
userId: ID!
username: String!
password: String!
person: Person!
}
Now assuming I already have a person in the database as follows:
{
personId: 1,
name: "John Doe"
}
How do I mutate a corresponding User node for this Person and ensure the necessary relationship is created using Apollo's auto generated mutations? Am using a neo4j backend by the way.
Thanks in advance
Don't know how you set up neo4j database but supposedly User should have a personId to link with Person.
You should probably defined a seperate schema type called UpsertUserInput or something like below for the mutation so neo4j can use it to link with the person
type UpsertUserInput{
userId: ID!
personId: ID!
username: String!
password: String!
}
the Appollo schema is not responsible for defining where data comes from or how it's stored. It is entirely implementation-agnostic.
https://www.apollographql.com/docs/apollo-server/schema/schema/#the-schema-definition-language
I see there is a solution, just needed to look a little deeper into the auto-generated mutations in the studio. Solution along the lines of...
useMutation(CREATE_USER, {
variables:{
input:{
userName: "johndoe#example.com",
password: "jdoespassword",
person:{
connect:{
where:{
node:{
personId: 1
}
}
}
}
}
}
})

Prisma js return join fields

I'm trying to implement batching and caching with the Facebook DataLoader. Let's say I have the following schema (taken from here):
type Post {
id: ID! #id
title: String!
published: Boolean! #default(value: false)
author: User
comments: [Comment!]!
}
type User {
id: ID! #id
name: String
posts: [Post!]!
comments: [Comment!]!
}
type Comment {
id: ID! #id
text: String!
post: Post!
writtenBy: User!
}
I am working on a tricky resolver which lists all comments created by the same user under the same post for a given comment. To retrieve a single entry I would go like:
const fetchCommentsByUserForSamePost = async (commentId: string, userId: string): Promise<Comment[]> => {
const comments = await this.prisma.comment.findOne({ where: { id: commentId } })
.post()
.comments({
where: {
writtenBy: { id: userId }
}
})
return comments;
}
This works well for a single query, but I would like to batch the queries. In raw SQL I'd return commentId and userId in every row, such that I can group the results by these fields. But I can't find a way to return the original commentId with Prisma to generalize the query to work with a list of commentId - userId pairs.
Is there a way to accomplish this with Prisma, or I am missing something? I am aware that making two requests could solve this, but that would result in an involved logic, plus I'd rather avoid making two DB roundtrips.
Prisma 2.0 already has a Dataloader built in exactly for this purpose. This means your resolvers might do multiple calls to findOne but those will be batched into one big SQL query under the hood. So there should be no need for you to implement this optimization on your own.

NodeJS/GraphQL: Trying to use a custom DateTime scalar and I'm getting an error

I'm following a tutorial where the teacher is using a String type for his createdAt fields and suggested that if we wanted, to use a custom scalar type for a stronger typed DateTime field so I'm trying to do just that.
I'm getting the following error: Error: Unknown type "GraphQLDateTime".
Here is the offending code:
const { gql } = require('apollo-server')
const { GraphQLDateTime } = require('graphql-iso-date')
module.exports = gql`
type Post {
id: ID!
username: String!
body: String!
createdAt: GraphQLDateTime!
}
type User {
id: ID!
email: String!
token: String!
username: String!
createdAt: GraphQLDateTime!
}
input RegisterInput {
username: String!
password: String!
confirmPassword: String!
email: String!
}
type Query {
getPosts: [Post]
getPost(postId: ID!): Post
}
type Mutation {
register(registerInput: RegisterInput): User
login(username: String!, password: String!): User!
createPost(body: String!): Post!
deletePost(postId: ID!): String!
}
`
I have added the graphql-iso-date library and VSCode's intellisense is picking that up so I know that's not the issue. It's also indicating that GraphQLDateTime is not being used anywhere in the file even though I'm referencing it.
I know this is probably an easy fix but I'm still new to NodeJS and GraphQL in the context of NodeJS. Any idea what I'm doing wrong? Also is there another DateTime scalar that might be more preferable (Best practices are always a good idea.) Thanks!
There's two steps to adding a custom scalar using apollo-server or graphql-tools:
Add the scalar definition to your type definitions:
scalar DateTime
Add the actual GraphQLScalar to your resolver map:
const { GraphQLDateTime } = require('graphql-iso-date')
const resolvers = {
/* your other resolvers */
DateTime: GraphQLDateTime,
}
Note that the key in the resolver map (here I used DateTime) has to match whatever you used as the scalar name in Step 1. This will also be the name you use in your type definitions. The name itself is arbitrary, but it needs to match.
See the docs for more details.

GraphQL: How nested to make schema?

This past year I converted an application to use Graphql. Its been great so far, during the conversion I essentially ported all my services that backed my REST endpoints to back grapqhl queries and mutations. The app is working well but would like to continue to evolve my object graph.
Lets consider I have the following relationships.
User -> Team -> Boards -> Lists -> Cards -> Comments
I currently have two different nested schema: User -> team:
type User {
id: ID!
email: String!
role: String!
name: String!
resetPasswordToken: String
team: Team!
lastActiveAt: Date
}
type Team {
id: ID!
inviteToken: String!
owner: String!
name: String!
archived: Boolean!
members: [String]
}
Then I have Boards -> Lists -> Cards -> Comments
type Board {
id: ID!
name: String!
teamId: String!
lists: [List]
createdAt: Date
updatedAt: Date
}
type List {
id: ID!
name: String!
order: Int!
description: String
backgroundColor: String
cardColor: String
archived: Boolean
boardId: String!
ownerId: String!
teamId: String!
cards: [Card]
}
type Card {
id: ID!
text: String!
order: Int
groupCards: [Card]
type: String
backgroundColor: String
votes: [String]
boardId: String
listId: String
ownerId: String
teamId: String!
comments: [Comment]
createdAt: Date
updatedAt: Date
}
type Comment {
id: ID!
text: String!
archived: Boolean
boardId: String!
ownerId: String
teamId: String!
cardId: String!
createdAt: Date
updatedAt: Date
}
Which works great. But I'm curious how nested I can truly make my schema. If I added the rest to make the graph complete:
type Team {
id: ID!
inviteToken: String!
owner: String!
name: String!
archived: Boolean!
members: [String]
**boards: [Board]**
}
This would achieve a much much deeper graph. However I worried how much complicated mutations would be. Specifically for the board schema downwards I need to publish subscription updates for all actions. Which if I add a comment, publish the entire board update is incredibly inefficient. While built a subscription logic for each create/update of every nested schema seems like a ton of code to achieve something simple.
Any thoughts on what the right depth is in object graphs? With keeping in mind the every object beside a user needs to be broadcast to multiple users.
Thanks
GraphQL's purpose is to avoid a couple of queries, so I'm sure that making the nested structure is the right way. With security in mind, add some GraphQL depth limit libraries.
GraphQL style guides suggest you have all complex structures in separate Object Types ( as you have, Comment, Team, Board... ).
Then making a complex query/mutation is up to you.
I'd like you to expand this sentence
Which if I add a comment, publish the entire board update is
incredibly inefficient
I'm not sure about this as you have your id of the Card. So adding new comment will trigger mutation which will create new Comment record and update Card with the new comment.
So your structure of data on the backend will define the way you fetch it but not so much the way you mutate it.
Take a look at the GitHub GraphQL API for example:
each of the mutations is a small function for updating/creating piece of the complex tree even if they have nested structure of types on the backend.
In addition for general knowledge of what are approaches for designing the mutations, I'd suggest this article.
You can use nesting in GraphQL like
type NestedObject {
title: String
content: String
}
type MainObject {
id: ID!
myObject: [NestedObject]
}
In the above code, the type definition of NestObject gets injected into the myObject array. To understand better you can see it as:
type MainObject {
id: ID!
myobject: [
{
title: String
content: String
}
]
}
I Hope this solves your problem!

graphql prisma nodejs postgresql how to add date created/update fild to graphql type?

I am using graphql + prisma (in docker locally), nodejs and postgresql.
How can I make it add some fields like created/edited date?
For example . I have this type:
type Post {
id: ID! #unique
title: String!
content: String!
published: Boolean! #default(value: "false")
author: User!
}
How can i add field like date. Make it equal to date the element created/updated?
Make this change in datamodel.prisma:
type Post {
id: ID! #unique
title: String!
content: String!
published: Boolean! #default(value: "false")
author: User!
updatedAt: DateTime!
createdAt: DateTime!
}
Make this change in schema.graphql:
updatedAt: String!
createdAt: String!
There are 2 fields hidden by default on each type you create in Prisma, but they're always created and kept up to date in the background: createdAt: DateTime! and updatedAt: DateTime!. To expose them, simply add them to your type and try querying them, you should see that there's data already.
Also note that they can't be deleted, by removing them from your schema you hide them again.
Hope this helps.

Resources