API Playground and Actual API Call have different outcomes

I have the following query:

query ($itemID: [ID!]) {
  items(ids: $itemID) {
    column_values(ids: ["text1", "email1", "text8", "email4"]) {
      id
      text
    }
  }
}

With a variable of the ItemID set in the API Playground, this is happy to return results.

When called from an actual API call from Nodejs using the Monday SDK (version set to “^0.4.12”), including the exact same variable, I am getting an error message:

Arguments are invalid. The argument 'ids' should be used

I’m not even sure what to investigate next…

Hello there @Sometom and welcome to the community!

I hope you like it here :muscle:

Can you please send over the full script you are using?

Looking forward to hearing from you!

Cheers,
Matias

1 Like

This works:

const functions = require('@google-cloud/functions-framework');
const monday = require('monday-sdk-js');

functions.http('helloHttp', (req, res) => {
  const mondayClient = monday();
  mondayClient.setApiVersion("2023-10");
  mondayClient.setToken("?????");

  try {
    const id = req.body.payload.inputFields.itemId;
    const query = `query {
     items(ids: ` + id + ` ) {
        column_values(ids: ["text1", "email1", "text8", "email4"]) {
          id
          text
        }
      }
    }`;
    const response = mondayClient.api(query);
    response.then(r => console.log(r.data));
    res.status(200).send(req.body);
  } catch (err) {
    console.error(err);
  }
});

This doesn’t:

const functions = require('@google-cloud/functions-framework');
const monday = require('monday-sdk-js');

functions.http('helloHttp', (req, res) => {
  const mondayClient = monday();
  mondayClient.setApiVersion("2023-10");
  mondayClient.setToken("?????");

  try {
    const id = req.body.payload.inputFields.itemId;
    const query = `query ($itemId: [ID!]) {
      items(ids: $itemId ) {
        column_values(ids: ["text1", "email1", "text8", "email4"]) {
          id
          text
        }
      }
    }`;
    const response = mondayClient.api(query, { "itemId" : id });
    response.then(r => console.log(r.data));
    res.status(200).send(req.body);
  } catch (err) {
    console.error(err);
  }
});

(obviously left out the tokens :slight_smile: )

Hello again,

I am not sure I understand the issue.

The first script looks good and it is working for you. Why is that not an option?

Why are you trying to pass the id as mondayClient.api(query, { "itemId" : id })?

1 Like

The issue is that both of the examples work in the App Playground. I’d expect both of them to work in scripts as well.

I prefer the second example because the first one is subject to injection attacks that can completely change the meaning of the query. I suspect many other developers would agree the second structure is vastly preferred. We’re dealing with PII, so I want to make sure we’re respecting the sensitivity of the data we have…

I need to implement updates as well, so I’d like to figure out how to do variable substitution to lower the risk of corruption.

Pretty easy -

  1. you should pass the variables object to the api method as the variables key of the options object (options is the second argument .api(query: string, options: { variables: object, ...moreOptions})).

const response = mondayClient.api(query, { variables: { itemId : id} }); would work.

I 100% agree with you that string manipulation is a terrible for graphql unless its concatenating query fragments (that are fixed strings).

That said, you are not validating Id in any way before using it! I like zod plus validator for this - something like the following.

const z = require('zod');
const validator = require('validator');

// define your validation schema outside the function
const inputFieldsSchema = z.object({
  itemId: z.coerce.string().refine(validator.isInt),
});

// i've moved this to the file scope, instead of function scope, this way you don't have to
// allocate the query on every request.
const query = `query ($itemId: [ID!]) {
      items(ids: $itemId ) {
        column_values(ids: ["text1", "email1", "text8", "email4"]) {
          id
          text
        }
      }
    }`;

functions.http('helloHttp', (req, res) => {
  //setup code

  try {
   const { itemId: id } = inputFieldsSchema.parse(req.body.payload.inputFields);
 //rest of your code
  } catch (err) {
   console.error(err)
  }

});
1 Like

Beautiful, thanks @codyfrisch.

Yes, the example was a bit reckless and reduced to fit, but I also appreciate the pointers to other libraries; I’m still learning what javascript libraries are available…

1 Like

Glad that was just for the example/test you were doing!

Zod is pretty decent for what we do with monday. If you were doing something with defined JSON schemas there are other tools which may work better. But for inputFields there isn’t anything generic since the developer defines it, and we also often need to manipulate things at the same time - such as coercing to string.

1 Like