Can't Query Graphql site

I am trying to use the /fetch tool to query some data via graphql. However no matter what I have tried, I get a Syntax Error. As far as I can see, the query is correct as it works in CURL and postman .

Here is the query. I have confirmed the URL and the bearer token are correct. I have onbiously changed the URL and the token for security purposes but have not changed the http-data . It is just the query that I am running and I am assuming there is an escape character somewhere but I can’t figure out where this is.

:put ([/tool fetch url="https://somewebsite/api/graphql" \
  http-method=post \
  http-header-field="Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNjdhYjYxOWZiMGFmODZiNDMyNTcyOTI1ODcwZGRkZTI1NTJlMGVlNzdkNjM2ZDQxNGQxYjlmOGI3NDNhNWE1MjM3MGYwZWFkNGVkN2NlNTgiLCJpYXQiOjE3MTMzNzU4MTQuMzcxNiwibmJmIjoxNzEzMzc1ODE0LjM3MTYsImV4cCI6MjE0NTkxNjgwMC4xMDM3LCJzdWIiOiIxNyIsInNjb3BlcyI6W119.XjbbSMlBYyioE4f2OLse6v5gDtfZS8SwL2hkI3tXSKZEokfZ-kbv5z4t-0901zY0ckNyXxjvPWRvnaSPTkfQSDEwoCykXE8R6S9oRb8LzZCxb1EsmGaM2wdpPwKHqqYiBsBD491rzgcWOYPAnOwx5oC7iOAKesrNsBGfRgGe1yOeN6AuzuZdF7ZicbD50y2XC4NTI69jfYKIQoFh0c1m4mcNWDynSeY6qWavi7RKoN2gzmNV8OniMIDWzOykh7_HuibosXuJ_l8MHhDQ9Py_85aWhJnZpFwCYhp2--WqMq8mZcX8QR7u54_fd20QsAQZX_DmLggeSSi_gFVhgjKnI1Hf_JcNMSpJyYpavyux4himsLtegHzcc9xlBTimvYJ0KTrBPi8i9xGe6ShdTwMHyrB68UC050dOqSm-OxxkzgLMcu2DD1PlLwoA707wIfZNT_usYrNM5ehDNqKwrUMDJ_fueqI9BwSgeuyP07QBmebj6Z99wbIkkASraxjRHDRb1YAkdNZDTNtBIsbWBKJHPXpqmQQxKF44bCux-yjGQlmY5R270H-Ru8-YbD2DQv8hj08ArqiUB2-QVx-spMoa9uoV3OhXhHor7Ot558l0J9m3ZP7cTZVlXGUjK2Dwfl5XNg1U2Kvyrgoq57vSTA8wW55kj3dCUD4fq1eIDQgLATI" \
  http-data="{\"query\":\"query accountid {accounts(id:2){entities{name}}}\"}" output=user as-value]->"data")



And here is what the terminal reports.

{"{\"query\":\"query_accountid_{accounts(id:2){entities{name}}}\"}":{"errors":[{"message":"Syntax Error: Unexpected <EOF>"}]}}

I believe you need spaces between attributes and brackets in graphql. Might want to try something like this:

or since query is already in the JSON perhaps

 http-data="{\"query\":\"accountid { accounts(id:2) { entities { name }}}\"}"

