I am using the library join-monster with GraphQL and postgres, with GraphiQL as client. When querying the database, I got the error: "joinMonster is not a function".
The method joinMonster() is provided by the library and is used in the resolvers.
The connection to the database is by knex and, apparently, it works. if I run the following code, I get the data form the table:
knex('students').then(rows => console.log(rows))
Database diagram
GraphiQL outpup
This is the schema-resolvers code:
const joinMonster = require('join-monster');
const knex = require('knex')({
client: 'postgres',
connection: {
host: 'localhost',
user: 'postgres',
password: 'myPassword',
database: 'test'
}
});
const { graphQLSchema } = require("graphql");
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQL
} = require('graphql');
const Subject = new GraphQLObjectType({
name: "Subject",
sqlTable: 'subjects',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
idEncoded: {
description: 'The ID base-64 encoded',
type: GraphQLString,
sqlColumn: 'id',
// specifies SQL column and applies a custom resolver
resolve: user => toBase64(user.idEncoded)
},
teacher: {
type: GraphQLString
},
students: {
type: new GraphQLList(Student),
junction: {
sqlTable: 'class',
sqlJoins: [
(subjectTable, junctionTable, args) => `${subjectTable}.id = ${junctionTable}.subject_id`,
(junctionTable, studentTable, args) => `${junctionTable}.student_id = ${studentTable}.id`
]
}
}
})
});
const Student = new GraphQLObjectType({
name: "Student",
sqlTable: 'students',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
idEncoded: {
description: 'The ID base-64 encoded',
type: GraphQLString,
sqlColumn: 'id',
resolve: user => toBase64(user.idEncoded)
},
lastname: {
type: GraphQLString
},
subjects: {
type: new GraphQLList(Subject),
junction: {
sqlTable: 'class',
sqlJoins: [
(studentTable, junctionTable, args) => `${studentTable}.id = ${junctionTable}.student_id`,
(junctionTable, subjectTable, args) => `${junctionTable}.subject_id = ${subjectTable}.id`
]
}
}
})
});
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
student: {
type: Student,
args: {
id: {
type: GraphQLInt
}
},
where: (studentsTable, args, context) => {
if (args.id) return `${studentsTable}.id = ${args.id}`
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, {}, sql => {
return knex.raw(sql)
})
}
},
subject: {
type: Subject,
args: {
id: {
type: GraphQLInt
}
},
where: (subjectsTable, args, context) => {
if (args.id) return `${subjectsTable}.id = ${args.id}`
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, {}, sql => {
return knex.raw(sql)
})
}
}
})
})
const schema = new GraphQLSchema({
query: QueryRoot,
});
module.exports = schema;
function toBase64(clear) {
return Buffer.from(String(clear)).toString('base64')
}
I have followed the documentation from https://join-monster.readthedocs.io/
Thanks
You can also import the function like this:
const joinMonster = require('join-monster').default;
The problem is that when making available the library to the file, an object is provided instead of a function.
const joinMonster = require('join-monster');
console.log(joinMonster)
// output
{ default: { [Function: joinMonster] getNode: [Function: getNode], version: '2.0.16' } }
I don't know why is served an object instead of a function. But
now, I invoke joinMonster.default, and it works:
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo, {}, sql => {
return knex.raw(sql)
})
Related
In GraphQL Code First Approach I am trying to pass the same argumment for createUser in createManyUser but I want to pass it as an array to create many users at once. I searched a lot but couldn't find it in GraphQL Code First Approach.
The Code
export const createUser = {
type: userType,
args: { // This work fine
email: { type: string },
username: { type: string },
firstName: { type: string },
lastName: { type: string }
},
resolve: async (_, args, { userAuth }) => {
try {
const user = await db.models.userModel.create(args);
return user;
} catch (error) {
throw Error(`${error.message}`)
}
}
}
export const createManyUser = {
type: new GraphQLList(userType),
args: [{ // Here I made an array [] but it dose not work so here is my problem
email: { type: string },
username: { type: string },
firstName: { type: string },
lastName: { type: string }
}],
resolve: async (_, args, { userAuth }) => {
try {
const user = await db.models.userModel.bulkCreate(args);
return user;
} catch (error) {
throw Error(`${error.message}`)
}
}
}
You can't just put the args options in an array, to tell GraphQL that you expect a list of things you explicitly need to construct a GraphQLList type.
And you can't make a mutation field take a list of named things either - you must give the mutation one named argument that expects a list of input objects. So it'll be
export const createManyUser = {
type: new GraphQLList(userType),
args: {
inputs: { type: new GraphQLList(new GraphQLNonNull(new GraphQLInputObjectType({
// ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
name: 'CreateUserInput',
description: 'Input payload for creating user',
fields: {
email: { type: string },
username: { type: string },
firstName: { type: string },
lastName: { type: string }
}
})))
},
resolve: async (_, args, { userAuth }) => {
const user = await db.models.userModel.bulkCreate(args.inputs);
// ^^^^^^^
return user;
}
}
See also this article.
I'm learning GraphQL so I got a strange issue
I have this code on one file Schema.js:
const graphQL = require('graphql');
const lodash = require('lodash')
const { GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLSchema, GraphQLList } = graphQL;
const StatusType = new GraphQLObjectType({
name: 'Status',
fields: () => ({
id: { type: GraphQLInt },
statusName: { type: GraphQLString },
user: {
type: new GraphQLList(UserType),
resolve(parentValue, args){
}
}
})
});
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLString },
username: { type: GraphQLString },
mail: { type: GraphQLString },
password: { type: GraphQLString },
status: {
type: StatusType,
resolve(parentValue, args){
}
},
})
});
const RouteQuery = new GraphQLObjectType({
name: 'RouteQuery',
user: {
type: UserType,
args: { id: { type: GraphQLString } },
resolve(parentValue, args){
//return lodash.find(users, { id: args.id })
}
},
userSome: {
type: new GraphQLList(UserType),
args: { id: { type: GraphQLString } },
resolve(parentValue, args){
if (args.id) {
//return users.filter(user => user.id === args.id);
}
//return users;
}
},
userAll: {
type: new GraphQLList(UserType),
resolve(parentValue){
//return users
}
},
status:{
type: StatusType,
args: { id: { type: GraphQLInt } },
resolve(parentValue, args){
//return lodash.find(status, { id: args.id })
}
},
statusAll: {
type: new GraphQLList(StatusType),
resolve(parentValue){
//return users
}
}
}
});
module.exports = new GraphQLSchema({
query: RouteQuery
})
This code run succesfully but when i try to separate these into multiple files: the const StatusType & UserType like the following case:
the StatusType is on StatusType.js file and the UserType is on UserType.js file
StatuType.js file:
const graphQL = require('graphql');
const { GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLSchema, GraphQLList } = graphQL;
const UserType = require('./UserType')
const StatusType = new GraphQLObjectType({
name: 'Status',
fields: () => ({
id: { type: GraphQLInt },
statusName: { type: GraphQLString },
user: {
type: new GraphQLList(UserType),
resolve(parentValue, args){
//return users.filter(user => user.status === parentValue.id);
}
}
})
});
module.exports = StatusType;
UserType.js file:
const graphQL = require('graphql');
const { GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLSchema, GraphQLList } = graphQL;
const StatusType = require('./StatusType')
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLString },
username: { type: GraphQLString },
mail: { type: GraphQLString },
password: { type: GraphQLString },
status: {
type: StatusType,
resolve(parentValue, args){
//return lodash.find(status, { id: parentValue.status })
}
},
})
});
module.exports = UserType;
And on the Schema.js file i include these 2 like that:
const StatusType = require('./StatusType');
const UserType = require('./UserType');
so instead of putting the all code on the same file, i putted the StatusType and UserType on respective files.
but when i run this code, i got this error:
So i don't know what the problem here :/
But when i'm tring to console.log the const UserType = require('./UserType') i got User as response :o like when it was on the same code on Schema.js
You are facing a problem in the way nodeJs handle require. See http://nodejs.org/api/modules.html#modules_cycles for how require is handled in node.
Specifically in your case, when you do:
const StatusType = require('./StatusType');
const UserType = require('./UserType');
StatusType is loaded from const StatusType = require('./StatusType');
StatusType.js loads UserType from const UserType = require('./UserType')
UserType.js should require StatusType but nodeJs prevent this to avoid infinite loop. As a result, it executes next lines
UserType is initialized as new GraphQLObjectType(...) and defined fields as a function. The function closure hand a variable StatusType not yet initialized. It's just an empty exported module {}
You can verify that adding console.log(StatusType); when creating UserType fields:
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => {
console.log(StatusType);
return ({
id: { type: GraphQLString },
username: { type: GraphQLString },
mail: { type: GraphQLString },
password: { type: GraphQLString },
status: {
type: StatusType,
resolve(parentValue, args) {
}
},
});
}
});
You'll get:
{} //instead of StatusType
You didn't encounter this problem when everything was in the same file because both UserType and StatusType are defined within the same closure and now each others.
To resolve that you had to define UserType and StatusType on the same level and inject them. A good example of how to do it can be found here. In your case:
// StatusType.js
const StatusType = (types) => new GraphQLObjectType({
name: 'Status',
fields: () => {
console.log(types.UserType);
return ({
id: { type: GraphQLInt },
statusName: { type: GraphQLString },
user: {
type: new GraphQLList(types.UserType),
resolve(parentValue, args) {
}
}
});
}
});
module.exports = StatusType;
// UserType.js
const UserType = (types) => new GraphQLObjectType({
name: 'User',
fields: () => {
console.log(types.StatusType);
return ({
id: { type: GraphQLString },
username: { type: GraphQLString },
mail: { type: GraphQLString },
password: { type: GraphQLString },
status: {
type: types.StatusType,
resolve(parentValue, args) {
}
},
});
}
});
module.exports = UserType;
// Schema.js
const StatusTypeInject = require('./StatusType');
const UserTypeInject = require('./UserType');
const types = {};
types.StatusType = StatusTypeInject(types);
types.UserType = UserTypeInject(types);
const StatusType = types.StatusType;
const UserType = types.UserType;
You could do with some cleaning up here and here's how I'd resolve these situations:
[..]
// import GraphQLNonNull from the graphql lib
// In your case, I'd use GraphQLID instead of GraphQLString
userSome: {
type: new GraphQLList(require('../path/to/UserType')),
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve: async (parentValue, args) => {
// No need for the if statement as we'd sure to have an id.
// return await filter users by id.
}
},
[..]
And as always, keep your fields as functions: fields: () => ({})
Injection solves this error
// StatusType.js
const StatusType = (types) => new GraphQLObjectType({
name: 'Status',
fields: () => {
console.log(types.UserType);
return ({
id: { type: GraphQLInt },
statusName: { type: GraphQLString },
user: {
type: new GraphQLList(types.UserType),
resolve(parentValue, args) {
}
}
});
}
});
module.exports = StatusType;
Create a types.js file in your directory.
Here you can add all your types that integrate with other types and inject the required types.
const UserTypeInject = require("./UserType");
const StatusTypeInject = require("./Author");
const types = {};
types.UserType = UserTypeInject(types);
types.StatusType = StatusTypeInject(types);
const UserType = types.UserType;
const StatusType = types.StatusType;
module.exports = { UserType, StatusType };
Whenever you need the type either in Mutations or Queries, you import it from the types.js files as such;
const { UserType } = require("../types");
https://i.stack.imgur.com/ORvCG.png
Please see the above Image and code line: 5.
I was inserting data into my client collection using Mutation GraphQL and used a single quote for ClientType ('ClientType') and got the same error. I have just removed a single quote as the type wants the client GraphQLObjectType and I was providing ClientType as String.
I searched for it for 2 days.
Hope it will help anyone.
You are importing UserType inside StatusType before declaration.
const StatusType = require('./StatusType');
const UserType = require('./UserType');
I'm new with GrpahQL and I'm trying to simulate Many to Many relationship between Users and Groups. I have the followinf types defined in my schema:
// UserType.js
const {
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLID } = require('graphql');
const {
GraphQLEmail } = require('graphql-custom-types');
const GroupType = require('./GroupType'); const AuthService = require('../../services/AuthService');
let authService = new AuthService();
const UserType = new GraphQLObjectType({
name: "UserType",
fields: () => ({
id: { type: GraphQLID },
user: { type: GraphQLString },
password: { type: GraphQLString },
name: { type: GraphQLString },
lastname: { type: GraphQLString },
email: { type: GraphQLEmail },
groups: {
type: new GraphQLList(GroupType),
resolve(parentValue) {
return authService.userGroups(userId);
}
}
}) });
module.exports = UserType;
and this is the other file:
// GroupType.js
const {
GraphQLObjectType,
GraphQLString,
GraphQLID,
GraphQLList
} = require('graphql');
const UserType = require('./UserType');
const AuthService = require('../../services/AuthService');
let authService = new AuthService();
const GroupType = new GraphQLObjectType({
name: "GroupType",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
description: { type: GraphQLString },
users: {
type: new GraphQLList(UserType),
resolve(parentArgs) {
return authService.userGroups(parentArgs.id);
}
}
})
});
module.exports = GroupType;
This example doesn't work for me because for some reason i got this error:
Error: Can only create List of a GraphQLType but got: [object Object].
This error happens only for the GroupType and not for the UserType when both are similars. What's going on here? What am I doing wrong?
The problem is that UserType requires GroupType, and GroupType requires UserType: this is known as a circular dependency.
What happens is that UserType.js gets required, exports an {} while finishing to run (this is standard Node.js module execution), requires GroupType, which requires back UserType and gets an empty object back, and exports the correct GraphQL GroupType to UserType. So UserType works because it is a list of GroupType, but GroupType doesn't it got an empty object for its require of UserType.
To circumvent this, you can use a runtime require in GroupType.js:
// GroupType.js
...
// Remove the line which requires UserType at the top
// const UserType = require('./UserType');
const AuthService = require('../../services/AuthService');
...
const GroupType = new GraphQLObjectType({
...
fields: () => ({
...
users: {
type: new GraphQLList(require('./UserType')), // Require UserType at runtime
...
}
})
});
...
I have to solve a cyclic definition for GraphQL, but for some reason I´m not being able to solve. I´ve checked several posts about thunks and lazy loading and here is my current code:
History/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "History")
},
timestamp: {
type: GraphQLLong
},
objectId: {
type: GraphQLString
},
user: {
type: require('../User/connections').default,
args: connectionArgs,
resolve: (source, args) => {
return UserModel.findOne({ id: source.id }).exec();
}
},
action: {
type: new GraphQLNonNull(GraphQLString)
}
};
export const HistoryType = new GraphQLObjectType({
name: 'History',
description: 'History',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof HistoryModel,
fields: () => (fields)
});
History/connections.js:
import { HistoryType } from './types';
const { connectionType: HistoryConnectionType } = connectionDefinitions( { nodeType: HistoryType });
export default HistoryConnectionType;
User/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "User")
},
email: {
type: GraphQLString
},
history: {
type: require('../History/connections').default,
args: connectionArgs,
resolve: (source, args, context) => {
return HistoryModel.find({ deleted: false, objectId: source.id}).sort('timestamp').exec();
}
}
};
export const UserType = new GraphQLObjectType({
name: 'User',
description: 'User',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof UserModel,
fields: () => (fields)
});
User/connections.js
import { UserType } from './types';
const { connectionType: UserConnectionType } = connectionDefinitions( { nodeType: UserType });
export default UserConnectionType;
Company/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "Company")
},
name: {
type: GraphQLString
},
users: {
type: require('../User/connections').default,
args: connectionArgs,
resolve: (source, args) => {
const { id } = source;
return UserModel.find({ companyId: id }).then((rows) => connectionFromArray(rows,args));
}
},
history: {
type: require('../History/connections').default,
args: connectionArgs,
resolve(source, args, context) {
return loaders.getHistory(source.id);
}
}
};
export const CompanyType = new GraphQLObjectType({
name: 'Company',
description: 'Company',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof CompanyModel,
fields: () => (fields)
});
Company/connections.js
import { CompanyType } from './types';
const { connectionType: CompanyConnectionType } = connectionDefinitions( { nodeType: CompanyType });
export default CompanyConnectionType;
I can´t make it work. When running I´m getting the following output:
History.user field type must be Output Type but got: undefined
I guess you resolved it by now, but for anyone passing by:
In history/types.js, the fields object must be inlined in the fields function of HistoryType, so it gets created at runtime.
const HistoryType = new GraphQLObjectType({
...
fields: () => ({ ... }), // not a var, object is inlined
...
});
I have to two GraphQLObjectType
const AType = new GraphQLObjectType({
name: 'A'
fields: () => ({
id: globalIdField('A', obj => obj._id),
Id: {
type: GraphQLID,
resolve: obj => obj._id
},
email: { type: GraphQLString },
name: { type: GraphQLString },
status: { type: GraphQLString },
description: { type: GraphQLString }
}),
interfaces: [nodeInterface]
});
and another Type BType, i am using AType inside the type of so t
const BType = new GraphQLObjectType({
name: 'BType',
fields: {
id: globalIdField('BType'),
ApplicantsDetails: {
type: AType,
resolve: obj => {
return obj.applicantsId;
}
},
respo_2: {
type: GraphQLString,
resolve: obj => "I have declared a ObjectType inside another"
}
},
interfaces: [nodeInterface]
});
and the main type from where i am returning a promise, so when i return the promise resolve(d), it should go to the BType and
const { connectionType: EmployerDashBoardConnectionType} =
connectionDefinitions({
name: 'EmployerDash',
nodeType: BType
});
above is the connection
EmployerDashBoardConnection: {
type: EmployerDashBoardConnectionType,
description: 'Employer DashBoard Details',
args: connectionArgsWithJobId,
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
}
/* Introduce your new fields here */
}
the response from Dash() function call is
{
applicantsId:
{ _id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z }
}
it only prints null
for using edges you need to pass an array to resolve function
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
// if d is object then
const a = [];
a.push(d);
resolve(connectionFromArray(a, args));
}
});
});
}
this code is to only solve your current problem
NOTE: while using relay connections you should always resolve a list [ ]
There are two problems in your code:
First problem:
The node type of EmployerDashBoardConnectionType is BType. So, the items returned in resolve() function of field EmployerDashBoardConnection should have the same properties - that's not the case in your code.
The structure of BType is:
{
id
ApplicantsDetails {
id,
Id,
email,
name,
status,
description,
}
respo_2,
}
whereas you are passing the following object, which totally does not match.
{
applicantsId: {
_id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z
}
}
Second problem:
This is why you're getting null for edges. Your resolve() function:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
where you return nothing. You can use connectionFromArray from graphql-relay npm module:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(connectionFromArray(d, args));
}
});
});
}
The value of d must be a list where each item should have top level properties id, ApplicantsDetails, respo_2.