Mongoose schema:
const postSchema = new mongoose.Schema({
title: { type: String },
content: { type: String },
comments: {
count: { type: Number },
data: [{
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
content: { type: String },
created: { type: Date },
replies: [{
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
content: { type: String },
created: { type: Date },
}]
}]
}
})
I don't want to normalize comments and replies because:
If I normalized it, I would have to:
This is one of the biggest strengths of MongoDB: to group your data specifically to suit your application data needs but GraphQL and Relay.js doesn't seem to support that?
Question:
Is there a way to get comments nested inside of a single post object (or at least get replies nested inside of a single comment) in order to get the whole thing in a single read?
GraphQL schema:
const postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
title: { type: GraphQLString },
content: { type: GraphQLString },
comments: {
// ?????
}
}),
interfaces: [nodeInterface],
})
const postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
title: { type: GraphQLString },
content: { type: GraphQLString },
commentCount: { type: GraphQLInt },
comments: { type: new GraphQLList(commentType) }
}),
interfaces: [nodeInterface]
})
const commentType = new GraphQLObjectType({
name: 'Comment',
fields: () => ({
content: { type: GraphQLString },
created: { type: GraphQLString },
author: {
type: userType,
resolve: async (comment) => {
return await getUserById(comment.author)
}
},
replies: {
type: new GraphQLList(commentType),
resolve: (comment) => {
// Log is below
console.log(comment)
// Error only occurs if I return it!
return comment.replies
}
}
})
})
Log (comment
):
{
author: 'user-1',
created: '05:35'
content: 'Wow, this is an awesome comment!',
replies:
[{
author: 'user-2',
created: '11:01',
content: 'Not really..',
},
{
author: 'user-1',
created: '11:03',
content: 'Why are you so salty?',
}]
}
This is one of the biggest strengths of MongoDB: to group your data specifically to suit your application data needs but GraphQL and Relay.js doesn't seem to support that?
GraphQL does support what you're trying to do. Before going into the details, we need to keep in mind that GraphQL is about exposing data, NOT about how we store data. We can store in whatever way we like - normalized or not. We just need to tell GraphQL how to get the data that we define in the GraphQL schema.
Is there a way to get comments nested inside of a single post object (or at least get replies nested inside of a single comment) in order to get the whole thing in a single read?
Yes, it's possible. You just define the comments as a list. However, GraphQL does not support arbitrary type of field in a GraphQL object type. Therefore, we need to define separate GraphQL types. In your mongoose schema, comments
is an object with count
of comments and the comments data
. The data
property is a list of another type of objects. So, we need to define two GraphQL objects - Comment
and CommentList
. The code looks like below:
const commentType = new GraphQLObjectType({
name: 'Comment',
fields: () => ({
author: { type: GraphQLString },
content: { type: GraphQLString },
created: { type: GraphQLString },
replies: { type: new GraphQLList(commentType) },
}),
});
const commentListType = new GraphQLObjectType({
name: 'CommentList',
fields: () => ({
count: {
type: GraphQLInt,
resolve: (comments) => comments.length,
},
data: {
type: new GraphQLList(commentType),
resolve: (comments) => comments,
},
}),
});
const postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
title: { type: GraphQLString },
content: { type: GraphQLString },
comments: {
type: commentListType,
resolve: (post) => post.comments,
}
}),
interfaces: [nodeInterface],
});
I don't want to normalize comments and replies because:
- I will always need post comments together (read in a single go)
- I will never use comments to query or sort anything
- It's so much easier to create, update and delete post with comments (document locking)
Defining post and comment GraphQL object types in the above way let you:
id
field.