(this would depending the backend processing the GraphQL.

otherwise I think your escaping looking right.

Hmm unfortunately the same response. It is so weird that it works with Curl or Postman but not with the fetch tool.

Can you post the command you’re using with curl that works?

You may also need to add JSON as the content-type to /tool/fetch, since my guess is curl is using a --json (which sets the content type):

http-header-field=“**Content-Type:application/json,**Authorization:Bearer eyJ0eXAiOiJKV…7cTZVlXGUjK2Dwfl5XNg1U2” \

Sure. Tried it again in Ubuntu 22.04 with Curl and it works. I changed the website but if you actually need to do a real test, let me know and I can get you a real website to test on

curl -L -X POST 'https://somewebsite/api/graphql' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiZDdmYTYzN2UxY2FjZmQ4ZDkwNjFkZDY2MDhiYzBiZTk2NTY5ZmUyMTZkYjkxZWZiNjVhM2NhNzVkMmUxZGZkZDQyMzc4OTFmNTA3Mzg1YTUiLCJpYXQiOjE3MTIyNDE1MzUuMDE0NSwibmJmIjoxNzEyMjQxNTM1LjAxNDUsImV4cCI6MjE0NTkxNjgwMC44NTQyLCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.CfrAkrsbw-161SCO4QpFuzW8k4ygkPPWUzr709gSvRFACZjzggHw4yO3SB1en9TXepu6GPrBzDUvsKrcSPNmaZGdS2wLe8pvcZygCGRatU6itVQ12CxDIhG7E5H9bmSeKc7okL7pSHW0j4huEXxijABcoCZ6tIIfKUBwFsLak7OSHQphQLc431TphsCFyE8kfDbJ-50u3MRJCYl1fGA-n7ztM5oMpZxtE_t8O0Gd0ON0tUM_8sYPHexM_w9pYTlunN_cEoeWDTmXl5jMZMQZU38CvHXHis_RUIn2cMtNvAA0mSaVs_LaBBWfQJVCW_zZY5Acf2beIl2faniZQ7RNkyCflLhjVipS-qHCX1AteRkplow9Sh0xBmPC1zqy3ECpl56qifxqX79T_T84bx0NliBRn3UfNYROHHnOjpAfLvbDuO31WmKQk_r8cWarHG32_831RSdV2BjIsYQudZ9xRwx6r_guq2Y5kARYsEkJJuv3COJvDOOD8E5r1Zg8NWKJcgv7WBN80gOPUqJqN6bqIymxoXkvEP9AY0-GHZy_s89oOKj0cwn3M2hz7JERHtUgVn9z1bLnelzJbvYmjxaWo4Hzwia91IsqAWkGpz4_lnrmR6hxQiRtLLdCyCX7yj9AOygcWS15xB6cfwzaqn0m-RcQHyH7eSLAaz7_Z-d37j0' -H 'Content-Type: application/json' --data-raw '{"query":"query accountid {accounts(id:2){entities{name}}}"}'

Well, it the -H ‘Content-Type: application/json’ that’s messing in your /tool/fetch - that’s setting it as JSON.

:put ([/tool fetch url="https://somewebsite/api/graphql" \
  http-method=post \
  http-header-field="Content-Type:application/json,Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNjdhYjYxOWZiMGFmODZiNDMyNTcyOTI1ODcwZGRkZTI1NTJlMGVlNzdkNjM2ZDQxNGQxYjlmOGI3NDNhNWE1MjM3MGYwZWFkNGVkN2NlNTgiLCJpYXQiOjE3MTMzNzU4MTQuMzcxNiwibmJmIjoxNzEzMzc1ODE0LjM3MTYsImV4cCI6MjE0NTkxNjgwMC4xMDM3LCJzdWIiOiIxNyIsInNjb3BlcyI6W119.XjbbSMlBYyioE4f2OLse6v5gDtfZS8SwL2hkI3tXSKZEokfZ-kbv5z4t-0901zY0ckNyXxjvPWRvnaSPTkfQSDEwoCykXE8R6S9oRb8LzZCxb1EsmGaM2wdpPwKHqqYiBsBD491rzgcWOYPAnOwx5oC7iOAKesrNsBGfRgGe1yOeN6AuzuZdF7ZicbD50y2XC4NTI69jfYKIQoFh0c1m4mcNWDynSeY6qWavi7RKoN2gzmNV8OniMIDWzOykh7_HuibosXuJ_l8MHhDQ9Py_85aWhJnZpFwCYhp2--WqMq8mZcX8QR7u54_fd20QsAQZX_DmLggeSSi_gFVhgjKnI1Hf_JcNMSpJyYpavyux4himsLtegHzcc9xlBTimvYJ0KTrBPi8i9xGe6ShdTwMHyrB68UC050dOqSm-OxxkzgLMcu2DD1PlLwoA707wIfZNT_usYrNM5ehDNqKwrUMDJ_fueqI9BwSgeuyP07QBmebj6Z99wbIkkASraxjRHDRb1YAkdNZDTNtBIsbWBKJHPXpqmQQxKF44bCux-yjGQlmY5R270H-Ru8-YbD2DQv8hj08ArqiUB2-QVx-spMoa9uoV3OhXhHor7Ot558l0J9m3ZP7cTZVlXGUjK2Dwfl5XNg1U2Kvyrgoq57vSTA8wW55kj3dCUD4fq1eIDQgLATI" \
  http-data="{\"query\":\"query accountid {accounts(id:2){entities{name}}}\"}" output=user as-value]->"data")

Unfortunately this was something I thought as well before I opened up this post. Tried with Content-Type added in the http-header-field and still got the same Unexpected EOF error.

So I did some more digging around. Since I could not see what the instance was seeing in my query , I started to send the body to webhook.site. From looking at it, there are no escape characters or anything like this and it seems like Mikrotik is sending everything correctly.
Then I started to playaround with Githubs graphql API. I formatted it the same as the above script and it worked on Github.
So the issue seems to be with how this website interacts with what is being sent in Mikrotik??

Is there a way to run a curl command on Mikrotik?

Possibly in a slim container, if the hardware allows, but it feels a bit overkill. I mean, it should be possible to get ‘fetch’ to work, but how to locate the root cause of the error is probably the $100,000 question. Have you checked it’s not an SSL certificate issue on either side?

The quoting all looks right, and CURL is doing same “single line” GraphQL.

My other thought is /tool/fetch is using \r\n as the line ending, not just \n…
Perhaps just add \n to the end, since it’s complaining about

In latest V7, there is the newer [:tolf] to convert any CRLF. So perhaps this might work:
http-data=[:tolf input=“{"query":"query accountid {accounts(id:2){entities{name}}}"}”]

I’m pretty sure it’s getting to GraphQL (e.g. error does look like it from HTTP server), so not sure certs or anything – that error looks like it’s from GraphQL server. What GraphQL backend are you using?

Well. Thanks to both of you for the help. I was able to just get it work with the regular tool. Here is what finally worked.
http-data=“{"query":"query accountid{accounts(id:2){entities{name}}}"}” output=user as-value]->“data”)

