GraphQL: directive for GraphQL+- custom resolver (e.g Geolocation)

I’ll give this a go!

The Geo features appear to be supported by the JSON API:
https://dgraph.io/docs/mutations/#geolocation-support

Yes, I too just discovered this:
The /query endpoint accepts two content types:

  1. application/json
  2. application/graphql± (sorry, this is what I meant by text/plain earlier)

For application/json, it accepts the following JSON object:

{
  "query": "some graphql+- query",
  "variables": map[string]string
}

But, I am not sure how to send variables in that query so that it could be directly plugged into @custom.

We will think about this.

1 Like

I think the only issue would be the query. Need to check if mutations need to be via HTTP API. I think it would run normally in GraphQL mutations.

Just want to jump in here and say that this support/level of discussion is incredible, guys.

6 Likes

That variable field is called “GraphQL var”. It is ours, it is not related to GraphQL. See https://dgraph.io/docs/query-language/#graphql-variables

1 Like

That’s great! So, graphql± has variables too. Its amazing.

But it still won’t be pluggable directly to @custom for queries with variables, as the query field inside the JSON object is a string (a primitive value). We don’t replace @custom variables inside a primitive value. Instead, we assume that the variable itself is a whole primitive value, and not part of a primitive value.

Yes, looking at the code, it is impossible in the current format whereby it considers the entire string starting with $ as the key.

I’d say you could change it so that it splits the strings by whitespace and then considers each of the resultant strings in the slice by whether or not they begin with the $ symbol, rather than the entire string, then reconstructs the string (but that feels like reinventing fmt.Sprintf).

Yeah!
So, if you had a JSON like:

{
  "k1": $v1,
  "k2": "$v2"
}

Then we can’t really replace $v2 with the variable value, as then there would be no way of distinguishing a simple json string and a variable.
We only replace $v1. Variables are supposed to be present without quotes, otherwise we can’t distinguish a string from a variable. Someone may actually be wanting to just send “$v2” as the value.

So, you will need to write a separate backend app to do the part of forming the graphql± query, and use that with @custom.

1 Like

Ok, I might wait for @michaelcompton to respond about the planned timelines for the inclusion of GraphQL± natively in the @custom directive before I proceed to write any middleware.

And I remember, @vardhanapoorv had tried this thing through Apollo federation (apollo acts as the middleware in this case). He can clarify more on this.

@abhimanyusinghgaur not sure if I got it.

e.g

type Query{
    getGeo(name: String!, v1: String!, v2: String!): GeoObject @custom(http:{
        url: "https://localhost:8080/query"
        method: "POST",
        body: "{ "query": "{ q(func: eq(name, \"$name\")) { uid  name } }, "variables": {"$a": $v1, "$b": $v2, "$name": $name }"}"
    })
}

Do you mean that we can’t pass the GraphQL variable to Dgraph’s GraphQL variable?

Feels like we don’t even would need to use it. Maybe it could be straightforward.
e.g:

type Query{
    getGeo(name: String!): GeoObject @custom(http:{
        url: "https://localhost:8080/query"
        method: "POST",
        body: "{ "query": "{ q(func: eq(name, $name)) { uid  name } } "}"
    })
}

That wouldn’t work?

Also, I don’t see many users using GraphQL Variables in Dgraph context - not sure if it is useful right now. It is used in GraphQL more tho.

2 Likes

Actually, yeah! this is possible. This way we can use it directly with @custom! As, the GraphQL variables will be replaced inside the “variables” in JSON. It’s cool!
We will need to modify the query slightly like:

type Query{
    getGeo(name: String!, v1: String!, v2: String!): GeoObject @custom(http:{
        url: "https://localhost:8080/query"
        method: "POST",
        body: "{ query: \"query q($name: string) { q(func: eq(name, $name)) { uid  name } }\", variables: {$a: $v1, $b: $v2, $name: $name }}"
    })
}

Earlier, I was thinking about only replacing variables directly in “query”, but that wouldn’t have been possible as the “query” value is string which contains the actual query, and we don’t parse variables from inside a string. But with this variable approach, it is possible to directly call dgraph.

Yes, this is what I was saying wouldn’t work as the variable can’t be replaced if it is present inside a string. But, in the other approach, it will work because GraphQL can identify the variables in that case and replace them with their value. Then, dgraph will receive the correct JSON and it will all work!

No middlewares required now!

Thanks @MichelDiz.

2 Likes

Thanks to both of you! I will attempt to setup the Geo stuff tonight and come back to you with my success :slight_smile:

2 Likes

At the moment, support for scalars inside body is not there in master. So, directly specifying the query string inside body won’t work for now. We will be adding support for scalars soon. In the meantime, you will need to send even the query as a variable like:

type Query{
    getGeo(name: String!, query: String!): GeoObject @custom(http:{
        url: "https://localhost:8080/query"
        method: "POST",
        body: "{ query: $query, variables: {$name: $name}}"
    })
}

Where the value of $query will be: “query q($name: string) { q(func: eq(name, $name)) { uid name } }”

Update: @machship-mm We have decided to work on adding Geo types in GraphQL too. Hope to have it out soon.

2 Likes

Amazing!! Thanks guys.

Also, apologies for not having come back to you as yet… I’ve had no time to try it out yet

1 Like

Hey @machship-mm,

Here is the RFC for implementing Geo types in GraphQL: Implementing Geo features in GraphQL

We would love your feedback on it! Would be real nice to see if it satifies your use-case.

1 Like

Hey @machship-mm I have found the solution with @vardhanapoorv. We have to forward the headers to make it work. I have tested the query bellow:

type Test @remote {
  data: Q
}
type Q @remote {
  q: [TestR]
}
type TestR @remote {
  test: String
}

type Query {
		getCountry(query: String!): Test
		  @custom(
			http: {
			  url: "http://localhost:8080/query"
			  forwardHeaders:["Content-Type"]
			  method: "POST"
			  body: "{ query: $query }"
			})
	  }

That approach can be unsafe in some levels, so you should combine with @auth directive.

The GraphQL Query

query {
  getCountry(query: "{ q(func: has(test)) { test } }") {
     data {  q { test } }
  }
}

@michaelcompton @pawan @abhimanyusinghgaur

I know that we gonna support Geo queries, but this approach is good for any kind of special query.

Note: Now that we know that this type of query is possible. It would be interesting to be able to do the body in a more dynamic way. The parser prevents us from using static queries. And let only the values ​​to be dynamic.

1 Like

Thanks @MichelDiz.

Yeah! we don’t support scalars in custom query body ATM. I will pick it up in the next sprint, so we can have static graphql± queries in the body.

1 Like

Do we have a ticket? (Jira?) I wanna add some things on it.

Yup! let me ping you on slack.