GraphQL association issue - graphql

Before diving into the code, here is a high-level explanation of my question:
In my GraphQL schema, I have two root types: Developers and Projects. I'm attempting to find all developers who are part of a given project. The query might look like this:
{
project(id:2) {
title
developers {
firstName
lastName
}
}
}
Currently, I'm getting a null value for developers.
Dummy data
const developers = [
{
id: '1',
firstName: 'Brent',
lastName: 'Journeyman',
projectIds: ['1', '2']
},
{
id: '2',
firstName: 'Laura',
lastName: 'Peterson',
projectIds: ['2']
}
]
const projects = [
{
id: '1',
title: 'Experimental Drug Bonanza',
company: 'Pfizer',
duration: 20,
},
{
id: '2',
title: 'Terrible Coffee Holiday Sale',
company: 'Starbucks',
duration: 45,
}
]
So, Brent has worked on both projects. Laura has worked on the second project. My issue is in the resolve function in ProjectType. I've tried many queries, but none seem to work.
ProjectType
const ProjectType = new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
company: { type: GraphQLString },
duration: { type: GraphQLInt },
developers: {
type: GraphQLList(DeveloperType),
resolve(parent, args) {
///////////////////////
// HERE IS THE ISSUE //
//////////////////////
return _.find(developers, { id: ? });
}
}
})
})
DeveloperType
const DeveloperType = new GraphQLObjectType({
name: 'Developer',
fields: () => ({
id: { type: GraphQLID },
firstName: { type: GraphQLString },
lastName: { type: GraphQLString }
})
})

So you need to return all the developers having current project's id in their .projectIds, right?
First, _.find cannot help since it returns first matched element and you need to get array with developers(since field has GraphQLList type).
So how about
resolve(parent, args) {
return developers.filter(
({projectIds}) => projectIds.indexOf(parent.id) !== -1
);
}

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 Nested Data in Mutation

I am having some difficulty getting a mutation working in GraphQL where the type in the schema includes a nested type. So say I have a data type for a booking:
const BookingType = new GraphQLObjectType({
name: 'Booking',
fields: () => ({
id: { type: GraphQLInt },
Date: { type: GraphQLString },
Venue: { type: GraphQLString }
})
});
In the schema file I also have a root mutation which looks like this:
createBooking: {
type: BookingType,
args: {
Date: { type: new GraphQLNonNull(GraphQLString) },
Venue: { type: new GraphQLNonNull(GraphQLString) }
},
resolve(parentValue, args){
return axios.post('http://localhost:3000/booking', args)
.then(resp => resp.data);
}
}
I can write a mutation in GraphiQL to create data for the booking no problem:
mutation {
createBooking(
Date: "2018-03-12",
Venue: "Some place",
) {
id
Date
Venue
}
}
So far so good. Now, I need to add a nested type to the original booking object to record staff members assigned to the booking. So I added types for the staff member (both input and output types) and added those to the Booking type and the mutation:
// output type
const AssignedStaffType = new GraphQLObjectType({
name: 'AssignedStaff',
fields: () => ({
id: { type: GraphQLInt },
Name: { type: GraphQLString }
})
});
// input type
const AssignedStaffInputType = new GraphQLInputObjectType({
name: 'AssignedStaffInput',
fields: () => ({
id: { type: GraphQLInt },
Name: { type: GraphQLString }
})
});
The booking type becomes:
const BookingType = new GraphQLObjectType({
name: 'Booking',
fields: () => ({
id: { type: GraphQLInt },
Date: { type: GraphQLString },
Venue: { type: GraphQLString },
Staff: { type: new GraphQLList(AssignedStaffType) }
})
});
And the root mutation becomes:
createBooking: {
type: BookingType,
args: {
Date: { type: new GraphQLNonNull(GraphQLString) },
Venue: { type: new GraphQLNonNull(GraphQLString) },
Staff: { type: new GraphQLList(AssignedStaffInputType) }
},
resolve(parentValue, args){
return axios.post('http://localhost:3000/booking', args)
.then(resp => resp.data);
}
}
What I don't know is how to now formulate the mutation in GraphiQL, specifically what to use as a value for Staff:
mutation {
createBooking(
Date: "2018-03-14",
Venue: "Some place",
Staff: // ??? <--- What goes here??
) {
id
Venue
Date
Staff
}
}
I have tried giving it an object, or an array of objects which have the same structure as AssignedStaffInputType, but I just get an error ('expecting AssignedStaffInputType'). The client (GraphiQL in this instance) doesn't know anything about the AssignedStaffInputType as defined in the schema, so I don't understand a) how to use this input type in the client, or b) how I would then populate such a type with the required data.
Help please!
Never mind, I figured it out. I can, in fact, pass an object (or array of objects) in the correct format (specified in the input type in the schema) and it works fine. The reason I was having problems is that I had the wrong scalar type for one of the fields in the input type and this was throwing the error. The client doesn't need to know about the types specified in the schema it seems. So, the above problematic mutation should, in fact, be written like this:
mutation {
createBooking(
Date: "2018-03-14",
Venue: "Some place",
Staff: [{staffId: 1}]
) {
id
Venue
Date
Staff{
Name
}
}
}

how to reuse resolvers in 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);
}
}
})
});

Filters in GraphQL

const { connectionType: PersonConnection } = connectionDefinitions({
name: 'Person',
nodeType: PersonType,
here i am using connectionFields for count
connectionFields: {
count: {
type: GraphQLInt,
resolve: (args) => {
const filter = args.args || {};
return Person.count(filter).exec();
},
},
},
});
i am quite confused about using args with custom filters and obtain data from database, using filter
if i don't provide any id count should provide all data count, if i provide any id it may also look for references data and search in another models so how to perform the count and efficient filteration of data.
Thanks in Advance
person: {
type: PersonConnection,
args: _.assign({
_id: { type: GraphQLID },
// assign mine custom filters
name: { type: GraphQLString },
location: { type: GraphQLString },
education: { type: GraphQLString },
}, connectionArgs),
resolve: (obj, args, auth, fieldASTs) => {
const filter = args;
return connectionFromPromisedArray(getPersons(filter, fieldASTs), args).then((data) => {
// using to connection Fields
data.args = filter;
return data;
}).catch(err => new Error(err));
},
},

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