Custom field resolvers are not always called

I’m experiencing some weird behaviour when using @custom directives fields in my Dgraph GraphQL schema.

I encountered this first on my locally running Dgraph instance inside Docker, but was able to replicate the issue on Slash GraphQL as well.

The example I came up to replicate the problem is as follows:

Schema:

type ItemType {
  typeId: String! @id
  name: String

  # The MarketStats would usually be a separate GraphQL server, but the bug can be reproduced with a single server as well.
  marketStats: MarketStatsR @custom(http: {
    url: "<url to my slash graphql instance>",
    method: POST,
    graphql: "query($typeId:String!) { getMarketStats(typeId: $typeId) }",
    skipIntrospection: true,
  })
}

type Blueprint {
  blueprintId: String! @id
  # Two variants to retrieve the blueprint's products, to better demonstrate the bug
  shallowProducts: [ItemType]
  deepProducts: [BlueprintProduct]
}

type BlueprintProduct {
  itemType: ItemType
  amount: Int
}

type MarketStats  {
  typeId: String! @id
  price: Float 
}

type MarketStatsR @remote {
  typeId: String
  price: Float
}

Basically I have Blueprints that produce Items. The Items have prices that are dynamically fetched by a different GraphQL server (which in this example is actually the same server, but the query is still sent via HTTP, so this still works).
The IDs all come from an external source and as such as decorated with @id.

I then added some data to the database:

mutation AddExampleData {
  addItemType(input: {
    typeId: "1"
    name: "Test"
  }) { numUids }
  addMarketStats(input: {
    typeId: "1"
    price: 9.99
  }) { numUids }
  addBlueprint(input: {
    blueprintId: "bp1"
    shallowProducts: [{ typeId: "1" }]
    deepProducts: [{
      amount: 1
      itemType: { typeId: "1" }
    }]
  }) { numUids }
}

The following query works fine:

query WorkingQuery {
  getItemType(typeId:"1") {
    typeId
    marketStats {
      price
    }
  }
  getBlueprint(blueprintId:"bp1") {
    shallowProducts {
      typeId
      marketStats { price }
    }
  }
}

which returns (as expected):

{
  "data": {
    "getItemType": {
      "typeId": "1",
      "marketStats": { "price": 9.99 }
    },
    "getBlueprint": {
      "shallowProducts": [
        {
          "typeId": "1",
          "marketStats": { "price": 9.99 }
        }
      ]
    }
  }
}

However, when trying to access the marketStats through the deepProducts edge:

query BrokenQuery {
  getBlueprint(blueprintId:"bp1") {
    deepProducts {
      itemType {
        typeId
        marketStats { price }
      }
    }
  }
}

What I expected to be returned:

{
  "data": { "getBlueprint": { "deepProducts": [{
    "itemType": {
      "typeId": "1",
      "marketStats": { "price": 9.99 }
    }
  }] } }
}

What is actually returned:

{
  "data": { "getBlueprint": { "deepProducts": [{
    "itemType": {
      "typeId": "1",
      "marketStats": null
    }
  }] } }
}

Am I doing something wrong, or is this indeed a Bug in Dgraph?

Any help would be greatly appreciated.

2 Likes

Oh, and another thing I stumbled upon:

query {
  works: getItemType(typeId:"1") {
    typeId
    marketStats { price }
  }
  doesntWork: getItemType(typeId: "1") {
    marketStats { price }
  }
}

This returns

{
  "data": {
    "works": {
      "typeId": "1",
      "marketStats": {
        "price": 9.99
      }
    },
    "doesntWork": null
  }
}

I get that the custom field resolver requires typeId to be defined since it’s used in the custom query, but I would not expect having to add typeId when I only want the marketStats. After all, I already know the typeId, as I used it in the query.

2 Likes

@pawan - could be related to ID in MarketStatsR?

The issue is that the @custom didn’t had the mode specified, and that is causing this weird behavior. It should default to SINGLE if not mentioned explicitly, but that wasn’t happening somehow.

This works, for the doesntWork query in your second post:

  marketStats: MarketStatsR @custom(http: {
    url: "<url to my slash graphql instance>",
    method: POST,
    mode: SINGLE,
    graphql: "query($typeId:String!) { getMarketStats(typeId: $typeId) }",
    skipIntrospection: true,
  })

There is also another bug, because of which the deepProducts BrokenQuery still doesn’t work in your first post.

Accepting this as a bug to be fixed.

Thanks

Hi @cmd-johnson,
This issue has been fixed with following PR: https://github.com/dgraph-io/dgraph/pull/7371 which will be part of the 21.03 release.