GraphQL Nested Data in Mutation - graphql

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
}
}
}

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 multiple arguments in field

I'm using GraphQL.
I'm able to pass one argument in a field. But I would like to know how to pass multiple arguments to a field.
This is my code:
GraphlQL Object type: Price availability
const priceAvailability = new GraphQLObjectType({
name: "priceAvailability",
description: "Check price and availability of article",
fields: () => ({
articleID: {
type: GraphQLString
},
priceType:{
type:GraphQLString
},
stockAvailability: {
type: StockAvailabilityType,
resolve(parentValue, args) {
// stuff to get the price and availability
return (data = getStockAvailability.getStockAvailability(
parentValue.isbn, parentValue.omgeving
));
}
}
})
});
The root query
const RootQuery = new GraphQLObjectType({
name: "RootQuery",
fields: () => ({
price: {
type: new GraphQLList(priceAvailability),
args: [{
articleID: {
type: new GraphQLList(GraphQLString),
description:
'List with articles. Example: ["artid1","artid2"]'
},
priceType: {
type: new GraphQLList(GraphQLString) ,
description:
'PriceType. Example: "SalePrice","CurrentPrice"'
}]
},
resolve: function(_, { articleID , priceType}) {
var data = [];
// code to return data here
return data;
}
}
})
});
Schema
module.exports = new GraphQLSchema({
query: RootQuery
});
This is the query I use in GraphiQL to test:
{
query: price(articleID:"ART03903", priceType:"SalePrice" ){
stockAvailability {
QuantityAvailable24hrs
QuantityAvailable48hrs
}
}
}
I can get the articleID via parentValue.articleID, but I have issues with getting parentValue.priceType.
Also GraphiQL tells me that priceType does not exists:
Unknown argument “priceType”. On field “price” of type “RootQuery”
args for a field takes an object instead of an array. Try:
args: {
articleID: {
type: new GraphQLList(GraphQLString),
description: 'List with articles. Example: ["artid1","artid2"]'
},
priceType: {
type: new GraphQLList(GraphQLString) ,
description: 'PriceType. Example: "SalePrice","CurrentPrice"'
},
}

Dynamic GraphQLObjectType

I'm trying to create a dynamic GraphQLObjectType with graphQl, something like this:
export const Project = (data) => {
return new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: {
type: GraphQLString
},
type: {
type: GraphQLString
},
author: {
type: User,
resolve: (root, args, req) => {
...
}
}
})
})
};
I call this model on my query in this way:
getProjectById: {
type: Project(structure),
args: {
id: { type: GraphQLString }
},
resolve(source, args, req) {
const projectService = new ProjectService(req);
return projectService.getProjectById(args.id)
}
}
the problem is that doing this I get this error:
Schema must contain unique named types but contains multiple types
named "Project"
where is the error? do you have some advice? many thanks
The call Project(structure) in turn calls new GraphQLObjectType({name: 'Project',...}) . If you invoke Project(structure) more than once, you try to declare multiple GraphQLObjectTypes with the same name (which makes no sense).
If you would create/declare GraphQLObjectType dynamically, you have to generate a unique name property. E.g. like this:
// assuming data.name is unique
export const Project = (data) => {
return new GraphQLObjectType({
name: `Project${data.name}`,
...
})
}

Dynamic field in graphql object type

I have some GraphQL types:
const Atype = new GraphQLObjectType({
name: 'Atype',
fields: {
data: { type: ADataType },
error: { type: GraphQLString },
}
})
and
const Btype = new GraphQLObjectType({
name: 'Btype',
fields: {
data: { type: BDataType },
error: { type: GraphQLString },
}
})
It looks redundant because only data fields are different...
How can I solve it in more elegant way in GraphQL ?
I created a new Type named Mixed just to solve similar issue., Mixed works as mongoose Mixed type, If you're familiar with it.
Create a new file named GraphQLMixed.js or name it whatever you want and place this code inside it.
import { GraphQLScalarType } from 'graphql';
const GraphQLMixed = new GraphQLScalarType({
name: 'Mixed',
serialize: value => value,
parseValue: value => value,
parseLiteral: ast => ast.value
});
export default GraphQLMixed;
Now, Based on your syntax I assume you're using express-graphql, So wherever you want to use this type, Do this
const GraphQLMixed = require('path/to/file/GraphQLMixed');
const Atype = new GraphQLObjectType({
name: 'Atype',
fields: {
data: { type: GraphQLMixed },
error: { type: GraphQLString },
}
})
const Btype = new GraphQLObjectType({
name: 'Btype',
fields: {
data: { type: GraphQLMixed },
error: { type: GraphQLString },
}
})
Hope this works and helps.

GraphQL mutation without sub section

I want to send graphql mutation request without sub section
mutation _ {
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com")
}
and I am getting
{
"errors": [
{
"message": "Field \"updateCurrentUser\" of type \"User\" must have a sub selection.",
...
}
]
}
add { id } to request works fine but I don't want
Also Schema code
const userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLString) },
fullName: { type: GraphQLString },
email: { type: GraphQLString },
}),
});
type: userType,
args: {
fullName: { type: GraphQLString },
email: { type: new GraphQLNonNull(emailType) },
password: { type: GraphQLString },
},
resolve: async (root, { fullName, email, password }, { rootValue }) => {
const user = await User.findById(rootValue.req.user.id);
...
return user;
},
You define the type of the field to be UserType. Even though it's a mutation, it still follows the same rules and behavior as a query. Because UserType is an object type, it requires nested fields.
mutation _ {
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com") {
fullName
email
}
}
// would respond with { fullName: 'Syava', email: 'fake#gmail.com' }
If you don't want the mutation to return a User, you can declare its type to GraphQLBoolean for example -- that's a scalar and doesn't have any nested fields.
{
type: GraphQLBoolean,
args: {
fullName: { type: GraphQLString },
email: { type: new GraphQLNonNull(emailType) },
password: { type: GraphQLString },
},
resolve: async (root, { fullName, email, password }, { rootValue }) => {
const user = await User.findById(rootValue.req.user.id);
user.fullName = fullName;
user.password = password; // or hashed to not store plain text passwords
return user.save(); // assuming save returns boolean; depends on the library you use
}
}
Note that the best practice for mutations in GraphQL APIs is to return a "result" object with multiple fields, such as the mutated object itself (e.g. user), clientMutationId (per Relay spec), and others as needed. This makes it flexible so you could add more data in the future.
updateCurrentUser(fullName: "Syava", email: "fake#gmail.com") {
clientMutationId
user {
...
}
}

Resources