Custom DQL resolver for field define in GraphQL schema

Hi,

First, thanks for the awesome documentation!

I read the custom resolver doc and currently I’m thinking which approach would be the best to achieve such a query

query {
    interfaces {
        implementations(filter: {
            requirementsSatisfiedBy: [
                {
                    typeRef: {
                        path: "cap.core.type.platform.kubernetes", revision: "1.0.1",},
                    value: "{\"version\": \"1.18.9\"}"
                }
            ]
        }) {
            name
        }
    }
}

I have such a schema:

type InterfaceRevision {
    id: ID!
    metadata: GenericMetadata!
    revision: String! @search(by: [exact, regexp])
    spec: InterfaceSpec!

    """
    List Implementations for a given Interface
    """
    implementations: [Implementation!] # here I want to have a custom resolver with my own filters
}

Without any customization, I can query any data using queryInterfaceRevision query but just for the implementations I want to have a custom resolver that will be able to provide additional business filters.

I do not want to create a totally new query as described here, as it is not needed, only this particular field needs to be resolved differently (only if additional filters are specified).

Is there an option to use @custom(dql: ...)on the filed? If not, how can I execute a custom query that calls back the same Dgraph instance using @custom(http:.. ) and DQL?

EDIT:
I checked version from master and the @custom(dql: ...) directive cannot be used: @custom directive with dql can be used only on queries, so only the @custom(http:.. ) is an option?

Cheers,
Mateusz

Hi @mszostok,
Welcome to our community!

At present, it is not possible to achieve what you are trying to do.

I understand that you also want to use the field arguments in order to construct the custom HTTP call, in addition to using other fields defined within the type. i.e., you want to define a schema like this:

type InterfaceRevision {
    id: ID!
    metadata: GenericMetadata!
    revision: String! @search(by: [exact, regexp])
    spec: InterfaceSpec!

    """
    List Implementations for a given Interface
    """
    implementations(filter: ImplementationFilter): [Implementation!] @custom(http: {
      url: "http://example.com"
      method: POST
     body: "{id:$id, filter:$filter}"
    })
}

input ImplementationFilter {
  requirementsSatisfiedBy: [Requirement]
}

input Requirement {
  typeRef: TypeRef
  value: String
}

input TypeRef {
  path: String
  revision: String
}

Unfortunately, we don’t yet support using field arguments with @custom fields, but it would be useful as implied by your use-case.

Accepting this as an enhancement.

BTW, would you only want to call some external API with that @custom(http: ...) field or having @custom(dql: ...) for fields would also be helpful for your use-case?

2 Likes

Hi,

Thanks for your response.

I tried to use the definition that you mentioned and:

  1. filter is reserved - but this is not a huge problem

  2. it seems that Dgraph does not accept the object as the input types for custom resolvers:

    {"errors":[{"message":"resolving updateGQLSchema failed because input:29: Type InterfaceRevision; Field implementations; @custom directive, body template must use fields defined within the type, found `customFilter`.\n (Locations: [{Line: 3, Column: 4}])","extensions":{"code":"Error"}}]}
    

    So only simple type such as String, Int etc are accepted?

  3. And what about custom queries? Let supposed I have such schema:

    type Tweets {
    	id: ID!
    	text: String! @search(by: [fulltext])
    	users: [User!]
    	timestamp: DateTime! @search
    }
    type User {
    	screen_name: String! @id
    	followers: Int @search
    	customResolver: [Tweets] @custom(http: {
    		url:  "http://host.docker.internal:8888/custom-business-logic",
    		method: POST
    		body: "{id:$id, filter:$filter}"
    	})
    	tweets: [Tweets] @hasInverse(field: users)
    }
    

    and I want to create a custom resolver with my own business logic. The problem that I’m currently facing is that I need to return the whole Tweets array with all nested fields from my custom resolver as I do not know which fields were requested by the user. This is quite problematic as I need to do sth like

    	{
    	 AllNodes(func: type(Tweets)) @filter(..custom filter..) {
    		expand(_all_) {
    		  expand(_all_) {
    			expand(_all_) {
    			  expand(_all_)
    			}
    		  }
    		}
    	  }
    	}
    

    With such approach, there is a lot of over fetched data. I just want to return the uids of Tweets so the rest can be resolved by Dgraph nested resolver.

    How to achieve that?

    Similar problem was described by donovan: Creating custom resolvers for Dgraph GraphQL

Cheers!

I think, I mentioned that the schema that I provided won’t work.

What I meant was, you want a schema like that, but that is not yet supported. @custom fields don’t support passing their arguments to the body. And that is the reason you are getting this error:

It can not use the field arguments in body, it can only use other fields from the type. It has nothing to do with Scalar vs Object.

It is also not possible at present. Adding support for this is in our roadmap, but can’t guarantee an ETA as of now.

Nice to know that you are hitting things for which we don’t yet have solutions. Helps us make the product better :slightly_smiling_face:

Cheers!

1 Like

Ok, understand, so we will try to figure out something.

Btw. I saw that you are using the gql-gen lib. Is there an option to send to the custom handler list of the fields that were requested by the user? As a result, we will be able to create a dedicated query and avoid over fetching for the data returned to the dgraph.

Getting all requested fields is possible when using the qgl-gen directly: https://gqlgen.com/reference/field-collection/

Are you also able to support such thing and pass this information to the custom resolver?

We are also open for contribution if there is such a need! :slight_smile:

If your remote custom server is a GraphQL server, then YES!
You are looking for this: Calling GraphQL custom resolvers

For custom resolvers that are resolved through GraphQL, the query that is sent to the remote server uses the selection set from the runtime GraphQL query received as input. And if you notice, that is the reason you don’t have to specify selection set for the query inside @custom(http: { graphql: "..."}).