Now I need to learn more scripting so I can save the object to a variable and select the first entity returned. Hopefully I won’t have to create another thread for it.

Thanks again

To store as a variable, you can just replace the ":put " with a ":global results " would work. There are also :local variables e.g.
{
:local results ([…]->“data”)
:put $results
}

The result is going to be JSON, so to get that into a RouterOS array, you need to use “:deserialize” on those results. I can’t test this but something like this might be a good example:

{
# variables for fetch
:local gqlurl "https://somewebsite/api/graphql"
:local query "{\"query\":\"query accountid{accounts(id:2){entities{name}}}\"}"
:local contenttype "application/json"
:local bearer "eyJ0e...LATI"
:local headers "Content-Type: $contenttype,Authorization: Bearer $bearer"

# use variables to make fetch call and store into new "fetched" variable
:local fetched [/tool fetch url=$gqlurl http-method=post http-header-field=$headers http-data=$query output=user as-value]
:put $fetched

# could check results here, skipping for example...

# store the resulted data which is JSON in a string type
:local json ($fetched->"data")
:put $json

# convert JSON string to RouterOS array
:local results [:deserialize $json from=json]
:put $results

# results should be an array key-value map, so should be able to use RouterOS array accessors against the GraphQL query results 
:put ($results->"accountid")
}

A more complex example of using fetch with variables (and wrapping it in a function to make it easier to use from CLI) is one I wrote for extracting ZeroTier members via ZT’s HTTP API to add static DNS entries for them:
http://forum.mikrotik.com/t/howto-import-zerotier-members-into-mikrotik-dns-using-zt2dns/173937/1

Thanks for your help so far.
So doing more testing and the following information is returned.
Lets say I get the following result from the graphql endpoint
{“data”:{“inventory_model_field_data”:{“entities”:[{“id”:“833”}]}}}
after de sanitizing it, I get this
data=inventory_model_field_data=entities=id=833.
However I want to convert this to array so I can grab the ID, however no matter what I do, I can’t seem to be able to grab the ID.

Yeah that’s how it RouterOS output’s an array, but the array “->” operator can be used. In routeros there an “index” using numbers (e.g. JSON backets ), or if “map” with key-values, then quoted named is used with the “->” routeros array accessor operator…

So if you start with your GQL as a string that converted using [:deserialize]:

# string here, but results from ([/tool/fetch ...]->"data") as "myjson" variable...
 :global myjson "{\"data\":{\"inventory_model_field_data\":{\"entities\":[{\"id\":\"833\"}]}}}"

