Pass Client Variables through Directive to Lambda

Continuing the discussion from Dgraph October Community Call:

First of all, THANK YOU for the upcoming @lambda in 20.11 I am really excited for this!!

Not trying to be negative at all, but want to ask for a feature request extension on this knowing it isn’t even released yet. :grimacing:

There is so much power in directives to hold imperative data from a client perspective. GraphQL has given the example of an @upper directive that could be used similar to how @lambda is used to convert a string to upper case within the resolver.

Dgraph took this to the next level by really making a full javascript hook that is linked to a field by adding the lambda directive and the appropriate js function in the lambda script. This is awesome! What it is lacking though is flexibility.

If I want to create a string manipulation field one for lower case and one for upper case, I would need to write two separate predicates and assign both the lambda directive, then write two separate functions to handle each one. This would look something like:

type Item {
  id: ID
  title: String!
  titleLC: String! @lambda
  titleUP: String! @lambda
}
addGraphQLREsolvers({
  "Item.titleLC": ({ parent: { title } }) => title.toLowerCase()
  "Item.titleUC": ({ parent: { title } }) => title.toUpperCase()
})

While this works, it causes stress points:

  • A new predicate for every type of text transformation I want.
  • A new function for every predicate with the lambda directive.

What I propose is:

  1. Allow a client directive to pass variables
  2. Allow the lambda schema directive to map to a function.

Allow a client directive to pass variables

Note: This looks to be started already in the codebase as the 4th param in getBodyForLambda

Extending the schema to allow for a @variables directive that can be applied on any field that also accepts the @lambda directive. This variables directive would accept parameters:

@variables(func: "toUpperCase", field: "title")
@variables(func: "toLowerCase", field: "text")

In the graphql/resolver.go:894 script, forward these parameters as the 4th param when calling getBodyForLambda

Extend the GraphQLEvent type to accept args: any | null

Then I could write [this is untested]:

type Item {
  id: ID
  title: String!
  text: String
  transform: String @lambda
}
addGraphQLResolvers({
  "Item.transform": ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
})

Allow the lambda schema directive to map to a function.

Currently the resolver for the lambda directive is linked directly to the Type.predicate. This makes reusing the same resolver one step more difficult:

type Item {
  id: ID
  title: String!
  text: String
  transform: String @lambda
}
type Second {
  id: ID
  title: String!
  text: String
  transform: String @lambda
}
const transform = ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
addGraphQLResolvers({
  "Item.transform": transform,
  "Second.transform": transform
})

This stress point here is needing to update the lambda script for any predicate later on that wants to use the same already created resolver.

What I propose is allowing the lambda directive to be parameterized accepting a single param which points to the resolver.

The default mapped resolver should be the field.GetObjectName() + "." + field.Name() as is already, with a check before that to look for a param from the lambda directive.

This would allow me to use the lambda directive as the following:

addGraphQLResolvers({
  transform: ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
})
type Item {
  id: ID
  title: String!
  text: String
  transform: String @lambda(transform)
}
type Second {
  id: ID
  title: String!
  text: String
  transform: String @lambda(transform)
}

I might have offered some pull requests but I am not familiar enough with Go nor do I understand how to read the directive from the schema or the request, sorry.

2 Likes

So seems like if we accepted the function name as an argument to lambda that would make it slightly easier to reuse the same functions across multiple fields. This sounds like a good to have though not sure if we can have it in the next release.

1 Like

Hey there, I’m a bit late to the party.
Is this still under consideration? I’d find this a really good fitting addition :slight_smile:

Edit: would be also nice: create own directives instead of @lambda and start them with _ so everyone knows that this is a custom one :slight_smile:

2 Likes