Slash Lambda Fields addGraphQLResolver missing args

Overview

Starting to think more with what all we can do with the power of Lambda!! :partying_face: Wohoo!! Ummm… :frowning_face: maybe.

Concept: With lambda Field resolver we can do just about anything we want inside a javascript resolver when that field is called and return whatever type we define in our schema. one of the available arguments from the props in the resolving function is args. So in theory, we can pass args from a query to a resolving lambda script. Let’s try it… Nope, Doesn’t Work.

I expect that either the args would equal the input from the field or the input from the root query. What I found is that args is an empty object {} no matter how I try to pass args.

I believe I am seeing 2 issues (args not passed, JWT not passed). This topic is only for the first issue of no args. I will open another based on this for JWT not being passed.

Steps to Reproduce:

Schema:

type Contact {
  id: ID
  name: String
  fromAPI: String
  syncAPI(input: String): String @lambda
}

Lambda:

async function syncAPI(props) {
  return JSON.stringify(props)
}

self.addGraphQLResolvers({
  "Contact.syncAPI": syncAPI
})

Add a User

mutation {
  addContact(input: [{
    name: "Foo"
  }]) {
    numUids
    contact {
      id
      name
      fromAPI
    }
  }
}

results:

{
  "data": {
    "addContact": {
      "numUids": 1,
      "contact": [
        {
          "id": "0x4",
          "name": "Foo",
          "fromAPI": null
        }
      ]
    }
  },
  "extensions": {
    "touched_uids": 7,
    "tracing": {
      "version": 1,
      "startTime": "2021-01-14T18:57:36.504098158Z",
      "endTime": "2021-01-14T18:57:36.509134348Z",
      "duration": 5036194,
      "execution": {
        "resolvers": [
          {
            "path": [
              "addContact"
            ],
            "parentType": "Mutation",
            "fieldName": "addContact",
            "returnType": "AddContactPayload",
            "startOffset": 115110,
            "duration": 4894812,
            "dgraph": [
              {
                "label": "mutation",
                "startOffset": 176368,
                "duration": 2436394
              },
              {
                "label": "query",
                "startOffset": 3641973,
                "duration": 1339675
              }
            ]
          }
        ]
      }
    }
  }
}

Query for Contacts

query {
  queryContact(first: 10) {
    id
    name
    fromAPI
    syncAPI(input: "bar")
  }
}

results

{
  "data": {
    "queryContact": [
      {
        "id": "0x4",
        "name": "Foo",
        "fromAPI": null,
        "syncAPI": "{\"isTrusted\":false,\"parents\":[{\"id\":\"0x4\",\"name\":\"Foo\"}],\"args\":{},\"dql\":{},\"parent\":{\"id\":\"0x4\",\"name\":\"Foo\"}}"
      }
    ]
  },
  "extensions": {
    "touched_uids": 3,
    "tracing": {
      "version": 1,
      "startTime": "2021-01-14T19:10:03.827410948Z",
      "endTime": "2021-01-14T19:10:03.845784571Z",
      "duration": 18373658,
      "execution": {
        "resolvers": [
          {
            "path": [
              "queryContact"
            ],
            "parentType": "Query",
            "fieldName": "queryContact",
            "returnType": "[Contact]",
            "startOffset": 89797,
            "duration": 18237001,
            "dgraph": [
              {
                "label": "query",
                "startOffset": 179257,
                "duration": 1563767
              }
            ]
          }
        ]
      }
    }
  }
}

Here is this: @pbassham

1 Like

To update, after moving this to a Lambda Mutation, the args for the parent mutation do come through. But we also need args from a Lambda Field resolver.

This is a feature request now instead of a bug, I just assumed this was already implemented from previous discussions around passing arguments into pre/post “hooks” for which lambda was developed.

@abhimanyusinghgaur. Interesting. Is there a reason we don’t pass args to fields? This feels like something we’ve missed. Do you think this would be a quick fix?

Actually, GraphQL doesn’t yet support accepting args for custom fields, and so they are not passed.
Args for any non-query/mutation field ATM are only auto-generated, and can’t be defined by user.

But, this is a useful scenario, and we will be looking into supporting it sometime soon.

1 Like

I’m glad to hear that there will be some work to support this.

After some initial excitement of some real magic I thought we could achieve, I tried to build something useful this past week, but I have found it pretty limiting to not be able to pass args. Its like writing functions with no parameters.

Instead of @lambda fields being this magically flexible field, it limits it to being pretty much a single function field, and any variable requires a duplicate field to handle that slightly different scenario.

Some simple things I was attempting:

  • prevent inadvertant calls to an expensive API (by requiring args before it ran)
  • limit fields fetched through variables (each data piece in said API has a cost, so be able to limit which fields were fetched dependent on variables)
  • String manipulation with various methods through a lambda.
  • Trim a string to a variable length
  • Change a string case to a variable case
  • Use a database stored template string to and process replace with variables.
  • Use a client provided template string and process replace with database fields.
  • Do complex math operations dependent on variables using the field value.
  • Format date fields per user’s supplied variable using a field from the database.
  • Use fields as database side logic (e.g. Is my supplied variable in the range for a certain field: boolean)

This list could go on for so many different use cases that this is just the starting point. This truly allows an almost endless flexibility to a lambda resolver which helps to reduce the lambda resolver script size because now fewer resolvers can do more things instead of needing a dozen or more resolvers for just a few cases and each resolver then needs to map to its own field, which not only clutters the lambda resolver, but now also the GraphQL schema.

2 Likes

Yes exactly! No developer would want to use a language where he could not pass arguments into functions and had to write a new function for every specific use case.

Just thought I would add, as far as typescript, you need to do this:

async function customFunction({ args, dql, authHeader }: any) {
...
...
(self as any).addGraphQLResolvers({
  "Mutation.customFunction": customFunction
});

Of course it really depends on your rules enabled in tsconfig…

J