# in V7.13+, :deserialize get the RouterOS array from the \$myjson
:global myarray [:deserialize from=json $myjson]

# NOW... if you just print it, it does not look like an array 
:put $myarray
#data=inventory_model_field_data=entities=id=833
# i.e. This is how RouterOS compact them...

Then to access it, it should be like this:

:put ($myarray->"data"->"inventory_model_field_data"->"entities"->0) 
# id=833

:put ($myarray->"data"->"inventory_model_field_data"->"entities"->0->"id")
# 833

# or show it's an array inside the GQL, [:len] will give us the count for it, here just 1
:put [:len ($myarray->"data"->"inventory_model_field_data"->"entities")]        
# 1

Yeah that’s how it RouterOS output’s an array, but the array “->” operator can be used. In routeros there an “index” using numbers (e.g. JSON backets ), or if “map” with key-values, then quoted named is used with the “->” routeros array accessor operator…

So if you start with your GQL as a string that converted using [:deserialize]:

# string here, but results from ([/tool/fetch ...]->"data") as "myjson" variable...
 :global myjson "{\"data\":{\"inventory_model_field_data\":{\"entities\":[{\"id\":\"833\"}]}}}"

# in V7.13+, :deserialize get the RouterOS array from the \$myjson
:global myarray [:deserialize from=json $myjson]

# NOW... if you just print it, it does not look like an array 
:put $myarray
#data=inventory_model_field_data=entities=id=833
# i.e. This is how RouterOS compact them...

Then to access it, it should be like this:

:put ($myarray->"data"->"inventory_model_field_data"->"entities"->0) 
# id=833

:put ($myarray->"data"->"inventory_model_field_data"->"entities"->0->"id")
# 833

# or show it's an array inside the GQL, [:len] will give us the count for it, here just 1
:put [:len ($myarray->"data"->"inventory_model_field_data"->"entities")]        
# 1

Note: RouterOS’s fetch command also returns a data element, so there are two “data” things, in different forms. e.g.
:global myjson ([/tool/fetch url=… as-value output=user]->“data”)
and that data has the JSON as a string, so get to the data element returns in the JSON… to highlight the different, this is one-line show that:
:put ([:deserialize from=json ([/tool/fetch url=… as-value output=user]->“data”)]->“data”->“inventory_model_field_data”->“entities”->0->“id”)

Thanks for this. I am terrible at programming but RouterOS seems a bit different than anything else I have looked at(python, JS).

Really appreciate the help.

FWIW, the [:deserialize from=json] is new operation – before :deserialize was added your problem here be a nightmare.

But fair enough, it is different from anything else :wink:. But so is Cisco IOS router’s TCL support, which was atrociously difficult/fragile, and harder than RouterOS script.

Most of the newer routers support Docker-like containers, so if you already have some working GraphQL code elsewhere… you might be able to run it using /container – thus avoid RouterOS scripting.

Thanks for this. Yes I am a noob in the programming but it seems like there are also some bugs specifically with how Fetch tool works with grapqhl.
For example I tried to query something like this
:global url “https://web-ipv6test.sonar.rocks/api/graphql
:global testname “testtest123”
:global contentType “application/json”
:global data"{"query":"query inventory{inventory_model_field_data(general_search:$testname){entities{id}}}"}"
:put ([/tool fetch url=$url http-method=post http-header-field=“Content-Type:application/json, Authorization: $authorization” http-data=$datafile mode=https output=user as-value]->“data”)

and I would get good results. However I I replaced the variable value with something like “01020304”, I started to get errors. This made no sense to get graphql errors. This made no sense to me so I started to do experiment to see where it breaks. It seems like anytime you have a leading 0, this would cause an issue. If I searched for “1020304” then, I would have no problem at all.

So it seems like I must go down the docker container way as it seems to be more reliable and less confusing.

The leading 0 in the value of $testname?

If so, you might want to quoting $testname. (Also, as quoted above, the variable name looks wrong.)
:global datafile “{"query":"query inventory{inventory_model_field_data(general_search:"$testname**"**){entities{id}}}"}”

It might something on the GQL backend that doesn’t like leading 0, or assumes you mean octal/base-8, dunno…

That same GraphQL query with leading 0 works via CLI or some other GraphQL tool?