I have a mongoose schema like the one below:
import mongoose from 'mongoose'
const ProjectSchema = new mongoose.Schema({
name: {
type: String
},
owner: {
type: String
},
member: {
type: String
},
updatedDate: {
type: Date
},
description: {
type: String
},
folder: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Folder'
},
dataSources: [{
name: {
type: String
},
updatedDate: {
type: Date
},
}],
propjectHistory: [{
no: {
type: Number
},
member: { // is this reference or just a string?
type: String
},
action: {
type: String
},
updatedDate: {
type: Date
},
}]
})
const Project = mongoose.model('Project', ProjectSchema)
And I integrated with graphql using graffiti and graffiti-mongoose.
However, the Graphiql documentation shows that I only have the ones below:
addProject(input: addProjectInput!):
name: String
owner: String
member: String
updatedDate: Date
description: String
folder: ID
clientMutationId: String!
I could successfully add project with a mutation query only using those parameters, but it seems that I cannot even send mutation query with projectHistory and dataSource, which are embedded inside project schema.
However, I can access projectHistory and dataSource when I send find queries.
I can't find any documentation about the problem.
sample mutation query without nested ones works.
mutation {
addProject(input:{
clientMutationId: "1"
name: "testproject",
owner: "keonwoo",
member: "keonwoo",
updatedDate: "2015-07-24T13:23:15.580Z",
description: "this is test project",
folder: "56fb93403eab9e1c14358fb7"
}){
clientMutationId
changedProjectEdge{
node{
_id
name
updatedDate
}
}
}
}
the above mutation returns the following:
{
"data": {
"addProject": {
"clientMutationId": "1",
"changedProjectEdge": {
"node": {
"_id": "56fb93ab3eab9e1c14358fb8",
"name": "testproject",
"updatedDate": "2015-07-24T13:23:15.580Z"
}
}
}
}
}
I am not using client like relay.
the problem was with the graffiti-mongoose library.
Turns out that maintainers of graffiti-mongoose just added embedded object feature and I did not update.
Related
Trying to pass nested variables to the GraphQL query but my server gets only top-level variables (shopId), everything else is null.
I tried:
#1
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice(
$shopId: String!
$address1: String
$zip: String
$city: String
$countryCode: String
) {
calculatePackagePrice(
where: {
shopId: $shopId
destination: {
address1: $address1
zip: $zip
city: $city
countryCode: $countryCode
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
)
And #2:
export function CALCULATE_PACKAGE_PRICE({ shopId, destination }) {
return gql`
query CalculatePackagePrice {
calculatePackagePrice(
where: {
shopId: "${shopId}"
destination: {
address1: "${destination.address1}"
zip: "${destination.zip}
city: "${destination.city}"
countryCode: "${destination.countryCode}"
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
}
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE({
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
})
)
It works just fine when I hardcoded variables content to the queries. What I'm doing wrong?
Here is a helpful snippet from graphql docs,
All declared variables must be either scalars, enums, or input object types. So if you want to pass a complex object into a field, you need to know what input type that matches on the server.
You're correctly passing in the variables as strings, but then trying (perhaps successfully, but I've never seen the syntax before) to create the object in the gql template string. Instead, create an input type for destination and where.
input WhereInput {
shopId: String!
destination: DestinationInput!
}
input DestinationInput {
address1: String!
zip: String!
city: String!
countryCode: String!
}
then change the query on the client (and update the server definition),
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice($where: WhereInput!) {
calculatePackagePrice(where: $where) {
name
price
userErrors {
field
message
}
}
}
`
then pass the variables like,
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
where: {
shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
}
)
In this example
https://graphql.org/graphql-js/constructing-types/
Is it possible for us to achieve Type referring back to its root | parent type? (manager field type referring back to userType)
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
manager: { type: userType },
}
});
Return fields as Thunk solved my issue.
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: ()=> {
return {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
manager: { type: userType }
}
}
});
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
}
}
}
I'm using express graphql with the react starter kit boilerplate and am trying to define my own nested schema when i get the error Error: Availability.rooms field type must be Output Type but got: undefined. I took the examples which all had resolve at the first depth field - I changed it so that my field had a nested field type and moved the resolve to the nested field - but I think I have a syntax error.
schema.js
import {
GraphQLSchema as Schema,
GraphQLObjectType as ObjectType,
} from 'graphql';
import hotel from './queries/hotel';
const schema = new Schema({
query: new ObjectType({
name: 'Query',
fields: {
hotel,
},
}),
});
export default schema;
hotel.js
import {
GraphQLObjectType as ObjectType,
GraphQLList as List,
GraphQLString as StringType,
GraphQLBoolean as BooleanType,
GraphQLInt as IntType,
GraphQLNonNull as NonNull,
} from 'graphql';
import { checkAvailability } from './hotels';
const RoomType = new ObjectType({
name: 'Room',
fields: {
name: { type: new NonNull(StringType) },
roomTypeCategory: { type: new NonNull(StringType) },
nights: { type: new NonNull(IntType) },
isAvailable: { type: new NonNull(BooleanType) },
startDate: { type: new NonNull(StringType) },
endDate: { type: new NonNull(StringType) },
availability: { type: new List(BooleanType) },
},
});
const AvailabilityType = new ObjectType({
name: 'Availability',
args: {
hotelId: { type: new NonNull(StringType) },
checkIn: { type: new NonNull(StringType) },
checkOut: { type: new NonNull(StringType) },
numberOfAdults: { type: new NonNull(IntType) },
},
fields: { rooms: new List(RoomType) },
async resolve({ request }, { hotelId, checkIn, checkOut, numberOfAdults }) {
const rooms = await checkAvailability({
hotelId,
checkIn,
checkOut,
numberOfAdults,
});
return { rooms };
},
});
export default {
type: new ObjectType({
name: 'Hotels',
fields: {
availability: { type: AvailabilityType },
},
}),
};
It should be:
fields: {
rooms: {type: new List(RoomType) }
}
As a field could get more properties other than type (resolve for example), you should pass an object that has a type property. Just like what you did for the rest of them.
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 {
...
}
}