I made simple board api system with graphql.
And I use sequelize(version 4) for connect to database.
[schema.graphql]
type Article {
article_no: Int!
subject: String!
content: String!
createdAt: String!
updatedAt: String!
comment: String
}
type Comment {
article_no: Int!
content: String!,
createdAt: String!
}
type Query {
articles: [Article]!
article(article_no: Int!): Article
comments: [Comment]!
comment(article_no: Int!): Comment
}
type Mutation {
createArticle(subject: String!, content: String!, password: String!): Boolean!
createComment(article_no: Int!, content: String!, password: String!): Boolean!
}
[resolvers.js]
import { Article, Comment } from './db';
const resolvers = {
Query: {
articles: async () => {
return Article.all();
},
article: async(_, args) => {
return Article.find({
where: args.article_no,
});
},
comments: async () => {
return Comment.all();
},
comment: async(_, args) => {
return Comment.find({
where: args.article_no
});
}
},
Mutation: {
createArticle: async (_, args) => {
try {
const article = await Article.create({
subject: args.subject,
content: args.content,
password: args.password
});
return true;
} catch(e) {
return false;
}
},
createComment: async(_, args) => {
try {
const comment = await Comment.create({
article_no: args.article_no,
content: args.content,
password: args.password
})
return comment;
} catch(e) {
return false;
}
}
}
}
export default resolvers;
[db.js]
const Sequelize = require('sequelize');
import config from '../config/config';
const db = new Sequelize(config.DB, config.USER, config.PASS, {
host: 'localhost',
dialect: 'mysql'
})
export const Article = db.define('article', {
article_no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
subject: {
type: Sequelize.STRING(30),
allowNull: false
},
content: {
type: Sequelize.STRING(100),
allowNull: false
},
password: {
type: Sequelize.STRING(20),
allowNull: false
},
comment: Sequelize.STRING
}, {
freezeTableName: true,
timestamps: true,
underscored: true
})
export const Comment = db.define('comment', {
content: {
type: Sequelize.STRING(150),
allowNull: false
},
password: {
type: Sequelize.STRING(20),
allowNull: false
},
}, {
freezeTableName: true,
timestamps: true,
underscored: true
})
Article.hasMany(Comment, {
foreignKey: 'article_no',
scope: {
comment: 'comment'
}
})
Comment.belongsTo(Article, {
foreignKey: 'article_no',
targetKey: 'article_no',
allowNull: false,
as: 'comment'
});
db.sync()
.then(console.log('[*] DB Sync Done'))
Article
and Comment
is 1:N realtionship.
So I set hasMany
to Article
and set belongsTo
to Comment
.
Also Comment as comment and include it to Article's scope.
But when I request query { article(id:1) { subject, comment } }
,
comment return null.
I refer document http://docs.sequelizejs.com/manual/tutorial/associations.html#foreign-keys and follow as well.
But it doesn't work.
My Expected result is here:
{
"data": {
"article": {
"subject": "test",
"comment": {
"article_no":1,
"content: "first comment",
"created_at": "2018010101001",
# every comment related article is here
}
}
}
}
Current result is here:
{
"data": {
"article": {
"subject": "test",
"comment": null
}
}
}
I want to display all the comments that related specific article.
Is there any solution about this?
Thanks.
Couple of notes to get you on the right track:
When defining a relationship, keep in mind that the model whose method you are calling is the one that you are effectively adding a property to. For example, if you call Article.hasMany(Comment)
, this will create a comments
property on the Article
model and will affect the Comment
model. Likewise, calling Comment.belongsTo(Article)
will create an article
property on the Comment
model and not affect the Article
model.
Note that even if the relationship is created, if you want to include the associated comments when you query an article (or vice versa), you must pass in the appropriate include
option. This can be done directly in the query call, i.e.
Article.findAll({ include: [Comment] })
or can be part of a scope, like the default scope. Scopes can be set as part of the model's definition, or added after the fact by calling addScope
. For example, you could do:
Article.addScope('defaultScope', {
include: [Comment],
}, { override: true })
Note: The override
here is necessary because the default scope already exists. To query the associated models, your query call or the scope you're using must include the appropriate include
array. Associations also have the idea of "scope" but it's different from a model's scope. See the docs here for a discussion of the differences.
Lastly, when passing a where
option to your find
call, you should make sure it includes the property names you want to query against, i.e.
Article.find({ where: { article_no: args.article_no } })
// or possibly even
Article.find({ where: args })
There are problems with your graphql schema, and your sequelize schema...
Lets look at your graphql schema
type Article { ... comment: String }
You wrote that Article and Comment have a 1:M relation but here the comment field is of type string and not even an array of string
the correct type definition for Article should be (imo):
type Article { ... comments: [Comment] }
now if you make the adjustments to your sequelize schema that @Daniel-Rearden
wrote in his answer your Article
model should have a comments
property so it will be returned by default from the default resolver
of the Article
type
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With