how to reuse resolvers in graphql - graphql

I am new to graphql, I was creating following schema with graphql
// promotion type
const PromoType = new GraphQLObjectType({
name: 'Promo',
description: 'Promo object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the promo'
},
title: {
type: GraphQLString,
description: 'this is just a test'
},
departments: {
type: new GraphQLList(DepartmentType),
description: 'departments associated with the promo'
}
})
})
and department type
// department type
const DepartmentType = new GraphQLObjectType({
name: 'Department',
description: 'Department object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the department'
},
name: {
type: GraphQLString,
description: 'name of the department'
},
createdAt: {
type: GraphQLDate,
description: 'date the promo is created'
},
updatedAt: {
type: GraphQLDate,
description: 'date the promo is last updated'
}
})
});
and the following are the resolvers
// Promos resolver
const promos = {
type: new GraphQLList(PromoType),
resolve: (_, args, context) => {
let promos = getPromos()
let departments = getDepartmentsById(promos.promoId)
return merge(promos, departments)
}
};
//Departments resolver
const departments = {
type: new GraphQLList(DepartmentType),
args: {
promoId: {
type: GraphQLID
}
},
resolve: (_, args, context) => {
return getDepartmentsById(args.promoId)
}
};
the problem is I want to use the resolver of the departments into the resolver of the promos to get the departments.
I might be missing something obvious but is there any way to do this?

This is the way to do it. You want to think of it as graphs, rather than just a single rest endpoint.
To get data for Promo, you need to do it similarly to how I did it here, but for the parent node, if that makes sense. So, in e.g. viewer's resolve you add the query for Promo.
const PromoType = new GraphQLObjectType({
name: 'Promo',
description: 'Promo object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the promo',
},
title: {
type: GraphQLString,
description: 'this is just a test',
},
departments: {
type: new GraphQLList(DepartmentType),
description: 'departments associated with the promo',
resolve: (rootValue) => {
return getDepartmentsById(rootValue.promoId);
}
}
})
});

Related

How to change property values for an object nested in an array in graphql?

