I am working on a Relay project and there is a problem with the update mutation. I'm new to Relay, and it's hard for me to establish what the problem is related to - with promises or with Relay settings. When sending a mutation, the findOneAndUpdate function does not save the data received from args. I checked using console.log(args) that the data is coming into updateHero function. I will give the files of my project. I would be grateful for any help! Thanks for attention.
hero.js:
const mongoose = require('mongoose');
mongoose.set('useFindAndModify', false);
const Schema = mongoose.Schema;
const heroSchema = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: true
},
img: {
type: String,
required: true
},
date: {
type: Date,
required: true
}
});
var heroModel = mongoose.model("Hero", heroSchema);
module.exports = {
getHeroes: () => {
return heroModel.find().sort({ _id: -1 })
.then(heroes => {
return heroes.map(hero => {
return {
...hero._doc,
id: hero.id,
date: new Date(hero.date).toISOString()
};
});
})
.catch(err => {
throw err;
});
},
getHero: id => {
return heroModel.findOne({ _id: id });
},
createHero: hero => {
return heroModel(hero).save();
},
removeHero: id => {
return heroModel.findByIdAndRemove(id);
},
updateHero: (args) => {
return new Promise((resolve) => {
(async () => {
await heroModel.findOneAndUpdate(
{ id: args.id },
{
name: args.name,
description: args.description,
img: args.img,
date: args.date
},
{ new: true }
);
})();
resolve("OK");
});
}
};
mutation.js:
const {
GraphQLObjectType,
GraphQLNonNull,
GraphQLString,
GraphQLBoolean
} = require('graphql');
const { fromGlobalId, mutationWithClientMutationId } = require('graphql-relay');
const { Hero } = require('./types/hero');
const heroModel = require('./models/hero');
const CreateHeroMutation = mutationWithClientMutationId({
name: "CreateHero",
inputFields: {
name: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
img: { type: new GraphQLNonNull(GraphQLString) },
date: { type: new GraphQLNonNull(GraphQLString) }
},
outputFields: {
hero: {
type: Hero
}
},
mutateAndGetPayload: args => {
return new Promise((resolve, reject) => {
heroModel.createHero({
name: args.name,
description: args.description,
img: args.img,
date: new Date(args.date)
})
.then(hero => resolve({ hero }))
.catch(reject);
});
}
});
const UpdateHeroMutation = mutationWithClientMutationId({
name: "UpdateHero",
inputFields: {
id: { type: new GraphQLNonNull(GraphQLString) },
name: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
img: { type: new GraphQLNonNull(GraphQLString) },
date: { type: new GraphQLNonNull(GraphQLString) }
},
outputFields: {
hero: {
type: Hero
}
},
mutateAndGetPayload: async args => {
return new Promise((resolve, reject) => {
heroModel.updateHero(args)
.then(hero => resolve({ hero }))
.catch(reject);
});
}
});
const RemoveHeroMutation = mutationWithClientMutationId({
name: "RemoveHero",
inputFields: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
outputFields: {
deleted: { type: GraphQLBoolean },
deletedId: { type: GraphQLString }
},
mutateAndGetPayload: async ({ id }, { viewer }) => {
const { id: productId } = fromGlobalId(id);
const result = await heroModel.removeHero(productId);
return { deletedId: id, deleted: true };
}
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
description: "kjhkjhkjhkjh",
fields: {
createHero: CreateHeroMutation,
removeHero: RemoveHeroMutation,
updateHero: UpdateHeroMutation
}
});
module.exports = Mutation;
Perhaps these files will be needed. nodes.js:
const { nodeDefinitions, fromGlobalId } = require('graphql-relay');
const heroModel = require('./models/hero');
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type == "Hero") {
return heroModel.getHero(id);
} else {
return null;
}
},
(obj) => {
const { Hero } = require('./types/hero');
if (obj instanceof Hero) {
return Hero;
}
return null;
}
);
module.exports = { nodeInterface, nodeField };
hero.js (type):
const {
GraphQLObjectType,
GraphQLString
} = require('graphql');
const { globalIdField, connectionDefinitions } = require('graphql-relay');
const { nodeInterface } = require('../nodes');
const Hero = new GraphQLObjectType({
name: "Hero",
description: "lkjlkjlkjlkjlk",
interfaces: [nodeInterface],
fields: () => ({
id: globalIdField(),
name: {
type: GraphQLString,
description: "The name of the hero"
},
description: {
type: GraphQLString,
description: "Hero biography"
},
img: {
type: GraphQLString,
description: "Portrait of hero"
},
date: {
type: GraphQLString,
description: "Date of birth"
}
})
});
const { connectionType: HeroConnection } = connectionDefinitions({
nodeType: Hero
});
module.exports = { Hero, HeroConnection };
Relay server requires fromGlobalID method to pass id as an argument to mongoose schema. The solution is to pass the "global id" and input arguments:
const UpdateHeroMutation = mutationWithClientMutationId({
name: "UpdateHero",
inputFields: {
id: { type: new GraphQLNonNull(GraphQLString) },
name: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
img: { type: new GraphQLNonNull(GraphQLString) },
date: { type: new GraphQLNonNull(GraphQLString) }
},
outputFields: {
updated: { type: GraphQLBoolean },
updatedId: { type: GraphQLString }
},
mutateAndGetPayload: async (args) => {
const { id: productId } = fromGlobalId(args.id);
const result = await heroModel.updateHero(productId, args);
return { updatedId: args.id, updated: true };
}
});
updateHero function from schema:
updateHero: (id, args) => {
return heroModel.findByIdAndUpdate(
id,
{
name: args.name,
description: args.description,
img: args.img,
date: args.date
},
{ new: true }
);
}
So it goes.