javascriptmongooserelayjsrelaygraphql-relay

Problem with update mutation in Relay project


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

Solution

  • 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.