I've just started to learn GraphQL recently and have decided to implement it in a react based polling app where users can create and vote on polls.
I've created a mongoose model that looks like this https://github.com/luckyrose89/Voting-App/blob/master/backend/models/poll.js.
I'm facing an issue with adding upvotes to a poll option while writing Graphql mutations. So far my schema looks like this:
const AnswerType = new GraphQLObjectType({
name: "Answer",
fields: () => ({
id: { type: GraphQLID },
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionType = new GraphQLObjectType({
name: "Question",
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLID) },
question: { type: GraphQLString },
answer: { type: GraphQLList(AnswerType) }
})
});
const AnswerTypeInput = new GraphQLInputObjectType({
name: "AnswerInput",
fields: () => ({
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionTypeInput = new GraphQLInputObjectType({
name: "QuestionInput",
fields: () => ({
question: { type: new GraphQLNonNull(GraphQLString) },
answer: { type: new GraphQLNonNull(GraphQLList(AnswerTypeInput)) }
})
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addPoll: {
\\\\ code here
},
deletePoll: {
\\\\\ code here
},
upvotePoll: {
type: QuestionType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parent, args) {}
}
}
});
So I've defined my types and I can add and delete polls and access a single poll(I've skipped my queries section here). But I don't understand how to access a single poll's AnswerType object without retrieving unnecessary data and use it to write my upVote mutation.
I hope someone can guide me with this

GraphQL- conditionally determine the type of a field in a schema

I have the following mongoose schema:
const MessageSchema = new Schema({
author: {
account:{
type:String,
enum:['employee','admin'],
},
id: String,
}
//other fields
})
Then in my graphql-schemas file, I have the following schema types:
const MessageType = new GraphQLObjectType({
name: 'Message',
fields: () => ({
account: {
type: AuthorType,
//resolve method
},
id: {type: GraphQLString},
})
})
const AuthorType= new GraphQLObjectType({
name: 'Author',
fields: () => ({
account: {
type://This will either be AdminType or EmployeeType depending on the value of account in db (employee or admin),
//resolve method code goes here
}
})
})
As indicated in the comments of AuthorType, I need the account field to resolve to Admin or Employee depending on the value of the account field in the database.
How do I conditionally determine the type of a field in a schema on the fly?
Instead of determining the type on the fly, I restructured my code as shown below:
const MessageType = new GraphQLObjectType({
name: 'Message',
fields: () => ({
id:{type:GraphQLString},
author: {
type: AuthorType,
async resolve(parent, args) {
if (parent.author.account === 'guard') {
return await queries.findEmployeeByEmployeeId(parent.author.id).then(guard => {
return {
username: `${guard.first_name} ${guard.last_name}`,
profile_picture: guard.profile_picture
}
})
} else if (parent.author.account === 'admin') {
return {
username: 'Administrator',
profile_picture: 'default.jpg'
}
}
}
},
//other fields
})
})
const AuthorType = new GraphQLObjectType({
name: 'Author',
fields: () => ({
username: {type: GraphQLString},
profile_picture: {type: GraphQLString},
})
})
Since all I need from the AuthorType is the author's username and profile picture, both employee and administrator have these fields, which I pass to AuthorType.
In MessageType, I apply the logic to determine account type in the resolve method of author, then construct custom object out of the logic, to match AuthorType.

how to nest graphql queries

All the examples I find have a query top level object, then a list of queries, which then return types to go deeper.
Since I have a large number of queries, I would like to group them up, this is what I tried:
const AppType = new GraphQLObjectType({
name: 'App',
description: 'Generic App Details',
fields: () => ({
name: { type: GraphQLString },
appId: { type: GraphQLInt },
}),
});
const MyFirstQuery = {
type: new GraphQLList(AppType),
args: {
appId: { type: GraphQLInt },
},
resolve: (root, args) => fetchApp(args.appId),
};
/* snip MySecondQuery, MyThirdQuery, MyFourthQuery */
const MyFirstGroupQuery = new GraphQLObjectType({
name: 'myFirstGroup',
description: 'the first group of queries',
fields: () => ({
myFirstQuery: MyFirstQuery,
mySecondQuery: MySecondQuery,
myThirdQuery: MyThirdQuery,
myFourthQuery: MyFourthQuery,
}),
});
/* snip MySecondGroupQuery, MyThirdGroupQuery and their types */
const QueryType = new GraphQLObjectType({
name: 'query',
description: 'read-only query',
fields: () => ({
myFirstGroup: MyFirstGroupQuery,
mySecondGroup: MySecondGroupQuery,
myThirdGroup: MyThirdGroupQuery,
}),
});
const Schema = new GraphQLSchema({
query: QueryType,
});
Why can't I make MyFirstGroupQuery like I did QueryType to make more nesting levels? The code works fine if I put all queries in QueryType, but my MyFirstGroupQuery produces errors:
Error: query.myFirstGroup field type must be Output Type but got: undefined.
How do I accomplish what I want? I really don't want to just prefix all my queries.
Error query.myFirstGroup field type must be Output Type but got: undefined. means that you haven't provided the type for myFirstGroup
you have to provide the type using type field
myFirstGroup: {
type: MyFirstGroupQuery,
resolve: () => MyFirstGroupQuery,
},
and if the type MyFirstGroupQuery each field must have the type defined such as GraphQLInt, GraphQLString, GraphQLID even if it's tht customtype like MyFirstGroupQuery
In GraphQLSchema constructor function you provide your RootQuery which is QueryType, It's a GraphQLSchema it only accepts the rootQuery with the GraphQLObjectType whose fields must have the type defined
GraphQL is strictly type based, every field you declared must have the type defined
https://github.com/graphql/graphql-js
https://github.com/graphql/graphql-js/blob/master/src/type/schema.js#L32
const {
GraphQLID,
GraphQLInt,
GraphQLString,
GraphQLObjectType,
GraphQLSchema,
GraphQLList,
} = require('graphql');
const AppType = new GraphQLObjectType({
name: 'App',
description: 'Generic App Details',
fields: () => ({
name: { type: GraphQLString },
appId: { type: GraphQLInt },
}),
});
// const MyFirstQuery = {
// type: new GraphQLList(AppType),
// args: {
// appId: { type: GraphQLInt },
// },
// resolve: (root, args) => fetchApp(args.appId),
// };
const myFirstQuery = new GraphQLObjectType({
name: 'First',
fields: () => ({
app: {
type: new GraphQLList(AppType),
args: {
appId: { type: GraphQLInt },
},
resolve: (root, args) => fetchApp(args.appId),
},
}),
});
/* snip MySecondQuery, MyThirdQuery, MyFourthQuery */
const MyFirstGroupQuery = new GraphQLObjectType({
name: 'myFirstGroup',
description: 'the first group of queries',
fields: () => ({
myFirstQuery: {
type: myFirstQuery,
resolve: () => [], // promise
},
// mySecondQuery: {
// type: MySecondQuery,
// resolve: () => //data
// }
// myThirdQuery: {
// type: MyThirdQuery,
// resolve: () => // data
// }
// myFourthQuery: {
// type: MyFourthQuery,
// resolve: () => //data
// }
}),
});
/* snip MySecondGroupQuery, MyThirdGroupQuery and their types */
const QueryType = new GraphQLObjectType({
name: 'query',
description: 'read-only query',
fields: () => ({
myFirstGroup: {
type: MyFirstGroupQuery,
resolve: () => MyFirstGroupQuery,
},
// mySecondGroup: {
// type: MySecondGroupQuery,
// resolve: MySecondGroupQuery
// }
// myThirdGroup: {
// type: MyThirdGroupQuery,
// resolve: MyThirdGroupQuery
// }
}),
});
const Schema = new GraphQLSchema({
query: QueryType,
});
module.exports = Schema;
GraphiQL

Why won't GraphQL (on Node.js) call my "resolve" method?

I'm trying to implement a very basic GraphQL interface in Node.js, but no matter what I do I can't seem to get the resolve method of my foo type to trigger. When I run the following code in a unit test it runs successfully, but I can see from the (lack of) console output that resolve wasn't called, and as a result I get an empty object back when I call graphql(FooSchema, query).
Can anyone more experienced with GraphQL suggest what I might be doing wrong? I'm completely baffled as to how the whole operation can even complete successfully if GraphQL can't find and call the method that is supposed to return the results ...
const fooType = new GraphQLInterfaceType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
const queryType = new GraphQLObjectType({
fields: {
foo: {
args: {
id: {
description: 'ID of the foo',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }) => {
console.log(12345);
return getFoo(id)
},
type: fooType,
}
},
name: 'Query',
});
export default new GraphQLSchema({
query: queryType,
types: [fooType],
});
// In test:
const query = `
foo {
title
}
`;
const result = graphql(FooSchema, query); // == {}
const fooType = new GraphQLInterfaceType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
This is an interface type, however your consumer queryType never implements it. A quick solution should be to change it to this:
const fooType = new GraphQLObjectType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
Here's an example that works for me:
const {
GraphQLNonNull,
GraphQLInt,
GraphQLString,
GraphQLObjectType,
GraphQLSchema,
graphql,
} = require('graphql');
const fooType = new GraphQLObjectType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
},
}),
});
const queryType = new GraphQLObjectType({
fields: {
foo: {
args: {
id: {
description: 'ID of the foo',
type: new GraphQLNonNull(GraphQLString),
},
},
resolve: (root, { id }) => {
return { id, title: 'some-title' };
},
type: fooType,
},
},
name: 'Query',
});
const schema = new GraphQLSchema({
query: queryType,
types: [fooType],
});
graphql(schema, `{ foo (id:"123") { id, title } }`).then(console.log.bind(console));
This should print:
$ node test.js
{ data: { foo: { id: 123, title: 'some-title' } } }
Here's the docs on the InterfaceType: http://graphql.org/learn/schema/#interfaces

