VulcanJS / vulcan-next

The Next starter for GraphQL developers
http://vulcan-docs.vercel.app
MIT License
395 stars 29 forks source link

Is there a way to write API schemas in Vulcan Next? #139

Open daniablank opened 2 years ago

daniablank commented 2 years ago

In particular: I'm trying to make an API function that takes in a string with variable names in it, e.g. "one + two", which should then be substituted in by the values of variables in those names in the variables collection (which should already have entries with name "one" and value 1, etc), and add to a different collection the evaluated result. I've written this and other functions in regular GraphQL, and would like to transfer them to Vulcan instead. Is this doable? For Vulcan Meteor, I looked at the page https://docs.vulcanjs.org/api-schemas.html , but apparently that's not how it works anymore. I asked in Slack, and it was suggested that I make an issue here.

eric-burel commented 2 years ago

Yep thank you for opening a ticket, it will be easier to track. Are you able to share code samples here so I can see how it could be structured?

Also, do you think your API endpoint is tied to a specific model? Like your "Variable" collection? Or is it a totally independant endpoint?

Because we could add typeDefs and resolvers fields to graphql server models to make this more intuitive, and automatically load them into the graphql API.

daniablank commented 2 years ago

Here's the original resolver, edited for brevity:

 calculateResult: async(obj, args, context, info) => {

      const db = mongoose.connection;

      const getVariable = async function(name) {
        return await db.collection("variables").find({/* some filters*/, name: name}).toArray()[0];
      }

      let allRelevantVariables = await db.collection("variables").find({/* some filters */}).toArray();

      const matchVariable = function(str) {
        for (let i = 0; i < allRelevantVariables.length; i++){
          const possibleVar = allRelevantVariables[i]["name"];
          if (str.includes(possibleVar)) {
            return allRelevantVariables[i];
          }
        }
        return false;
      }

      const inputString = args.expression;
      const  percolateVariables = async function(str) {
        let firstFoundVariable = matchVariable(str);
        if (! firstFoundVariable) {
          return str;
        } else {
          const fullFirstVarValue : String = String(await percolateVariables(firstFoundVariable["value"]));
          const truncatedFirstVarValue = replaceAll(fullFirstVarValue, "=", "");          
          const substitutedStr = replaceAll(str, firstFoundVariable["name"], truncatedFirstVarValue);
          return await percolateVariables(substitutedStr);
        }
      }
      let fullString = await percolateVariables(inputString);
        let assignedValue = /* Parse and evaluate fullString */;
        db.collection("models").findOneAndUpdate({/* some filters */}, {$set: {/* misc fields */, score: assignedValue}});
        return assignedValue;
      }
   },
  }
};
daniablank commented 2 years ago

As you can see, it uses the data in one collection, "variables", to update another collection, "models".

eric-burel commented 2 years ago

Hi, I've worked a bit on this this afternoon:

You can check how we seed data, in order to understand "mutators". They are the building blocks of Vulcan default resolvers, but not yet well exposed and they lack a "Read" counterpart.

If you want to simplify this code, a first step would be to use the "Connector" from Vulcan. They add some sugar over Mongoose with generic operations. But you are not obligated to do so, I really think your code is fine. Vulcan is there to make the generic operation easy, but the non-generic stuff should preferably be written using barebones Mongoose/Apollo, exactly like you did.