GraphQL: How do you pass args to to sub objects

I am using GraphQL to query an object that will be composed from about 15 different REST calls. This is my root query in which I pass in in the ID from the query. This works fine for the main student object that resolves correctly. However, I need to figure out how to pass the ID down to the address resolver. I tried adding args to the address object but I get an error that indicates that the args are not passed down from the Student object. So my question is: How do I pass arguments from the client query to sub objects in a GraphQL server?
let rootQuery = new GraphQLObjectType({
name: 'Query',
description: `The root query`,
fields: () => ({
Student : {
type: Student ,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (obj, args, ast) => {
return Resolver(args.id).Student();
}
}
})
});
export default rootQuery;
This is my primary student object that I link the other objects. In this case I have attached the ADDRESS object.
import {
GraphQLInt,
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
GraphQLList
} from 'graphql';
import Resolver from '../../resolver.js'
import iAddressType from './address.js'
let Student = new GraphQLObjectType({
name: 'STUDENT',
fields: () => ({
SCHOOLCODE: { type: GraphQLString },
LASTNAME: { type: GraphQLString },
ACCOUNTID: { type: GraphQLInt },
ALIENIDNUMBER: { type: GraphQLInt },
MIDDLEINITIAL: { type: GraphQLString },
DATELASTCHANGED: { type: GraphQLString },
ENROLLDATE: { type: GraphQLString },
FIRSTNAME: { type: GraphQLString },
DRIVERSLICENSESTATE: { type: GraphQLString },
ENROLLMENTSOURCE: { type: GraphQLString },
ADDRESSES: {
type: new GraphQLList(Address),
resolve(obj, args, ast){
return Resolver(args.id).Address();
}}
})
});
Here is my address object that is resolved by a second REST call:
let Address = new GraphQLObjectType({
name: 'ADDRESS',
fields: () => ({
ACTIVE: { type: GraphQLString },
ADDRESS1: { type: GraphQLString },
ADDRESS2: { type: GraphQLString },
ADDRESS3: { type: GraphQLString },
CAMPAIGN: { type: GraphQLString },
CITY: { type: GraphQLString },
STATE: { type: GraphQLString },
STATUS: { type: GraphQLString },
TIMECREATED: { type: GraphQLString },
TYPE: { type: GraphQLString },
ZIP: { type: GraphQLString },
})
});
export default Address;
These are my resolver
var Resolver = (id) => {
var options = {
hostname: "myhostname",
port: 4000
};
var GetPromise = (options, id, path) => {
return new Promise((resolve, reject) => {
http.get(options, (response) => {
var completeResponse = '';
response.on('data', (chunk) => {
completeResponse += chunk;
});
response.on('end', () => {
parser.parseString(completeResponse, (err, result) => {
let pathElements = path.split('.');
resolve(result[pathElements[0]][pathElements[1]]);
});
});
}).on('error', (e) => { });
});
};
let Student= () => {
options.path = '/Student/' + id;
return GetPromise(options, id, 'GetStudentResult.StudentINFO');
}
let Address= () => {
options.path = '/Address/' + id + '/All';
return GetPromise(options, id, 'getAddressResult.ADDRESS');
};
return {
Student,
Address
};
}
export default Resolver;
ADDRESSES: {
type: new GraphQLList(Address),
resolve(obj, args, ast){
return Resolver(args.id).Address();
}
}
args passed to ADDRESSES are arguments passed to ADDRESSES field at query time. In the resolve method, obj should be the student object and if you have an id property on it, all you need to do is: return Resolver(obj.id).Address();.

Resources